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(little optimization + bitmap filter)

/**
 * Copyright tamac124 ( http://wonderfl.net/user/tamac124 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/thqK
 */

// forked from wonderwhyer's Multiphase flow(little optimization + bitmap filter)
package  {
    
    import flash.utils.*;
    import flash.text.*;
    import flash.filters.*;
    import flash.geom.*;
    import flash.events.*;
    import flash.display.*;

    [SWF(frameRate = "60",backgroundColor = "0")]
    public class Fluid extends Sprite {
        public static const GRAVITY:Number = 0.05;
        public static const RANGE:Number = 8;//Radius of influence
        public static const RANGE2:Number = RANGE * RANGE;
        public static const DENSITY:Number = 1.8;//fluid baeline density
        public static const PRESSURE:Number = 1;//Pressure coefficient
        public static const PRESSURE_NEAR:Number = 1;//Close-pressure coefficient
        public static const VISCOSITY:Number = 0.1;//Viscosity
        
        public static var NUM_GRIDS_X:int;//number of grid cells by x
        public static var NUM_GRIDS_Y:int;//number of grid cells by y
        public static var NUM_GRIDS:int;//number of grid cells
        public static var GRID_CELL_SIZE:Number = 9;//aproximate grid cell size, will be rounded to match size of the box
        public static var INV_GRID_SIZE:Number;
        public static var BOX_SIZE_X:Number;
        public static var BOX_SIZE_Y:Number;
        public var bevel:BevelFilter = new BevelFilter(4,45,0xFFFFFF,0.7,0,0.7,6,6,2,1,"inner",false);
        
        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 view:Bitmap;
        private var grids:Vector.<Grid>;
        private const COLOR_TRANSFORM:ColorTransform = new ColorTransform(0.5, 0.5, 0.5);
        public var particleCount:TextField;
        public var lastMouseX:Number = stage.mouseX;
        public var lastMouseY:Number = stage.mouseY;
        
           public var fps:FPS;
        
        public function Fluid() {
            initialize();
        }
        
        private function initialize():void {
            

            stage.doubleClickEnabled = true;
            particles = new Vector.<Particle>();
            numParticles = 0;
            neighbors = new Vector.<Neighbor>();
            numNeighbors = 0;
            
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            
            view = addChild(new Bitmap()) as Bitmap;
            view.scaleX = view.scaleY = 2;
            initGrid();
            
            count = 0;
            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;});
            stage.addEventListener(MouseEvent.DOUBLE_CLICK,function(e:MouseEvent):void {
                particles.length=0;
                numParticles=0;
            });
            stage.addEventListener(Event.RESIZE,function(e:Event):void{
                initGrid();
            });
                                   
            particleCount = new TextField();
            particleCount.textColor=0xffffff;
            particleCount.x = 100;
            particleCount.y = 50;
            addChild(particleCount);
            
            fps = new FPS();
            addChild(fps);
            fps.textColor="0xFF";
            fps.fpsColor="0xFF0000";
            fps.graphWidth=100; 
            fps.graphHeight=50;
            fps.init();
            fps.graphics.beginFill(0x0,0.5);
            fps.graphics.drawRect(0,0,200,110);
        }
        
        private function initGrid():void
        {
            var min:Number = Math.min(stage.stageWidth,stage.stageHeight)
            BOX_SIZE_X = stage.stageWidth/2;
            BOX_SIZE_Y = stage.stageHeight/2;
            
            NUM_GRIDS = Math.round(min/GRID_CELL_SIZE);
            GRID_CELL_SIZE = min / NUM_GRIDS;
            INV_GRID_SIZE = 1/GRID_CELL_SIZE;
            
            NUM_GRIDS_X = Math.floor(BOX_SIZE_X/GRID_CELL_SIZE);
            NUM_GRIDS_Y = Math.floor(BOX_SIZE_Y/GRID_CELL_SIZE);
            NUM_GRIDS = NUM_GRIDS_X*NUM_GRIDS_Y;
            
            BOX_SIZE_X = NUM_GRIDS_X*GRID_CELL_SIZE;
            BOX_SIZE_Y = NUM_GRIDS_Y*GRID_CELL_SIZE;
            
            
            grids = new Vector.<Grid>(NUM_GRIDS, true);
            for(var i:int = 0; i < NUM_GRIDS; i++) {
                    grids[i] = new Grid();
            }

           view.bitmapData = bitmap = new BitmapData(BOX_SIZE_X, BOX_SIZE_Y, true, 0);
           view.x = (stage.stageWidth-view.width)*0.5;
           view.y = (stage.stageHeight-view.height)*0.5;
        }
        
        private function frame(e:Event):void {
            fps.clear();
            fps.startCounting("Whole Frame");
            fps.startCounting("pour");
            if(press)
                pour();
            fps.stopCounting("pour");
            
            lastMouseX = view.mouseX;
            lastMouseY = view.mouseY;

            move();

            particleCount.text = particles.length.toString();
            fps.stopCounting("Whole Frame");
            
        }

        private function pour():void {
             var dx:Number = view.mouseX-lastMouseX;
             var dy:Number = view.mouseY-lastMouseY;                 
             var l:Number = Math.sqrt(dx*dx+dy*dy);
             if(dx==0 && dy==0) l=1;
             var ang:Number = Math.atan2(dx,dy);
            for(var i:int = -4; i <= 4; i++) {
                 particles[numParticles++] = new Particle(view.mouseX+Math.random()*50-25, view.mouseY+Math.random()*50-25,
                     count / 10 % 5);
                 const p:Particle = particles[numParticles - 1];
                 p.vy = 5*dy/l;
                 p.vx = 5*dx/l;
            }
        }

        private function move():void {
            count++;
            bitmap.lock();
            BOX_SIZE_Y
            fps.startCounting("clear screen");
            bitmap.fillRect(bitmap.rect,0);
            fps.stopCounting("clear screen");
            
            fps.startCounting("updateGrid+findNeighbours");
            updateGrids();
            fps.stopCounting("updateGrid+findNeighbours");
            
            fps.startCounting("filters");
            bitmap.applyFilter(bitmap,bitmap.rect,bitmap.rect.topLeft,bevel);
            fps.stopCounting("filters");
            
            bitmap.unlock();
            
            fps.startCounting("calcForce");
            calcForce();
            fps.stopCounting("calcForce");
        }

        private function updateGrids():void {
            var i:uint;
            var j:uint;
            numNeighbors = 0;
            for(i = 0; i < NUM_GRIDS; i++)
                    grids[i].numParticles = 0;
            for(i = 0; i < numParticles; i++) {
                const p:Particle = particles[i];
                
                p.move();
                //bitmap.fillRect(new Rectangle(p.x - 1, p.y - 1, 3, 3), p.color);
                bitmap.fillRect(new Rectangle(p.x-2,p.y-2,5,5),p.color);
                //.setPixel(p.x,p.y,p.color);
                
                p.fx = p.fy = p.density = p.densityNear = 0;
                p.gx = p.x * INV_GRID_SIZE; // which grid cell this particle is in
                p.gy = p.y * INV_GRID_SIZE;
                
                var xMin:uint;// = p.gx != 0?p.gx - 1:p.gx;
                var xMax:uint;// = p.gx != NUM_GRIDS_X - 1?p.gx + 2:p.gx+1;
                var yMin:uint;// = (p.gy != 0?p.gy - 1:p.gy)*Fluid.NUM_GRIDS_X;
                var yMax:uint;// = (p.gy != NUM_GRIDS_Y - 1?p.gy + 2:p.gy+1)*Fluid.NUM_GRIDS_X;
                
                if(p.gx < 1) {
                    xMin = p.gx = 0;
                } else {
                    xMin = p.gx - 1;
                }
                
                if(p.gy < 1) {
                    yMin = p.gy = 0;
                } else {
                    yMin = (p.gy - 1)*NUM_GRIDS_X;
                }
                
                if(p.gx > NUM_GRIDS_X - 2){
                    p.gx = NUM_GRIDS_X - 1;
                    xMax = NUM_GRIDS_X;
                }else{
                    xMax = p.gx+2;
                }
                
                if(p.gy > NUM_GRIDS_Y - 2){
                    p.gy = NUM_GRIDS_Y - 1;
                    yMax = NUM_GRIDS_Y*NUM_GRIDS_X;
                }else{
                    yMax = (p.gy+2)*NUM_GRIDS_X;
                }
                
               // if(p.gy > NUM_GRIDS_Y - 1)
                //    p.gy = NUM_GRIDS_Y - 1;
                    
                p.gi = p.gy * Fluid.NUM_GRIDS_X+p.gx;
                

                var currentGrid:Grid = grids[p.gi];
                
                for(var gx:uint = xMin;gx<xMax;gx++)
                {
                    for(var gy:uint = yMin;gy<yMax;gy+=Fluid.NUM_GRIDS_X)
                    {
                        var g:Grid = grids[gx+gy];
                        for(j = 0; j < g.numParticles; j++) {
                            var pj:Particle = g.particles[j];
                            
                            var nx:Number = p.x-pj.x;
                            var ny:Number = p.y-pj.y;
                            var distance:Number = nx*nx+ny*ny;
                            
                            if(distance < RANGE2) {
                                if(neighbors.length == numNeighbors)
                                    neighbors[numNeighbors] = new Neighbor();
                                var neighbour:Neighbor = neighbors[numNeighbors++];
                                neighbour.setParticle(p, pj,nx,ny,distance);
                            }
                        }
                    }
                }
                currentGrid.add(p);
            }
        }

        private function calcForce():void {
            for(var i:uint = 0; i < numNeighbors; i++)
                neighbors[i].calcForce();
        }
    }

}
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextField;
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.display.Graphics;
import flash.display.Bitmap;
import flash.text.TextFormat;
import flash.utils.getTimer;
import flash.system.System;
import flash.geom.Point;

    
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 static const RANGE:Number = Fluid.RANGE;
    public static const INV_RANGE:Number = 1/RANGE;
    public static const PRESSURE:Number = Fluid.PRESSURE;
    public static const PRESSURE_NEAR:Number = Fluid.PRESSURE_NEAR;
    public static const DENSITY:Number = Fluid.DENSITY;
    public static const VISCOSITY:Number = Fluid.VISCOSITY;
    public function Neighbor() {
    }

    public function setParticle(p1:Particle, p2:Particle,nx:Number,ny:Number,distance2:Number):void { 
        this.distance = distance;
        this.p1 = p1;
        this.p2 = p2;    
        
        this.nx = nx;
        this.ny = ny;
        distance = Math.sqrt(distance2);
        
        weight = 1 - distance *INV_RANGE;
        var density:Number = weight * weight; // pressure force
        p1.density += density;
        p2.density += density;
        density *= weight * PRESSURE_NEAR; 
        p1.densityNear += density;
        p2.densityNear += density;
        const invDistance:Number = 1 / distance;
        this.nx *= invDistance;
        this.ny *= invDistance;
    }

    public function calcForce():void {
        var p:Number;
        if(p1.type != p2.type)
            p = (p1.density + p2.density - DENSITY * 1.2) * 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 Particle {
    public var x:Number;
    public var y:Number;
    public var gx:int;
    public var gy:int;
    public var gi: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:uint;
    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;
        switch(type) {
        case 0:
            color = 0xff6060ff;
            break;
        case 1:
            color = 0xffff6000;
            break;
        case 2:
            color = 0xffff0060;
            break;
        case 3:
            color = 0xff00d060;
            break;
        case 4:
            color = 0xffd0d000;
            break;
        }
    }

    public function move():void {
        vy += GRAVITY;
        if(density > 0) {
            vx += fx / (density * 0.9 + 0.1); // 
            vy += fy / (density * 0.9 + 0.1); //     
        }
        x += vx;
        y += vy;
        if(x < 5)
            vx += (5 - x- vx) * 0.5 ;
        if(x > Fluid.BOX_SIZE_X-5)
            vx += (Fluid.BOX_SIZE_X - 5 - x - vx) * 0.5;
        if(y < 5)
            vy += (5 - y - vy) * 0.5;
        if(y > Fluid.BOX_SIZE_Y-5)
            vy += (Fluid.BOX_SIZE_Y-5 - y - vy) * 0.5;
    }
}

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;
    }
}

class FPS extends Sprite {
        
        public var fps:TextField;
        public var ms:TextField;
        public var mem:TextField;
        public var output:TextField;
        public var startPoints:Object;
        public var collectedTime:Object;
        public var lastTime:Object;
        public var graphColors:Object;
        public var textColor:String="0xFF";
        public var fpsColor:String="0xFF0000";
        public var graphWidth:uint=200;
        public var graphHeight:uint=50;
        public var maxFPS:uint;
        public var col:int;
        public var iFPScolor:uint;
        public var fpsMultiply:Number;
        public var countersMultiply:Number;
        public var tt:int;
        public var pt:int;
        public var bmd:BitmapData;
        public var bmp:Bitmap;
        public var m:Matrix;
        public var tmp:BitmapData;
        public var sp:Sprite;
        public var g:Graphics;
        public var lfp:Number;

        public function FPS() {}

        public function init():void {
            mouseChildren=false;
            fps = new TextField();
            fps.width=55;

            ms = new TextField();
            ms.width=35;
            ms.x=55;

            mem = new TextField();
            mem.width=80;
            mem.x=90;

            output = new TextField();
            output.width=170;

            maxFPS=stage.frameRate;
            col=int(textColor);
            iFPScolor=uint(fpsColor);
            fpsMultiply = graphHeight/(maxFPS*1.2);
            countersMultiply=maxFPS*graphHeight/1050;
            startPoints={};
            collectedTime={};
            graphColors={};
            lastTime={};
            tt=pt=getTimer();
            var tf:TextFormat = new TextFormat(null,12,0xFF);
            
            fps.defaultTextFormat = tf;
            ms.defaultTextFormat = tf;
            mem.defaultTextFormat = tf;
            output.defaultTextFormat = tf;
            output.x=0;
            output.autoSize=flash.text.TextFieldAutoSize.LEFT;
            output.y=20+graphHeight+10;
            
            output.selectable=true;
            ms.selectable=true;
            mem.selectable=true;
            
            pt=tt;
            bmd=new BitmapData(graphWidth,graphHeight,true,0);
            bmp=new Bitmap(bmd);
            addChild(bmp);
            bmp.y=20;
            m = new Matrix();
            m.tx=-1;
            tmp=new BitmapData(bmd.width,bmd.height,true,0);
            sp = new Sprite();
            g=sp.graphics;
            lfp=0;
            addEventListener(Event.ENTER_FRAME,frame);
            addChild(fps);    
            addChild(ms);    
            addChild(mem);        
            addChild(output);
        }
        
        public function setValue(name:String,val:Number):void {
            if (! startPoints.hasOwnProperty(name)) {
                startPoints[name]=0;
                collectedTime[name]=0;
                lastTime[name]=0;
            }
            startPoints[name]   = val;
            collectedTime[name] = val;
        }
        
        public function startCounting(name:String):void {
            if (! startPoints.hasOwnProperty(name)) {
                startPoints[name]=0;
                collectedTime[name]=0;
                lastTime[name]=0;
            }
            startPoints[name]=getTimer();
        }
        public function stopCounting(name:String):void {
            var t:int=getTimer();
            collectedTime[name]+=getTimer()-startPoints[name];
            startPoints[name]=getTimer();
            //trace(collectedTime[name]);
        }

        public function getData(name:String):Number {
            return collectedTime[name];
        }

        public function clear():void  {
            for (var name:String in collectedTime) {
                lastTime[name]=collectedTime[name];
                collectedTime[name]=0;
            }
        }

        public function addToGraph(name:String,color:uint):void {
            if (graphColors==null) {
                init();
            }
            graphColors[name]=color;
        }
        public function removeFromGraph(name:String,color:uint):void {
            if (graphColors.hasOwnProperty(name)) {
                graphColors[name]=null;
            }
        }
        public function frame(evt:Event):void {
            tt=getTimer();
            var dt:int=tt-pt;
            var fp:Number=Math.round((10000/dt))/10;
            fps.text="FPS: "+fp.toString()+" /";
            ms.text=(dt).toString()+" ms";
            mem.text="mem: "+Math.round(100*System.totalMemory/1048576)/100+" Mb";
            output.text="";
            for (var name:String in lastTime) {
                output.appendText(name+" : "+lastTime[name]+"\n");
            }
            tmp.fillRect(tmp.rect,0);
            tmp.draw(bmd,m);
            bmd.copyPixels(tmp,tmp.rect,new Point());
            g.clear();
            g.lineStyle(0,iFPScolor);
            g.moveTo(bmd.width-2,bmd.height-lfp*fpsMultiply);
            g.lineTo(bmd.width-1,bmd.height-fp*fpsMultiply);
            pt=tt;
            lfp=fp;
            for (name in graphColors) {
                if (graphColors[name]!=null) {
                    g.lineStyle(0,graphColors[name]);
                    g.moveTo(bmd.width-2,bmd.height-lastTime[name]*countersMultiply);
                    g.lineTo(bmd.width-1,bmd.height-collectedTime[name]*countersMultiply);
                }
            }
            bmd.draw(sp);
        }
    }