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