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

Particle Fluid + WaterFilter

流体シミュレーション+水フィルタ (Particle Fluid + WaterFilter)
/**
 * Copyright shohei909 ( http://wonderfl.net/user/shohei909 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/7i7A
 */

// forked from shohei909's 流体シミュ+水フィルタ
// forked from saharan's 流体シミュ最適化/Particle Fluid Optimization
/*
 * 流体シミュ最適化/Particle Fluid Optimization
 *
 * クリック:水を注ぐ
 * Click:Pouring
 * 
 * Order:O(n)
 * ・空間分割により高速化。
 * ・近接粒子の保存方法を変更し最適化。
 * ・その他パラメーター調整。より水っぽく!
 */
 //image: http://www.flickr.com/photos/ttdesign/2558614528/
package {
    import flash.system.LoaderContext;
    import flash.net.URLRequest;
    import flash.utils.*;
    import flash.text.*;
    import flash.filters.*;
    import flash.geom.*;
    import flash.events.*;
    import flash.display.*;
    import net.hires.debug.Stats;
    import mx.utils.Base64Decoder;
    [SWF(frameRate = 60)]
    public class SPH extends Sprite {
        public static const GRAVITY:Number = 0.05;//重力
        public static const RANGE:Number = 7;//影響半径
        public static const RANGE2:Number = RANGE * RANGE;//影響半径の二乗
        public static const DENSITY:Number = 0.2;//流体の密度
        public static const PRESSURE:Number = 2;//圧力係数
        public static const VISCOSITY:Number = 0.09;//粘性係数
        public static const NUM_GRIDS:int = 66;//グリッド数(≒ 465 / RANGE)
        public static const INV_GRID_SIZE:Number = 1/ (465 / NUM_GRIDS);//グリッドサイズの逆数(≒ 1 / RANGE)
        public static const IMG_URL:String = "http://assets.wonderfl.net/images/related_images/a/a2/a227/a227dbacb189f4866a9f2d80acce25498fdb2de3"
        public static const SHADER_DATA:String = "pQEAAACkCwB3YXRlckZpbHRlcqAMbmFtZXNwYWNlAEFJRgCgDHZlbmRvcgBBZG9iZVN5c3RlbXMAoAh2ZXJzaW9uAAIAoAxkZXNjcmlwdGlvbgB3YXRlcgChAQIAAAxfT3V0Q29vcmQAoQEBAAACZGlzdGFuY2UAogFtaW5WYWx1ZQAAAAAAogFkZWZhdWx0VmFsdWUAQsgAAKEBAQAAAWhlaWdodACiAWRlZmF1bHRWYWx1ZQBAoAAAowAEYmFja0ltYWdlAKMBBHdhdGVySW1hZ2UAoQIEAQAPZHN0AB0CAMEAABAAMgIAIAAAAAAyAgAQv4AAAB0DAMECABAAAQMAwQIAsAAwBADxAwAQAR0DAPMEABsAMgIAIAAAAAAyAgAQP4AAAB0EAMECABAAAQQAwQIAsAAwBQDxBAAQAR0EAPMFABsAMgIAIL+AAAAyAgAQAAAAAB0FAMECABAAAQUAwQIAsAAwBgDxBQAQAR0FAPMGABsAMgIAID+AAAAyAgAQAAAAAB0GAMECABAAAQYAwQIAsAAwBwDxBgAQAR0GAPMHABsAHQIAIAMAAAABAgAgAwBAAB0CABACAIAAAQIAEAMAgAAdAgAgAgDAAAMCACADAMAAHQIAEAIAgAAdAgAgBAAAAAECACAEAEAAHQcAgAIAgAABBwCABACAAB0CACAHAAAAAwIAIAQAwAAdBwCAAgCAAB0CACAFAAAAAQIAIAUAQAAdBwBAAgCAAAEHAEAFAIAAHQIAIAcAQAADAgAgBQDAAB0HAEACAIAAHQIAIAYAAAABAgAgBgBAAB0HACACAIAAAQcAIAYAgAAdAgAgBwCAAAMCACAGAMAAHQcAIAIAgAAdAgAgBwCAAAICACAHAEAAHQgAgAIAgAAdAgAgBwAAAAICACACAMAAHQgAQAIAgAAdAgAgAgDAAAECACAHAAAAHQcAEAIAgAABBwAQBwBAAB0CACAHAMAAAQIAIAcAgAAdBwAQAADAAAMHABACAIAAMgIAIECAAAAECAAgAgCAAAMIACAHAMAAHQIAIAgAgAABAgAgAACAAB0HABAAAMAAAwcAEAIAgAAdCAAxCAAQAAMIADEHAPAAHQgAwQIAEAABCADBCACwADAJAPEIABAAHQEA8wkAGwA="
        
/*
<languageVersion:1.0;>
kernel waterFilter<namespace:"AIF";vendor:"AdobeSystems";version:2;description:"water";>{
    parameter float distance<minValue:0.0;defaultValue:100.0;>;
    parameter float height<defaultValue:5.0;>;
    input image4 backImage;
    input image4 waterImage;
    output pixel4 dst;
    void evaluatePixel(){
        float2 o = outCoord();
        float4 a = sampleNearest(waterImage,o+float2(0.0,-1.0));
        float4 b = sampleNearest(waterImage,o+float2(0.0,1.0));
        float4 l = sampleNearest(waterImage,o+float2(-1.0,0.0));
        float4 r = sampleNearest(waterImage,o+float2(1.0,0.0));
        
        float a2 = (a.r+a.g+a.b)*a.a;
        float b2 = (b.r+b.g+b.b)*b.a;
        float l2 = (l.r+l.g+l.b)*l.a;
        float r2 = (r.r+r.g+r.b)*r.a;
        
        dst = sampleNearest( backImage, o+float2(r2-l2,b2-a2)*(height*(height*(a2+b2+l2+r2)/4.0+distance)) );
    }
}
*/
        private var shader:Shader;
        private var shaderFilter:ShaderFilter;
        private var img:BitmapData;
        private var bitmap:Bitmap;
        private var background:Loader;
        private var particles:Vector.<Particle>;
        private var numParticles:uint;
        private var neighbors:Vector.<Neighbor>;
        private var numNeighbors:uint;
        private var color:ColorTransform;
        private var filter:BlurFilter;
        private var count:int;
        private var press:Boolean;
        private var labP:TextField;
        private var grids:Vector.<Vector.<Grid>>;
        
        public function SPH() {
            initialize();
        }
        
        private function initialize():void {
            color = new ColorTransform(1,1,1,0)
            filter = new BlurFilter(4,4)
            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;
            var decoder:Base64Decoder = new Base64Decoder ;
            decoder.decode( SHADER_DATA );
            var ba:ByteArray = decoder.toByteArray();
            shader = new Shader( ba );
            
            background = new Loader();
            background.load( new URLRequest(IMG_URL), new LoaderContext(true) );
            addChild(background);
            
            img = new BitmapData(465, 465, true, 0);
            addChild( new Bitmap(img) ).alpha = 0.5;
            shader.data.height.value = [3];
            shader.data.distance.value = [5];
            shader.data.waterImage.input = img;
            shaderFilter = new ShaderFilter( shader );
            background.filters = [ shaderFilter ];
            
            addEventListener(Event.ENTER_FRAME, frame);
            labP = new TextField();
            labP.selectable = false;
            var tf:TextFormat = new TextFormat();
            tf.color = 0xffffff;
            tf.size = 12;
            tf.font = "MS UI Gothic";
            labP.defaultTextFormat = tf;
            labP.width = 256;
            labP.x = 70;
            var st:Stats = new Stats();
            st.alpha = 0.75;
            addChild(st);
            addChild(labP);
            
            
            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();
            img.lock();
            img.colorTransform(img.rect, color);
            draw();
            img.applyFilter( img, img.rect, new Point, filter );
            img.unlock();
            shader.data.waterImage.input = img;
            background.filters = [ shaderFilter ];
            
            labP.text = "Particle : " + numParticles;
        }

        private function draw():void {
            var w:Number = Sphere.W/2;
            var h:Number = Sphere.H/2;
            for(var i:int = 0; i < numParticles; i++) {
                var p:Particle = particles[i];
                img.draw( sphere, new Matrix(1,0,0,1,p.x-w,p.y-h) );
            }
        } 

        private function pour():void {
            if(count % 3 == 0)
                for(var i:int = -4; i <= 4; i++) {
                     particles[numParticles++] = new Particle(mouseX + i * RANGE * 0.8, 5);
                     particles[numParticles - 1].vy = 3;
                }
        }

        private function move():void {
            count++;
            var i:int;
            var j:int;
            var dist:Number;
            var p:Particle;
            updateGrids();
            findNeighbors();
            calcPressure();
            calcForce();
            for(i = 0; i < numParticles; i++) {
                p = particles[i];
                p.move();
            }
        }

        private function updateGrids():void {
            var i:int;
            var j:int;
            for(i = 0; i < NUM_GRIDS; i++)
                for(j = 0; j < NUM_GRIDS; j++)
                    grids[i][j].clear();
            for(i = 0; i < numParticles; i++) {
                var p:Particle = particles[i];
                p.fx = p.fy = p.density = 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;
                grids[p.gx][p.gy].add(p);
            }
        }

        private function findNeighbors():void {
            numNeighbors = 0;
            for(var i:int = 0; i < numParticles; i++) {
                var p:Particle = particles[i];
                var xMin:Boolean = p.gx != 0;
                var xMax:Boolean = p.gx != NUM_GRIDS - 1;
                var yMin:Boolean = p.gy != 0;
                var 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]);
            }
        }

        private function findNeighborsInGrid(pi:Particle, g:Grid):void {
            for(var j:int = 0; j < g.numParticles; j++) {
                var pj:Particle = g.particles[j];
                if(pi == pj)
                    continue;
                var 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 calcPressure():void {
            for(var i:int = 0; i < numParticles; i++) {
                var p:Particle = particles[i];
                if(p.density < DENSITY)
                    p.density = DENSITY;
                p.pressure = p.density - DENSITY;
            }
        }

        private function calcForce():void {
            var i:int;
            for(i = 0; i < numNeighbors; i++) {
                var n:Neighbor = neighbors[i];
                n.calcForce();
            }
        }
    }
}
import flash.geom.Point;
import flash.display.BitmapData;

var sphere:BitmapData = new Sphere; 
class Sphere extends BitmapData{
    static public const W:int = 15; 
    static public const H:int = 15; 
    function Sphere(){
        var w:int = W, h:int = H;
        super( w, h, true, 0 );
        for( var i:int = 0; i < w; i++ ){
            for( var j:int = 0; j < h; j++ ){
                var r:Number = 1-(Math.sqrt((w/2-i)*(w/2-i)+(h/2-j)*(h/2-j)))*2/w
                if(r<0){r=0} 
                setPixel32( i, j, ((r*0xFF) << 24) + 0xFFFFFF )
            }
        }
    }
}

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 pressure:Number;
    public function Particle(x:Number, y:Number) {
        this.x = x;
        this.y = y;
        vx = vy = fx = fy = 0;
    }

    public function move():void {
        vy += SPH.GRAVITY;
        vx += fx;
        vy += fy;
        x += vx;
        y += vy;
        if(x < 5)
            vx += (5 - x) * 0.5 - vx * 0.5;
        if(y < 5)
            vy += (5 - y) * 0.5 - vy * 0.5;
        if(x > 460)
            vx += (460 - x) * 0.5 - vx * 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 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 / SPH.RANGE;
        var temp:Number = weight * weight * weight;
        p1.density += temp;
        p2.density += temp;
        temp = 1 / distance;
        nx *= temp;
        ny *= temp;
    }

    public function calcForce():void {
        var pressureWeight:Number = weight * (p1.pressure + p2.pressure) / (p1.density + p2.density) * SPH.PRESSURE;
        var viscosityWeight:Number = weight / (p1.density + p2.density) * SPH.VISCOSITY;
        p1.fx += nx * pressureWeight;
        p1.fy += ny * pressureWeight;
        p2.fx -= nx * pressureWeight;
        p2.fy -= ny * pressureWeight;
        var rvx:Number = p2.vx - p1.vx;
        var rvy:Number = p2.vy - p1.vy;
        p1.fx += rvx * viscosityWeight;
        p1.fy += rvy * viscosityWeight;
        p2.fx -= rvx * viscosityWeight;
        p2.fy -= rvy * viscosityWeight;
    }
}

class Grid {
    public var particles:Vector.<Particle>;
    public var numParticles:uint;
    public function Grid() {
        particles = new Vector.<Particle>;
        numParticles = 0;
    }

    public function clear():void {
        numParticles = 0;
    }

    public function add(p:Particle):void {
        particles[numParticles++] = p;
    }
}