Sludge, Glorious Sludge
/**
* Copyright yonatan ( http://wonderfl.net/user/yonatan )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/a1kj
*/
// forked from yonatan's Thicker liquid simulation
// forked from iunpin's forked from: Liquid simulation
// forked from zlaper's Liquid simulation
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.utils.getTimer;
import net.hires.debug.Stats;
[SWF(frameRate="30", backgroundColor="0xeeeeee")]
public class LiquidTest extends Sprite
{
private var particles:Vector.<Particle> = new Vector.<Particle>();
private var gsizeX:int = 100;
private var gsizeY:int = 100;
private var grid:Vector.<Vector.<Node>>;
private var active:Vector.<Node> = new Vector.<Node>();
private var g:Graphics;
private var water:Material = new Material(1.0, 1.0, 1.0, 1.0, 1.0, 1.0);
private var pressed:Boolean;
private var pressedprev:Boolean;
private var mx:int;
private var my:int;
private var mxprev:int;
private var myprev:int;
private var _canvas:BitmapData;
private var _bitmap:Bitmap;
private var _blur:BlurFilter = new BlurFilter(8,8,2);
private var _color:ColorTransform = new ColorTransform(.5,.5,.5,32);
private var _g:Graphics;
private var _s:Shape;
private var _o:Point = new Point(0, 0);
public function LiquidTest()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
public function init(event:Event):void
{
_canvas = new BitmapData(400, 400, true, 0xFFFFFFFF);
_bitmap = addChild(new Bitmap(this._canvas)) as Bitmap;
_bitmap.x = _bitmap.y = 65;
_s = new Shape();
_g = _s.graphics;
var i:int, j:int;
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] = new Node();
}
}
stage.addEventListener(Event.ENTER_FRAME, paint);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mousePressed);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseReleased);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoved);
var p:Particle;
for (i = 0; i < 50; i++)
for (j = 0; j < 80; j++)
{
var r:int = i / 50 * 0xFF;
var g:int = j / 80 * 0xFF;
p = new Particle(water, i + 4, j + 4, 0.0, 0.0, 0xff000000 | (r << 16) | (g << 8));
particles.push(p);
}
addChild(new Stats());
}
public function paint(event:Event):void
{
simulate();
_canvas.lock();
_canvas.fillRect(_canvas.rect,0x0);
for each (var p:Particle in particles)
{
_canvas.setPixel32(int(4.0 * p.x), int(4.0 * p.y), p.color)
//_canvas.setPixel32(int(4.0 * (p.x - p.u)), int(4.0 * (p.y - p.v)), p.color);
}
_canvas.draw(_s);
_canvas.applyFilter(_canvas, _canvas.rect, _o, _blur);
_canvas.colorTransform(_canvas.rect, _color);
_canvas.unlock();
}
public function mouseMoved(event:MouseEvent):void
{
var p:Point = new Point(stage.mouseX, stage.mouseY);
p = _bitmap.globalToLocal(p);
mx = p.x;
my = p.y;
}
public function mousePressed(event:MouseEvent):void
{
pressed = true;
}
public function mouseReleased(event:MouseEvent):void
{
pressed = false;
}
public function simulate():void
{
var drag:Boolean = false;
var mdx:Number = 0.0, mdy:Number = 0.0;
if (pressed && pressedprev)
{
drag = true;
mdx = 0.25 * (mx - mxprev);
mdy = 0.25 * (my - myprev);
}
pressedprev = pressed;
mxprev = mx;
myprev = my;
var n:Node, p:Particle;
for each (n in active)
n.clear();
active.length = 0;
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.px[0] = (0.5 * x * x + 1.5 * x + 1.125);
p.gx[0] = (x + 1.5);
x += 1.0;
p.px[1] = (-x * x + 0.75);
p.gx[1] = (-2.0 * x);
x += 1.0;
p.px[2] = (0.5 * x * x - 1.5 * x + 1.125);
p.gx[2] = (x - 1.5);
y = p.cy - p.y;
p.py[0] = (0.5 * y * y + 1.5 * y + 1.125);
p.gy[0] = (y + 1.5);
y += 1.0;
p.py[1] = (-y * y + 0.75);
p.gy[1] = (-2.0 * y);
y += 1.0;
p.py[2] = (0.5 * y * y - 1.5 * y + 1.125);
p.gy[2] = (y - 1.5);
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
n = grid[p.cx + i][p.cy + j];
if (!n.active)
{
active.push(n);
n.active = true;
}
phi = p.px[i] * p.py[j];
n.m += phi * p.mat.m;
n.d += phi;
n.gx += p.gx[i] * p.py[j];
n.gy += p.px[i] * p.gy[j];
}
}
}
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;
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.0;
fy = 0.0;
if (p.x < 4.0)
fx += p.mat.m * (4.0 - p.x);
else if (p.x > gsizeX - 5)
fx += p.mat.m * (gsizeX - 5 - p.x);
if (p.y < 4.0)
fy += p.mat.m * (4.0 - p.y);
else if (p.y > gsizeY - 5)
fy += p.mat.m * (gsizeY - 5 - p.y);
if (drag)
{
var vx:Number = Math.abs(p.x - 0.25 * mx);
var vy:Number = Math.abs(p.y - 0.25 * my);
if ((vx < 10.0) && (vy < 10.0))
{
weight = p.mat.m * (1.0 - vx * 0.10) * (1.0 - vy * 0.10);
fx += weight * (mdx - p.u);
fy += weight * (mdy - p.v);
}
}
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
n = grid[(p.cx + i)][(p.cy + j)];
phi = p.px[i] * p.py[j];
n.ax += -((p.gx[i] * p.py[j]) * pressure) + fx * phi;
n.ay += -((p.px[i] * p.gy[j]) * pressure) + fy * phi;
}
}
}
for each (n in active)
{
if (n.m > 0.0)
{
n.ax /= n.m;
n.ay /= n.m;
n.ay += 0.03;
}
}
var mu:Number, mv:Number;
for each (p in particles)
{
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
n = grid[(p.cx + i)][(p.cy + j)];
phi = p.px[i] * p.py[j];
p.u += phi * n.ax;
p.v += phi * n.ay;
}
}
mu = p.mat.m * p.u;
mv = p.mat.m * p.v;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
n = grid[(p.cx + i)][(p.cy + j)];
phi = p.px[i] * p.py[j];
n.u += phi * mu;
n.v += phi * mv;
}
}
}
for each (n in active)
{
if (n.m > 0.0)
{
n.u /= n.m;
n.v /= n.m;
}
}
var gu:Number, gv:Number;
for each (p in particles)
{
gu = 0.0;
gv = 0.0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
n = grid[(p.cx + i)][(p.cy + j)];
phi = p.px[i] * p.py[j];
gu += phi * n.u;
gv += phi * n.v;
}
}
p.x += gu;
p.y += gv;
p.u += 1.0 * (gu - p.u);
p.v += 1.0 * (gv - p.v);
if (p.x < 1.0)
{
p.x = (1.0 + Math.random() * 0.01);
p.u = 0.0;
}
else if (p.x > gsizeX - 2)
{
p.x = (gsizeX - 2 - Math.random() * 0.01);
p.u = 0.0;
}
if (p.y < 1.0)
{
p.y = (1.0 + Math.random() * 0.01);
p.v = 0.0;
}
else if (p.y > gsizeY - 2)
{
p.y = (gsizeY - 2 - Math.random() * 0.01);
p.v = 0.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 function clear():void
{
m = d = gx = gy = u = v = ax = ay = 0.0;
active = false;
}
}
class Particle
{
public var color:uint;
public var mat:Material;
public var x:Number;
public var y:Number;
public var u:Number;
public var v:Number;
public var dudx:Number = 0;
public var dudy:Number = 0;
public var dvdx:Number = 0;
public var dvdy:Number = 0;
public var cx:int;
public var cy:int;
public var px:Vector.<Number> = new Vector.<Number>(3, true);
public var py:Vector.<Number> = new Vector.<Number>(3, true);
public var gx:Vector.<Number> = new Vector.<Number>(3, true);
public var gy:Vector.<Number> = new Vector.<Number>(3, true);
public function Particle(mat:Material, x:Number, y:Number, u:Number, v:Number, color:uint = 0xFF000000)
{
this.color = color;
this.mat = mat;
this.x = x;
this.y = y;
this.u = u;
this.v = v;
}
}
class Material
{
public var m:Number;
public var rd:Number;
public var k:Number;
public var v:Number;
public var d:Number;
public var g:Number;
public function Material(m:Number, rd:Number, k:Number, v:Number, d:Number, g:Number)
{
this.m = m;
this.rd = rd;
this.k = k;
this.v = v;
this.d = d;
this.g = g;
}
}