water physics
click to change gravity
I know this is far from ready, but I just can't get it any better......
anyone?
/**
* 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;
}
}
}