In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: 混相流 / Multiphase flow

混相流 / Multiphase flow

いくつかの種類の液体のシミュレーションです。

クリックで注ぎます。

Click:Pouring
/**
 * Copyright xsli1112 ( http://wonderfl.net/user/xsli1112 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/5vVc
 */

// forked from saharan's 混相流 / Multiphase flow

/*

 * 混相流 / Multiphase flow

 * 

 * いくつかの種類の液体のシミュレーションです。

 * クリックで注ぎます。

 * Click:Pouring

 */

package {

    import flash.utils.*;

    import flash.text.*;

    import flash.filters.*;

    import flash.geom.*;

    import flash.events.*;

    import flash.display.*;

    import net.hires.debug.Stats;

    [SWF(frameRate = "60", backgroundColor = "0xFFFFFF")]

    public class Fluid extends Sprite {        

        public static const GRAVITY:Number = 0.05;//重力

        public static const RANGE:Number = 16;//影響半径

        public static const RANGE2:Number = RANGE * RANGE;//影響半径の二乗

        public static const DENSITY:Number = 3;//流体の基準(安定する)密度

        public static const PRESSURE:Number = 1;//圧力係数

        public static const PRESSURE_NEAR:Number = 1;//近距離圧力係数

        public static const VISCOSITY:Number = 0.3;//粘性係数

        public static const NUM_GRIDS:int = 29;//グリッド数(≒ 465 / RANGE)

        public static const INV_GRID_SIZE:Number = 1 / (465 / NUM_GRIDS);//グリッドサイズの逆数(≒ 1 / RANGE)

        private var particles:Vector.<Particle>;

        private var numParticles:uint;

        private var neighbors:Vector.<Neighbor>;

        private var numNeighbors:uint;

        private var count:int;

        private var press:Boolean;

        private var bitmap:BitmapData;

        private var grids:Vector.<Vector.<Grid>>;

        private const COLOR_TRANSFORM:ColorTransform = new ColorTransform(0,0,0);



        public function Fluid() {

            initialize();

        }



        private function initialize():void {

            particles = new Vector.<Particle>();

            numParticles = 0;

            neighbors = new Vector.<Neighbor>();

            numNeighbors = 0;

            grids = new Vector.<Vector.<Grid>>(NUM_GRIDS, true);

            for(var i:int = 0; i < NUM_GRIDS; i++) {

                grids[i] = new Vector.<Grid>(NUM_GRIDS, true);

                for(var j:int = 0; j < NUM_GRIDS; j++)

                    grids[i][j] = new Grid();

            }

            count = 0;

            bitmap = new BitmapData(465, 465, false, 0);

            addChild(new Bitmap(bitmap));

            // var s:Stats = new Stats();

            // s.alpha = 0.8;

            // addChild(s);

            addEventListener(Event.ENTER_FRAME, frame);

            stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {press = true;});

            stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {press = false;});

        }



        private function frame(e:Event):void {

            if(press)

                pour();

            move();

        }



        private function pour():void {

            for(var i:int = -2; i <= 2; i++) {

                 particles[numParticles++] = new Particle(mouseX + i * 10, mouseY,

                     count / 50 % 5);

                 particles[numParticles - 1].vy = 5;

            }

        }



        private function move():void {

            count++;

            updateGrids();

            findNeighbors();

            calcForce();

            bitmap.lock();

            bitmap.colorTransform(bitmap.rect, COLOR_TRANSFORM);

            for(var i:uint = 0; i < numParticles; i++) {

                const p:Particle = particles[i];

                p.move();

                //bitmap.fillRect(new Rectangle(p.x - 8, p.y - 8, 8, 8), p.color);

                drawCircle(bitmap, p.x - 2, p.y - 2, 4, p.color, p.color);

            }

            bitmap.unlock();

        }



        private function updateGrids():void {

            var i:uint;

            var j:uint;

            for(i = 0; i < NUM_GRIDS; i++)

                for(j = 0; j < NUM_GRIDS; j++)

                    grids[i][j].numParticles = 0;

            for(i = 0; i < numParticles; i++) {

                const p:Particle = particles[i];

                p.fx = p.fy = p.density = p.densityNear = 0;

                p.gx = p.x * INV_GRID_SIZE; // グリッドのどこにいるか計算

                p.gy = p.y * INV_GRID_SIZE;

                if(p.gx < 0) // グリッドから外れてたら一番近いとこに入ってることにする

                    p.gx = 0;

                if(p.gy < 0)

                    p.gy = 0;

                if(p.gx > NUM_GRIDS - 1)

                    p.gx = NUM_GRIDS - 1;

                if(p.gy > NUM_GRIDS - 1)

                    p.gy = NUM_GRIDS - 1;

            }

        }

        private function findNeighbors():void { // 空間分割で近接粒子を計算

            numNeighbors = 0;

            for(var i:uint = 0; i < numParticles; i++) {

                const p:Particle = particles[i];

                const xMin:Boolean = p.gx != 0;

                const xMax:Boolean = p.gx != NUM_GRIDS - 1;

                const yMin:Boolean = p.gy != 0;

                const yMax:Boolean = p.gy != NUM_GRIDS - 1;

                findNeighborsInGrid(p, grids[p.gx][p.gy]);

                if(xMin) findNeighborsInGrid(p, grids[p.gx - 1][p.gy]);

                if(xMax) findNeighborsInGrid(p, grids[p.gx + 1][p.gy]);

                if(yMin) findNeighborsInGrid(p, grids[p.gx][p.gy - 1]);

                if(yMax) findNeighborsInGrid(p, grids[p.gx][p.gy + 1]);

                if(xMin && yMin) findNeighborsInGrid(p, grids[p.gx - 1][p.gy - 1]);

                if(xMin && yMax) findNeighborsInGrid(p, grids[p.gx - 1][p.gy + 1]);

                if(xMax && yMin) findNeighborsInGrid(p, grids[p.gx + 1][p.gy - 1]);

                if(xMax && yMax) findNeighborsInGrid(p, grids[p.gx + 1][p.gy + 1]);

                grids[p.gx][p.gy].add(p);

            }

        }



        private function findNeighborsInGrid(pi:Particle, g:Grid):void {

            for(var j:uint = 0; j < g.numParticles; j++) {

                var pj:Particle = g.particles[j];

                const distance:Number = (pi.x - pj.x) * (pi.x - pj.x) + (pi.y - pj.y) * (pi.y - pj.y);

                if(distance < RANGE2) {

                    if(neighbors.length == numNeighbors)

                        neighbors[numNeighbors] = new Neighbor();

                    neighbors[numNeighbors++].setParticle(pi, pj);

                }

            }

        }



        private function calcForce():void {

            for(var i:uint = 0; i < numNeighbors; i++)

                neighbors[i].calcForce();

        }

                        

        public function drawCircle(obj:BitmapData, xCenter:Number, yCenter:Number, r:Number, color:Number, fill:Number):void

        {

            var r2:Number = r * r;

            var x:Number = 1;

            var y:Number = Math.ceil(Math.sqrt(r2 - 1));

            obj.setPixel(xCenter, yCenter + r, color);

            obj.setPixel(xCenter, yCenter - r, color);

            obj.setPixel(xCenter + r, yCenter, color);

            obj.setPixel(xCenter - r, yCenter, color);

            while (x <= y)

            {

                obj.setPixel(xCenter + x, yCenter + y, color);

                obj.setPixel(xCenter - x, yCenter + y, color);

                obj.setPixel(xCenter - x, yCenter - y, color);

                obj.setPixel(xCenter + x, yCenter - y, color);

                obj.setPixel(xCenter + y, yCenter + x, color);

                obj.setPixel(xCenter - y, yCenter + x, color);

                obj.setPixel(xCenter - y, yCenter - x, color);

                obj.setPixel(xCenter + y, yCenter - x, color);

                x++;

                y = Math.ceil(Math.sqrt(r2 - x * x));

            }

            for (var i:int = 0; i < r; i++) {

                y = Math.ceil(Math.sqrt(r2 - i * i));

                for (var ii:int = 1; ii <= y; ii++) {

                    obj.setPixel(xCenter - y + ii, yCenter - i, fill);

                    obj.setPixel(xCenter + y - ii, yCenter - i, fill);

                    obj.setPixel(xCenter - y + ii, yCenter + i, fill);

                    obj.setPixel(xCenter + y - ii, yCenter + i, fill);

                }

            }

        }

    }

}



class Particle {

    public var x:Number;

    public var y:Number;

    public var gx:int;

    public var gy:int;

    public var vx:Number;

    public var vy:Number;

    public var fx:Number;

    public var fy:Number;

    public var density:Number;

    public var densityNear:Number;

    public var color:int;

    public var type:int;

    public const GRAVITY:Number = Fluid.GRAVITY;

    public function Particle(x:Number, y:Number, type:int) {

        this.x = x;

        this.y = y

        this.type = type;

        vx = vy = fx = fy = 0;

        

        var h:Number = 20;  // random color vars

        var bs:Number = 1; 

        var s:Number = 0; 

        var bl:Number = 0.5; 

        var l:Number = 0; 

        

        switch(type) {

        case 0:

            color = randomColor(225, h, bs, s, bl, 0.1); // Dark blue

            break;

        case 1:

            color = randomColor(21, h, bs, s, bl, 0.1); // Orange

            break;

        case 2:

            color = randomColor(317, h, bs, s, bl, 0.2); // Purple

            break;

        case 3:

            color = randomColor(138, h, bs, s, bl, 0.2); // Green

            break;

        case 4:

            color = randomColor(56, h, bs, s, bl, 0.1); // Yellow

            break;

        }

    }



    public function move():void {

        vy += GRAVITY;

        if(density > 0) {

            vx += fx / (density * 0.9 + 0.1); // 本当は密度で割る計算だけど0に近かった時に

            vy += fy / (density * 0.9 + 0.1); //     粒子が飛び出すから微調整

        }

        x += vx;

        y += vy;

        if(x < 5) // 壁境界

            vx += (5 - x) * 0.5 - vx * 0.5;

        if(x > 460)

            vx += (460 - x) * 0.5 - vx * 0.5;

        if(y < 5)

            vy += (5 - y) * 0.5 - vy * 0.5;

        if(y > 460)

            vy += (460 - y) * 0.5 - vy * 0.5;

    }

}



class Neighbor {

    public var p1:Particle;

    public var p2:Particle;

    public var distance:Number;

    public var nx:Number;

    public var ny:Number;

    public var weight:Number;

    public const RANGE:Number = Fluid.RANGE;

    public const PRESSURE:Number = Fluid.PRESSURE;

    public const PRESSURE_NEAR:Number = Fluid.PRESSURE_NEAR;

    public const DENSITY:Number = Fluid.DENSITY;

    public const VISCOSITY:Number = Fluid.VISCOSITY;

    public function Neighbor() {

    }



    public function setParticle(p1:Particle, p2:Particle):void { // 使いまわし

        this.distance = distance;

        this.p1 = p1;

        this.p2 = p2;

        nx = p1.x - p2.x;

        ny = p1.y - p2.y;

        distance = Math.sqrt(nx * nx + ny * ny);

        weight = 1 - distance / RANGE;

        var density:Number = weight * weight; // 普通の圧力カーネルは距離の二乗

        p1.density += density;

        p2.density += density;

        density *= weight * PRESSURE_NEAR; // 粒子が近づきすぎないよう近距離用のカーネルを計算

        p1.densityNear += density;

        p2.densityNear += density;

        const invDistance:Number = 1 / distance;

        nx *= invDistance;

        ny *= invDistance;

    }



    public function calcForce():void {

        var p:Number;

        if(p1.type != p2.type) // 違うタイプの粒子なら安定する密度をちょっと減らす

            p = (p1.density + p2.density - DENSITY * 1.5) * PRESSURE;

        else // 同じタイプなら普通に計算

            p = (p1.density + p2.density - DENSITY * 2) * PRESSURE;

        const pn:Number = (p1.densityNear + p2.densityNear) * PRESSURE_NEAR; // 基準密度に関係なく近づきすぎたら跳ね返す!

        var pressureWeight:Number = weight * (p + weight * pn); // 結果としてかかる圧力

        var viscosityWeight:Number = weight * VISCOSITY;

        var fx:Number = nx * pressureWeight;

        var fy:Number = ny * pressureWeight;

        fx += (p2.vx - p1.vx) * viscosityWeight; // 単純に粘性項を解く

        fy += (p2.vy - p1.vy) * viscosityWeight;

        p1.fx += fx;

        p1.fy += fy;

        p2.fx -= fx;

        p2.fy -= fy;

    }

}



class Grid {

    public var particles:Vector.<Particle>;

    public var numParticles:uint;

    public function Grid() {

        particles = new Vector.<Particle>;

    }



    public function add(p:Particle):void {

        particles[numParticles++] = p;

    }

}





//////////////////////////////////////////////////////





      /**

      * Interpolate between two colors.

      * @param color1 Starting color.

      * @param color2 Ending color.

      * @param weight Value between 0 and 1.

      */

      function blendColors(color1:uint, color2:uint, w:Number):uint {

        var r:uint = color1 >> 16 & 0xff;

        var g:uint = color1 >> 8 & 0xff;

        var b:uint = color1 & 0xff;

        r = r + ((color2 >> 16 & 0xff) - r) * w;

        g = g + ((color2 >> 8 & 0xff) - g) * w;

        b = b + ((color2 & 0xff) - b) * w;

        return 0xff000000 | (r << 16) | (g << 8) | b;

      }

      

   /**

  * Get a random color.

  * @param col Base Color to randomly vary from.

  * @param hue.

  * @param saturation.

  * @param lightness.

  */

  function randomColor(baseH:int = 180, rangeH:Number = 360, baseS:Number = 1, rangeS:Number = 0, baseL:Number = 0.5, rangeL:Number = 0):uint {

     var hsl:Object = {hue: baseH, saturation: baseS, lightness: baseL}; 

     var hrand:int = (hsl.hue - rangeH/2 + Math.random()*rangeH)%360;

     var srand:Number = Math.max(0,Math.min(1,hsl.saturation - rangeS/2 + Math.random()*rangeS));

     var lrand:Number = Math.max(0,Math.min(1,hsl.lightness - rangeL/2 + Math.random()*rangeL));

     

     var col:uint = HSLtoRGB(1, hrand, srand, lrand);

     return col

  }

  

 /**

  * Convert HSL to RGB.

  * http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL

  * @param a Alpha.

  * @param h Hue

  * @param s Saturation

  * @param l Lightness

  */

  function HSLtoRGB(a:Number=1, h:Number=0, s:Number=0.5, l:Number=1):uint{

     h = h % 360;

     if (h < 0) h += 360;

     h /= 60;

     s = Math.min(1, Math.max(0, s));

     l = Math.min(1, Math.max(0, l));;

     a = Math.min(1, Math.max(0, a));;

     var c:Number = (1 - Math.abs((2 * l) - 1)) * s;

     var x:Number = c * (1 - Math.abs((h % 2) - 1));

     var m:Number = (l - (c / 2)) * 255;

     var cm:Number = (c * 255) + m;

     var xm:Number = (x * 255) + m;

     var rgb:uint = uint(a * 255) << 24;

     if (h < 1)      rgb |= (cm << 16) | (xm << 8) | m;

     else if (h < 2) rgb |= (xm << 16) | (cm << 8) | m;

     else if (h < 3) rgb |= (m  << 16) | (cm << 8) | xm;

     else if (h < 4) rgb |= (m  << 16) | (xm << 8) | cm;

     else if (h < 5) rgb |= (xm << 16) | (m  << 8) | cm;

     else if (h < 6) rgb |= (cm << 16) | (m  << 8) | xm;

     return rgb;

  }