fluid squares
Fluid \ Learning \ Processing 1.0
http://www.processing.org/learning/topics/fluid.html
/**
* Fluid \ Learning \ Processing 1.0
* http://www.processing.org/learning/topics/fluid.html
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Rectangle;
import net.hires.debug.Stats;
[SWF(backgroundColor=0x0, width=1000, height=500, frameRate=30)]
public class FluidLine extends Sprite
{
private var pmouseX:Number;
private var pmouseY:Number;
private var canvasWidth:int = 1000;
private var canvasHeight:int = 500;
private var mousePressed:Boolean;
private var resolution:int = 25;
private var penSize:int = 40;
private var numCols:int = canvasWidth / resolution;;
private var numRows:int = canvasHeight / resolution;
private var numParticles:int = 5000;
private var gridDatasVectors:Vector.<Vector.<GridData>> = new Vector.<Vector.<GridData>>();
private var particles:Vector.<Particle> = new Vector.<Particle>(numParticles, true);
private var pcount:int = 0;
public function FluidLine()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
stage.quality = StageQuality.MEDIUM;
//addChild(new Stats());
// create multi-dimentional Vector grid
for (var col:int = 0; col < numCols; ++col)
{
gridDatasVectors[col] = new Vector.<GridData>(numRows, true);
for (var row:int = 0; row < numRows; ++row)
{
var gridData:GridData = new GridData(col * resolution, row * resolution, resolution);
gridData.col = col;
gridData.row = row;
gridDatasVectors[col][row] = gridData;
addChild(gridData);
}
}
// randomly place all particles on the canvas
for (var i:int = 0; i < numParticles; i++)
{
particles[i] = new Particle(Math.random() * canvasWidth, Math.random() * canvasHeight);
}
// establish relationship between vector rectangles
for (col = 0; col < numCols; ++col)
{
for (row = 0; row < numRows; ++row)
{
gridData = gridDatasVectors[col][row];
if (row > 0)
{
var up:GridData = gridDatasVectors[col][row - 1];
// establish "up" for this rectangle
gridData.up = up;
// establish "down" for upper rectangle
up.down = gridData;
}
if (col > 0)
{
var left:GridData = gridDatasVectors[col - 1][row];
gridData.left = left;
left.right = gridData;
}
if (row > 0 && col > 0)
{
var upperLeft:GridData = gridDatasVectors[col - 1][row - 1];
gridData.upperLeft = upperLeft;
upperLeft.lowerRight = gridData;
}
if (row > 0 && col < numCols - 1)
{
var upperRight:GridData = gridDatasVectors[col + 1][row - 1];
gridData.upperRight = upperRight;
upperRight.lowerLeft = gridData;
}
}
}
gridDatasVectors.fixed = true;
addEventListener(Event.ENTER_FRAME, draw);
}
private function draw(e:Event):void
{
var mouseXvel:Number = mouseX - pmouseX;
var mouseYvel:Number = mouseY - pmouseY;
for each(var gridDatas:Vector.<GridData> in gridDatasVectors)
{
for each(var gridData:GridData in gridDatas)
{
if (mousePressed)
{
updateGridDataVelocity(gridData, mouseXvel, mouseYvel, penSize);
}
updatePressure(gridData);
}
}
graphics.clear();
graphics.lineStyle(1, 0xFFFFCC);
updateParticle();
for each(gridDatas in gridDatasVectors)
{
for each(gridData in gridDatas)
{
updateVelocity(gridData);
}
}
pmouseX = mouseX;
pmouseY = mouseY;
}
public function updateParticle():void
{
for each(var p:Particle in particles)
{
if (p.x >= 0 && p.x < canvasWidth && p.y >= 0 && p.y < canvasHeight)
{
// determine grid location based on Particle location
var col:int = int(p.x / resolution);
var row:int = int(p.y / resolution);
if (col > numCols - 1) col = numCols - 1;
if (row > numRows - 1) row = numRows - 1;
var gridData:GridData = gridDatasVectors[col][row];
var ax:Number = (p.x % resolution) / resolution;
var ay:Number = (p.y % resolution) / resolution;
p.xvel += (1 - ax) * gridData.xvel * 0.05;
p.yvel += (1 - ay) * gridData.yvel * 0.05;
p.xvel += ax * gridData.right.xvel * 0.05;
p.yvel += ax * gridData.right.yvel * 0.05;
p.xvel += ay * gridData.down.xvel * 0.05;
p.yvel += ay * gridData.down.yvel * 0.05;
p.x += p.xvel;
p.y += p.yvel;
var dx:Number = p.px - p.x;
var dy:Number = p.py - p.y;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
var limit:Number = Math.random() * 2;
if (dist > limit)
{
graphics.moveTo(p.x, p.y);
graphics.lineTo(p.px, p.py);
}
else
{
graphics.moveTo(p.x, p.y);
graphics.lineTo(p.x + limit, p.y + limit);
}
p.px = p.x;
p.py = p.y;
}
else
{
p.x = p.px = Math.random() * canvasWidth;
p.y = p.py = Math.random() * canvasHeight;
p.xvel = 0;
p.yvel = 0;
}
p.xvel *= 0.5;
p.yvel *= 0.5;
}
}
public function updateGridDataVelocity(gridData:GridData, mvelX:int, mvelY:int, penSize:Number):void
{
var dx:Number = gridData.x - mouseX;
var dy:Number = gridData.y - mouseY;
var dist:Number = Math.sqrt(dy * dy + dx * dx);
if (dist < penSize)
{
if (dist < 4)
{
dist = penSize;
}
var power:Number = penSize / dist;
gridData.xvel += mvelX * power;
gridData.yvel += mvelY * power;
}
}
public function updatePressure(gridData:GridData):void
{
var pressureX:Number = (
gridData.upperLeft.xvel * 0.5
+ gridData.left.xvel
+ gridData.lowerLeft.xvel * 0.5
- gridData.upperRight.xvel * 0.5
- gridData.right.xvel
- gridData.lowerRight.xvel * 0.5
);
var pressureY:Number = (
gridData.upperLeft.yvel * 0.5
+ gridData.up.yvel
+ gridData.upperRight.yvel * 0.5
- gridData.lowerLeft.yvel * 0.5
- gridData.down.yvel
- gridData.lowerRight.yvel * 0.5
);
gridData.pressure = (pressureX + pressureY) * 0.25;
// pass colors to grids
gridData.updateColorRect(pressureX, pressureY);
}
public function updateVelocity(gridData:GridData):void
{
gridData.xvel += (
gridData.upperLeft.pressure * 0.5
+ gridData.left.pressure
+ gridData.lowerLeft.pressure * 0.5
- gridData.upperRight.pressure * 0.5
- gridData.right.pressure
- gridData.lowerRight.pressure * 0.5
) * 0.25;
gridData.yvel += (
gridData.upperLeft.pressure * 0.5
+ gridData.up.pressure
+ gridData.upperRight.pressure * 0.5
- gridData.lowerLeft.pressure * 0.5
- gridData.down.pressure
- gridData.lowerRight.pressure * 0.5
) * 0.25;
// pass colors to grids
gridData.updateColorRect(gridData.xvel, gridData.yvel);
}
private function mouseDownHandler(e:Event):void
{
mousePressed = true;
}
private function mouseUpHandler(e:Event):void
{
mousePressed = false;
}
}
}
// define params for grid
import flash.display.Sprite;
import flash.geom.Rectangle;
import flash.display.Shape;
class BaseGridData extends Sprite
{
public var col:int = 0;
public var row:int = 0;
public var xvel:Number = 0;
public var yvel:Number = 0;
public var pressure:Number = 0;
public var rectangle:Rectangle;
public var colorRect:ColorRect;
}
// handle null grid data
class NullGridData extends BaseGridData {}
// handle grid data
import flash.geom.Rectangle;
import flash.geom.ColorTransform;
class GridData extends BaseGridData
{
public function GridData(x:int, y:int, res:Number)
{
this.x = x;
this.y = y;
rectangle = new Rectangle(x, y, res, res);
colorRect = new ColorRect(res);
this.addChild(colorRect);
}
public function updateColorRect(xVel:Number, yVel:Number):void
{
var rOffset:Number = Math.abs(xVel);
var gOffset:Number = Math.abs((xVel+yVel)/2);
var bOffset:Number = Math.abs(yVel);
colorRect.transform.colorTransform = new ColorTransform(1, 1, 1, 1, rOffset, gOffset, bOffset, 0);
}
public var up:BaseGridData = new NullGridData();
public var upperRight:BaseGridData = new NullGridData();
public var right:BaseGridData = new NullGridData();
public var lowerRight:BaseGridData = new NullGridData();
public var down:BaseGridData = new NullGridData();
public var lowerLeft:BaseGridData = new NullGridData();
public var left:BaseGridData = new NullGridData();
public var upperLeft:BaseGridData = new NullGridData();
}
// handle paSpriteimport
class Particle
{
public function Particle(x:Number, y:Number)
{
this.x = px = x;
this.y = py = y;
}
public var x:Number;
public var y:Number;
public var px:Number;
public var py:Number;
public var xvel:Number = 0;
public var yvel:Number = 0;
}
// handle color rectabgle
import flash.display.Sprite;
import flash.display.Shape;
class ColorRect extends Sprite
{
public function ColorRect(res:Number)
{
var child:Shape = new Shape();
child.graphics.beginFill(0x000000);
child.graphics.lineStyle(1, 0x111111);
child.graphics.drawRect(0, 0, res/2, res/2);
child.graphics.endFill();
addChild(child);
}
}