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: forked from: 混相流 / Multiphase flow

混相流 / Multiphase flow

いくつかの種類の液体のシミュレーションです。
クリックで注ぎます。
Click:Pouring
/**
 * Copyright blamfantastico ( http://wonderfl.net/user/blamfantastico )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/qZUlP
 */

// forked from: 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:Shape;
        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 Shape();
            addChild(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.graphics.clear();
            //bitmap.lock();
            //bitmap.graphics.colorTransform(bitmap.rect, COLOR_TRANSFORM);
            for(var i:uint = 0; i < numParticles; i++) {
                const p:Particle = particles[i];
                p.move();
                bitmap.graphics.beginFill(p.color);
                bitmap.graphics.drawCircle(p.x - 2, p.y - 2, 4);
                //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;
  }