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

liquid simulation (10000 particles)

highly optimized
/**
 * Copyright cid ( http://wonderfl.net/user/cid )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/2lgo
 */

package
{
import flash.display.*;
import flash.events.*;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import net.hires.debug.Stats;


[SWF(width=465, height=465, frameRate=100, backgroundColor=0xffffff)]

public class LiquidTest_1c extends Sprite
{
    private var _numParticles    :uint = 10000;
    private var gravityX        :Number = 0;
    private var gravityY        :Number = 0;
    
    private var gsizeX            :int = 100;        //grid width
    private var gsizeY            :int = 100;        //grid height
    
    private var _mapWidth        :uint = 300;    //bitmapData width
    private var _mapHeight        :uint = 300;    //bitmapData height
    
    private var _mapScaleX        :Number = _mapWidth / gsizeX;
    private var _mapScaleY        :Number = _mapHeight / gsizeY;
    
    private var mass            :Number = 1;
    
    private var particles        :Vector.<Particle>;
    private var grid            :Vector.<Vector.<Node>>;
    private var nodes            :Vector.<Node>;

    private var mx                :int;
    private var my                :int;
    private var mxprev            :int;
    private var myprev            :int;

    private var _canvas            :BitmapData;
    private var _mapSize        :uint = _mapWidth * _mapHeight;
    private var _mapBuffer        :Vector.<uint>;
    private var _bitmap            :Bitmap;

    private var _blur            :BlurFilter = new BlurFilter(5, 5, 5);
    private var _tintColor        :ColorTransform = new ColorTransform(1, 1, 1, 1, 230, -230, -230, 0);
    private var _pixelColor        :uint = 0x000000;
    
    private var _mouseScaleX    :Number;
    private var _mouseScaleY    :Number;
    
    private var drag            :Boolean = false;
    private var mdx                :Number = 0, mdy:Number = 0;

    private static const ZERO_POINT    :Point = new Point();


    public function LiquidTest_1c()
    {
        Wonderfl.capture_delay(5);
        addEventListener(Event.ADDED_TO_STAGE, init);
    }

    public function init(event:Event):void
    {
        stage.scaleMode = StageScaleMode.NO_SCALE;
        stage.align = StageAlign.TOP_LEFT;
        
        _canvas = new BitmapData(_mapWidth, _mapHeight, false, 0);
        _bitmap = new Bitmap(_canvas);
        _bitmap.scaleX = stage.stageWidth / _canvas.width;
        _bitmap.scaleY = stage.stageHeight / _canvas.height;
        _bitmap.smoothing = true;
        addChild(_bitmap);

        _mouseScaleX = gsizeX / stage.stageWidth;
        _mouseScaleY = gsizeY / stage.stageHeight;

        nodes = new Vector.<Node>(gsizeX * gsizeY, true);

        var i:int, j:int;
        var n:int = 0;
        grid = new Vector.<Vector.<Node>>(gsizeX, true);
        
        for (i = 0; i < gsizeX; i++){
            grid[i] = new Vector.<Node>(gsizeY, true);
            for (j = 0; j < gsizeY; j++){
                grid[i][j] = nodes[n++] = new Node();
            }
        }

        for (i = 0; i < gsizeX-2; i++){
            for (j = 0; j < gsizeY-2; j++){
                grid[i][j].n00 = grid[i+0][j+0];
                grid[i][j].n10 = grid[i+1][j+0];
                grid[i][j].n20 = grid[i+2][j+0];
                grid[i][j].n01 = grid[i+0][j+1];
                grid[i][j].n11 = grid[i+1][j+1];
                grid[i][j].n21 = grid[i+2][j+1];
                grid[i][j].n02 = grid[i+0][j+2];
                grid[i][j].n12 = grid[i+1][j+2];
                grid[i][j].n22 = grid[i+2][j+2];
            }
        }

        particles = new Vector.<Particle>(_numParticles, true);
        
        for (i = 0; i < _numParticles; i++){
            particles[i] = new Particle(mass, Math.random() * (gsizeX - 8) + 4, Math.random() * (gsizeY - 8) + 4);
        }

        addChild(new Stats());
        _mapBuffer = new Vector.<uint>(_mapSize, true);
        
        stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
        stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
    }


    public function onEnterFrame(e:Event):void
    {
        simulate();

        _canvas.lock();
        _canvas.fillRect(_canvas.rect, 0xffffff);

        for each (var p:Particle in particles){
            _canvas.setPixel(int(_mapWidth / gsizeX * p.x), int(_mapHeight / gsizeY * p.y), _pixelColor);
        }
        _canvas.applyFilter(_canvas, _canvas.rect, ZERO_POINT, _blur);
        _canvas.draw(_canvas, null, _tintColor, BlendMode.ADD);
        _canvas.unlock();
    }


    public function onDown(e:MouseEvent):void
    {
        stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
        stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
        mx = mxprev = e.localX * _mouseScaleX;
        my = myprev = e.localY * _mouseScaleY;
        drag = true;
    }

    public function onMove(e:MouseEvent):void
    {
        mx = e.localX * _mouseScaleX;
        my = e.localY * _mouseScaleY;
    }
    
    public function onUp(e:MouseEvent):void
    {
        stage.removeEventListener(MouseEvent.MOUSE_UP, onUp);
        stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMove);
        drag = false;
    }


    public function simulate():void
    {
        if (drag){
            mdx = mx - mxprev;
            mdy = my - myprev;
            mxprev = mx;
            myprev = my;
        }
        
        var n:Node, p:Particle;

        for each (n in nodes) if (n.active) n.clear();

        var i:int, j:int;
        var x:Number, y:Number, phi:Number;
        var fx:Number = 0.0, fy:Number = 0.0;
    
        for each (p in particles){
            p.cx = int(p.x - 0.5);
            p.cy = int(p.y - 0.5);
            
            x = p.cx - p.x;
            p.px0 = 0.5 * x * x + 1.5 * x + 1.125;
            p.gx0 = x + 1.5;
            x += 1.0;
            p.px1 = -x * x + 0.75;
            p.gx1 = -2.0 * x;
            x += 1.0;
            p.px2 = 0.5 * x * x - 1.5 * x + 1.125;
            p.gx2 = x - 1.5;
            
            y = p.cy - p.y;
            p.py0 = 0.5 * y * y + 1.5 * y + 1.125;
            p.gy0 = y + 1.5;
            y += 1.0;
            p.py1 = -y * y + 0.75;
            p.gy1 = -2.0 * y;
            y += 1.0;
            p.py2 = 0.5 * y * y - 1.5 * y + 1.125;
            p.gy2 = y - 1.5;

            n = p.n = grid[p.cx][p.cy];
            n.n00.active = n.n10.active = n.n20.active = n.n01.active = n.n11.active = n.n21.active = n.n20.active = n.n21.active = n.n22.active = true;

            p.p00 = p.px0 * p.py0;
            p.p10 = p.px1 * p.py0;
            p.p20 = p.px2 * p.py0;
            p.p01 = p.px0 * p.py1;
            p.p11 = p.px1 * p.py1;
            p.p21 = p.px2 * p.py1;
            p.p02 = p.px0 * p.py2;
            p.p12 = p.px1 * p.py2;
            p.p22 = p.px2 * p.py2;

            
            n.n00.m += p.p00 * p.mass;
            n.n00.d += p.p00;
            n.n00.gx += p.gx0 * p.py0;
            n.n00.gy += p.px0 * p.gy0;
            
            n.n10.m += p.p10 * p.mass;
            n.n10.d += p.p10;
            n.n10.gx += p.gx1 * p.py0;
            n.n10.gy += p.px1 * p.gy0;
            
            n.n20.m += p.p20 * p.mass;
            n.n20.d += p.p20;
            n.n20.gx += p.gx2 * p.py0;
            n.n20.gy += p.px2 * p.gy0;
            
            n.n01.m += p.p01 * p.mass;
            n.n01.d += p.p01;
            n.n01.gx += p.gx0 * p.py1;
            n.n01.gy += p.px0 * p.gy1;
            
            n.n11.m += p.p11 * p.mass;
            n.n11.d += p.p11;
            n.n11.gx += p.gx1 * p.py1;
            n.n11.gy += p.px1 * p.gy1;
            
            n.n21.m += p.p21 * p.mass;
            n.n21.d += p.p21;
            n.n21.gx += p.gx2 * p.py1;
            n.n21.gy += p.px2 * p.gy1;
            
            n.n02.m += p.p02 * p.mass;
            n.n02.d += p.p02;
            n.n02.gx += p.gx0 * p.py2;
            n.n02.gy += p.px0 * p.gy2;
            
            n.n12.m += p.p12 * p.mass;
            n.n12.d += p.p12;
            n.n12.gx += p.gx1 * p.py2;
            n.n12.gy += p.px1 * p.gy2;
            
            n.n22.m += p.p22 * p.mass;
            n.n22.d += p.p22;
            n.n22.gx += p.gx2 * p.py2;
            n.n22.gy += p.px2 * p.gy2;
       }

        var density:Number, pressure:Number, weight:Number;
        var n01:Node, n02:Node;
        var n11:Node, n12:Node;
        var cx:int, cy:int;
        var cxi:int, cyi:int;

        var pdx:Number, pdy:Number;
        var C20:Number, C02:Number, C30:Number, C03:Number;
        var csum1:Number, csum2:Number;
        var C21:Number, C31:Number, C12:Number, C13:Number, C11:Number;

        var u:Number, u2:Number, u3:Number;
        var v:Number, v2:Number, v3:Number;
        var vx:Number, vy:Number;

        for each (p in particles){
            cx = int(p.x);
            cy = int(p.y);
            cxi = cx + 1;
            cyi = cy + 1;

            n01 = grid[cx][cy];
            n02 = grid[cx][cyi];
            n11 = grid[cxi][cy];
            n12 = grid[cxi][cyi];

            pdx = n11.d - n01.d;
            pdy = n02.d - n01.d;
            C20 = 3.0 * pdx - n11.gx - 2.0 * n01.gx;
            C02 = 3.0 * pdy - n02.gy - 2.0 * n01.gy;
            C30 = -2.0 * pdx + n11.gx + n01.gx;
            C03 = -2.0 * pdy + n02.gy + n01.gy;
            csum1 = n01.d + n01.gy + C02 + C03;
            csum2 = n01.d + n01.gx + C20 + C30;
            C21 = 3.0 * n12.d - 2.0 * n02.gx - n12.gx - 3.0 * csum1 - C20;
            C31 = -2.0 * n12.d + n02.gx + n12.gx + 2.0 * csum1 - C30;
            C12 = 3.0 * n12.d - 2.0 * n11.gy - n12.gy - 3.0 * csum2 - C02;
            C13 = -2.0 * n12.d + n11.gy + n12.gy + 2.0 * csum2 - C03;
            C11 = n02.gx - C13 - C12 - n01.gx;

            u = p.x - cx;
            u2 = u * u;
            u3 = u * u2;
            v = p.y - cy;
            v2 = v * v;
            v3 = v * v2;
            density = n01.d + n01.gx * u + n01.gy * v + C20 * u2 + C02 * v2 + C30 * u3 + C03 * v3 + C21 * u2 * v + C31 * u3 * v + C12 * u * v2 + C13 * u * v3 + C11 * u * v;

            pressure = density - 1.0;
            if (pressure > 2.0) pressure = 2.0;

            fx = 0;
            fy = 0;

            if (p.x < 4) fx += p.mass * (4 - p.x);
            else if (p.x > gsizeX - 4) fx += p.mass * (gsizeX - 4 - p.x);

            if (p.y < 4) fy += p.mass * (4 - p.y);
            else if (p.y > gsizeY - 4) fy += p.mass * (gsizeY - 4 - p.y);

            if (drag){
                vx = p.x - mx; if (vx < 0) vx = -vx;
                vy = p.y - my; if (vy < 0) vy = -vy;
                
                if (vx < 8 && vy < 8){
                    weight = p.mass * (1 - vx * 0.1) * (1 - vy * 0.1);
                    fx += weight * (mdx - p.u);
                    fy += weight * (mdy - p.v);
                }
            }

            n = p.n;
            
            n.n00.ax += -(p.gx0 * p.py0) * pressure + fx * p.p00;
            n.n00.ay += -(p.px0 * p.gy0) * pressure + fy * p.p00;
            
            n.n10.ax += -(p.gx1 * p.py0) * pressure + fx * p.p10;
            n.n10.ay += -(p.px1 * p.gy0) * pressure + fy * p.p10;
            
            n.n20.ax += -(p.gx2 * p.py0) * pressure + fx * p.p20;
            n.n20.ay += -(p.px2 * p.gy0) * pressure + fy * p.p20;
            
            n.n01.ax += -(p.gx0 * p.py1) * pressure + fx * p.p01;
            n.n01.ay += -(p.px0 * p.gy1) * pressure + fy * p.p01;
            
            n.n11.ax += -(p.gx1 * p.py1) * pressure + fx * p.p11;
            n.n11.ay += -(p.px1 * p.gy1) * pressure + fy * p.p11;
            
            n.n21.ax += -(p.gx2 * p.py1) * pressure + fx * p.p21;
            n.n21.ay += -(p.px2 * p.gy1) * pressure + fy * p.p21;
            
            n.n02.ax += -(p.gx0 * p.py2) * pressure + fx * p.p02;
            n.n02.ay += -(p.px0 * p.gy2) * pressure + fy * p.p02;
            
            n.n12.ax += -(p.gx1 * p.py2) * pressure + fx * p.p12;
            n.n12.ay += -(p.px1 * p.gy2) * pressure + fy * p.p12;
            
            n.n22.ax += -(p.gx2 * p.py2) * pressure + fx * p.p22;
            n.n22.ay += -(p.px2 * p.gy2) * pressure + fy * p.p22;
        }

        for each (n in nodes){
            if (n.active && n.m > 0){
                n.ax /= n.m;
                n.ay /= n.m;
                n.ax += gravityX;
                n.ay += gravityY;
            }
        }

        var mu:Number, mv:Number;
        
        for each (p in particles){
            n = p.n;

            p.u += (p.p00 * n.n00.ax + p.p10 * n.n10.ax + p.p20 * n.n20.ax + p.p01 * n.n01.ax + p.p11 * n.n11.ax + p.p21 * n.n21.ax + p.p02 * n.n02.ax + p.p12 * n.n12.ax + p.p22 * n.n22.ax);
            p.v += (p.p00 * n.n00.ay + p.p10 * n.n10.ay + p.p20 * n.n20.ay + p.p01 * n.n01.ay + p.p11 * n.n11.ay + p.p21 * n.n21.ay + p.p02 * n.n02.ay + p.p12 * n.n12.ay + p.p22 * n.n22.ay);
            
            mu = p.mass * p.u;
            mv = p.mass * p.v;

            n.n00.u += p.p00 * mu;    n.n00.v += p.p00 * mv;
            n.n10.u += p.p10 * mu;    n.n10.v += p.p10 * mv;
            n.n20.u += p.p20 * mu;    n.n20.v += p.p20 * mv;
            n.n01.u += p.p01 * mu;    n.n01.v += p.p01 * mv;
            n.n11.u += p.p11 * mu;    n.n11.v += p.p11 * mv;
            n.n21.u += p.p21 * mu;    n.n21.v += p.p21 * mv;
            n.n02.u += p.p02 * mu;    n.n02.v += p.p02 * mv;
            n.n12.u += p.p12 * mu;    n.n12.v += p.p12 * mv;
            n.n22.u += p.p22 * mu;    n.n22.v += p.p22 * mv;
        }

        for each (n in nodes){
            if (n.active && n.m > 0){
                n.u /= n.m;
                n.v /= n.m;
            }
        }

        var gu:Number, gv:Number;
        
        for each (p in particles){
            n = p.n;

            gu = p.p00 * n.n00.u + p.p10 * n.n10.u + p.p20 * n.n20.u + p.p01 * n.n01.u + p.p11 * n.n11.u + p.p21 * n.n21.u + p.p02 * n.n02.u + p.p12 * n.n12.u + p.p22 * n.n22.u;
            gv = p.p00 * n.n00.v + p.p10 * n.n10.v + p.p20 * n.n20.v + p.p01 * n.n01.v + p.p11 * n.n11.v + p.p21 * n.n21.v + p.p02 * n.n02.v + p.p12 * n.n12.v + p.p22 * n.n22.v;

            p.x += gu;
            p.y += gv;
            p.u += (gu - p.u);
            p.v += (gv - p.v);
            
            if (p.x < 1){
                p.x = (1 + Math.random() * 0.01);
                p.u = 0;
            }else if (p.x > gsizeX - 2){
                p.x = (gsizeX - 2 - Math.random() * 0.01);
                p.u = 0;
            }
            if (p.y < 1){
                p.y = (1 + Math.random() * 0.01);
                p.v = 0;
            }else if (p.y > gsizeY - 2){
                p.y = (gsizeY - 2 - Math.random() * 0.01);
                p.v = 0;
            }
        }
    }
}
}

class Node
{
    public var m        :Number = 0;
    public var d        :Number = 0;
    public var gx        :Number = 0;
    public var gy        :Number = 0;
    public var u        :Number = 0;
    public var v        :Number = 0;
    public var ax        :Number = 0;
    public var ay        :Number = 0;
    public var active    :Boolean;

    public var n00:Node, n10:Node, n20:Node, n01:Node, n11:Node, n21:Node, n02:Node, n12:Node, n22:Node;

    public function clear():void
    {
        m = d = gx = gy = u = v = ax = ay = 0;
        active = false;
    }
}

class Particle
{
    public var n        :Node;
    public var mass        :Number;
    public var x        :Number;
    public var y        :Number;
    public var u        :Number = 0;
    public var v        :Number = 0;
    public var cx        :int;
    public var cy        :int;

    public var px0:Number, px1:Number, px2:Number;
    public var py0:Number, py1:Number, py2:Number;
    public var gx0:Number, gx1:Number, gx2:Number;
    public var gy0:Number, gy1:Number, gy2:Number;
    
    public var p00:Number, p10:Number, p20:Number, p01:Number, p11:Number, p21:Number, p02:Number, p12:Number, p22:Number;

    public function Particle(mass:Number, x:Number, y:Number)
    {
        this.mass = mass;
        this.x = x;
        this.y = y;
    }
}