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

water physics

click to change gravity

I know this is far from ready, but I just can't get it any better......
anyone?
Get Adobe Flash player
by owenray 01 Jun 2011

    Talk

    bradsedito at 04 Jun 2011 05:26
    This is an awesome blank slate in regards to the excitingly awesome world of realtime physics and fluid dynamics in Flash. Nice job. I'll take a 'stab' at it later!

    Tags

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

/**
* anyone know how to improve this..
* I just don't know what to do anymore..
*/
package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.GradientType;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.TimerEvent;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.BlurFilter;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.utils.ByteArray;
    import flash.utils.Timer;
    
    import flashx.textLayout.operations.CopyOperation;
    
    import mx.core.UIComponent;
    
    import org.osmf.events.TimeEvent;
    
    public class WaterPhysx extends Sprite
    {
        private var fields:Array = new Array();
        
        private var t:Timer = new Timer(20, 0);
        
        private var size:int = 40;
        private var numParticles:int = 60;
        private var collisionOn:Number = .2;
        private var balls:Array = new Array();
        private var damping:Number = 1;
        
        private var tFields:Array = new Array();
        //private var Width:int = 1024, Height:int = 1280;
        //private var Width:int = 500, Height:int = 500;
        private var Width:int = 450, Height:int = 450;
        
        private var drawSmaller:int = 1;
        private var bmd:BitmapData = new BitmapData(Width/drawSmaller, Height/drawSmaller, false);
        private var theWaterColor:BitmapData = new BitmapData(Width/drawSmaller, Height/drawSmaller, false);
        private var bm:Bitmap = new Bitmap(bmd);
        private var draw:Sprite = new Sprite();
        
        private var gravity:Number = .5;
        
        private var tf:TextField = new TextField();
        
        //private var lastTime:Number = 0;
        private var arrPos:int = 0;
        private var timesArray:Array = new Array();
        
        private var ball:BitmapData = new BitmapData(size*2/drawSmaller, size*2/drawSmaller, true, 0);
        
        public function WaterPhysx()
        {
            addChild(bm);
            addChild(tf);
            tf.textColor = 0xFFFFFF;
            theWaterColor.fillRect(new Rectangle(0, 0, Width, Height), 0xFF0000);
            bm.scaleX = bm.scaleY = drawSmaller;
            bm.smoothing = true;
            
            var s:Sprite = new Sprite();
            var m:Matrix = new Matrix();
            m.createGradientBox(size*2/drawSmaller, size*2/drawSmaller, 0);
            s.graphics.beginGradientFill(GradientType.RADIAL, [0x0000FF, 0x0000FF], [1, 0], [0, 200], m);
            s.graphics.drawCircle(size/drawSmaller, size/drawSmaller, size/drawSmaller);
            ball.draw(s);
            
            if(stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        public function init(e:Event = null):void
        {
            stage.frameRate = 30;
            stage.addEventListener(MouseEvent.CLICK, clicked);
            for(var c:int = 0; c<numParticles; c++){
                var o:Object =  new Object();
                o.speed = new Point(0, 0);
                o.pos = new Point(Width*Math.random(), Height*Math.random());
                //o.size = size+(size*Math.random());
                o.size = size;
                fields[c] = o;
            }
            
            
            var t:Timer = new Timer(25, 0);
            t.addEventListener(TimerEvent.TIMER, pool);
            t.start();
        }
        
        public function clicked(e:MouseEvent):void
        {
            gravity+=.5;
            if(gravity>1) gravity = -1;
        }
        
        public function pool(e:Event):void
        {
            var dt:Number = 1;
            
            //tf.text = ""+( 1000/((new Date().getTime())-lastTime));
                
            timesArray[arrPos] = new Date().getTime();
            arrPos++;
            if(arrPos>=30){
                arrPos = 0;
                var tot:Number = 0;
                var c2:int = 0;
                for(var times:String in timesArray){
                    if(timesArray[int(times)-1]){
                        c2++;
                        tot+=(1000/(timesArray[times]-timesArray[int(times)-1]));
                    }
                }
                tf.text = ""+(tot/c2);
            }
            ///* this methodes groups the balls on position then checks on collision on nearby elements
            var groups:Array = new Array();
            var o:Object, key:String;
            for(key in fields){
                o = fields[key];
                o.pos = o.pos.add(o.speed);
                o.speed.y+=gravity;
                if(o.pos.y+o.size*collisionOn>Height) o.speed.y = -Math.abs(o.speed.y)*.8;
                if(o.pos.x+o.size*collisionOn>Width) o.speed.x = -Math.abs(o.speed.x)*.8;
                if(o.pos.y-o.size*collisionOn<0) o.speed.y = Math.abs(o.speed.y)*.8;
                if(o.pos.x-o.size*collisionOn<0) o.speed.x = Math.abs(o.speed.x)*.8;
                
                o.speed.x = o.speed.x*damping;
                o.speed.y = o.speed.y*damping;
                
                var pos:Point = new Point(Math.round(fields[key].pos.x/(size*2*collisionOn)), Math.round(fields[key].pos.y/(size*2*collisionOn)));
                //trace(pos);
                for(var X:int = pos.x==0?pos.x:pos.x-1; X<=pos.x+1; X++){ 
                    for(var Y:int = pos.y==0?pos.y:pos.y-1; Y<=pos.y+1; Y++){ 
                        if(!groups[X])
                            groups[X] = new Array();
                        if(!groups[X][Y])
                            groups[X][Y] = new Array();
                        //trace(X+':'+Y);
                        groups[X][Y].push(fields[key]);
                    }
                }
            }
            
            var c:int = 0;
            for(var X2:String in groups){
                if(groups[X2])
                    for(var Y2:String in groups[X2]){
                        for(var item:String in groups[X2][Y2]){
                            o = groups[X2][Y2][item];
                            for(var item2:String in groups[X2][Y2]){
                                var o2:Object = groups[X2][Y2][item2];
                                if(o2!=o){
                                    //trace(o.pos);
                                    //trace(o2.pos);
                                    c++;
                                    checkObjectCollision(o.pos, o2.pos, o.speed, o2.speed, o.size*collisionOn, o2.size*collisionOn);
                                }
                            }
                        }
                    }
            }
            trace(c);
            //*/
            /* checks collission on all object 
            for(var key:String in fields){
                var o:Object = fields[key];
                for(var key2:String in fields){
                    var o2:Object = fields[key2];
                    if(o2!=o)
                        checkObjectCollision(o.pos, o2.pos, o.speed, o2.speed, o.size*collisionOn, o.size*collisionOn);
                }
                
                o.pos = o.pos.add(o.speed);
                o.speed.y+=gravity;
                if(o.pos.y+o.size*collisionOn>Height) o.speed.y = -Math.abs(o.speed.y)*.8;
                if(o.pos.x+o.size*collisionOn>Width) o.speed.x = -Math.abs(o.speed.x)*.8;
                if(o.pos.y-o.size*collisionOn<0) o.speed.y = Math.abs(o.speed.y)*.8;
                if(o.pos.x-o.size*collisionOn<0) o.speed.x = Math.abs(o.speed.x)*.8;
                
                //o.speed.x = o.speed.x*damping;
                //o.speed.y = o.speed.y*damping;
            }
            //*/

            drawAll();
        }
    
    public function checkObjectCollision(b1:Point, b2:Point, v1:Point, v2:Point, size1:Number, size2:Number):void
    {
        var b:Array = new Array(b1, b2);
        var v:Array = new Array(v1, v2);
        // get distances between the balls components
        var bVect:Point = new Point();
        bVect.x = b[1].x - b[0].x;
        bVect.y = b[1].y - b[0].y;
        
        // calculate magnitude of the vector separating the balls
        var bVectMag:Number = Math.sqrt(bVect.x * bVect.x + bVect.y * bVect.y);
        if (bVectMag < size1 + size2){
            // get angle of bVect
            var theta:Number  = Math.atan2(bVect.y, bVect.x);
            // precalculate trig values
            var sine:Number = Math.sin(theta);
            var cosine:Number = Math.cos(theta);
            
            /* bTemp will hold rotated ball positions. You 
            just need to worry about bTemp[1] position*/
            var bTemp:Array = new Array(new Point(), new Point());
            
            /* b[1]'s position is relative to b[0]'s
            so you can use the vector between them (bVect) as the 
            reference point in the rotation expressions.
            bTemp[0].x and bTemp[0].y will initialize
            automatically to 0.0, which is what you want
            since b[1] will rotate around b[0] */
            bTemp[1].x  = cosine * bVect.x + sine * bVect.y;
            bTemp[1].y  = cosine * bVect.y - sine * bVect.x;
            
            // rotate Temporary velocities
            var vTemp:Array = new Array(new Point(), new Point());
            vTemp[0].x  = cosine * v[0].x + sine * v[0].y;
            vTemp[0].y  = cosine * v[0].y - sine * v[0].x;
            vTemp[1].x  = cosine * v[1].x + sine * v[1].y;
            vTemp[1].y  = cosine * v[1].y - sine * v[1].x;
            
            /* Now that velocities are rotated, you can use 1D
            conservation of momentum equations to calculate 
            the final velocity along the x-axis. */
            var vFinal:Array = new Array(new Point(), new Point());
            // final rotated velocity for b[0]
            vFinal[1].x = ((1 - 1) * vTemp[1].x + 2 * 1 * 
                vTemp[0].x) / (1 + 1);
            vFinal[1].y = vTemp[1].y;
            
            // hack to avoid clumping
            bTemp[0].x += vFinal[0].x;
            bTemp[1].x += vFinal[1].x;
            
            /* Rotate ball positions and velocities back
            Reverse signs in trig expressions to rotate 
            in the opposite direction */
            // rotate balls
            
            var bFinal:Array = new Array(new Point(), new Point());
            bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y;
            bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x;
            bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y;
            bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x;
            
            // update balls to screen position
            var overlapping:Number = (size1+size2)-bVectMag;
            var add:Point = getDirectionsFromAngle(getAngle(b1, b2), overlapping);
            b[1].x = b[0].x + bFinal[1].x+add.x;
            b[1].y = b[0].y + bFinal[1].y+add.y;
            b[0].x = b[0].x + bFinal[0].x;
            b[0].y = b[0].y + bFinal[0].y;
            
            // update velocities
            v[0].x = cosine * vFinal[0].x - sine * vFinal[0].y;
            v[0].y = cosine * vFinal[0].y + sine * vFinal[0].x;
            v[1].x = cosine * vFinal[1].x - sine * vFinal[1].y;
            v[1].y = cosine * vFinal[1].y + sine * vFinal[1].x;
            
        }
    }
    
    
    
        private function drawAll():void
        {
            draw.graphics.clear();
            
            bmd.fillRect(new Rectangle(0, 0, Width/drawSmaller, Height/drawSmaller), 0xFFFFFF);
            for(var key:String in fields)
            {
                var m:Matrix = new Matrix(1, 0, 0, 1, (fields[key].pos.x-fields[key].size)/drawSmaller, (fields[key].pos.y-fields[key].size)/drawSmaller);
                //m.scale(fields[key].size/size/drawSmaller, fields[key].size/size/drawSmaller);
                bmd.draw(ball, m);
            }
            
            theWaterColor.fillRect(new Rectangle(0, 0, Width/drawSmaller, Height/drawSmaller), 0x0000FF);
            bm.bitmapData = theWaterColor;
            //bmd.draw(draw);
            //bm.bitmapData = bmd;
            bm.bitmapData.threshold(bmd, new Rectangle(0, 0, Width/drawSmaller, Height/drawSmaller), new Point(0, 0), ">=", 0xbbbbbb, 0x00FFFFFF, 0x00FFFFFF, false);
            //bm.smoothing =true;
        }
        
        protected function getDirectionsFromAngle(angle:Number, distance:Number):Point
        {
            return new Point(Math.cos((angle-90)/180*Math.PI)*distance, Math.sin((angle-90)/180*Math.PI)*distance);
        }
        
        public static function getDistanceBetweenPoints(p1:Point, p2:Point):Number
        {
            return Math.sqrt(Math.pow(p1.x-p2.x, 2)+Math.pow(p1.y-p2.y, 2));
        }
        
        public static function getAngle(p1:Point, p2:Point):Number
        {
            var opposite:Number = p1.x-p2.x;
            var adjacent:Number = p1.y-p2.y;
            var angle:Number = Math.atan(opposite/adjacent)/Math.PI*180;
            if(adjacent<0)
                if(opposite>0)
                    angle = 90-Math.abs(angle)+90;
                else
                    angle = -180+angle;
            
            if(opposite>0) angle-=360;
            return -angle;
        }
    }
}