Ball Drop
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
[SWF(backgroundColor="#999999")]
public class QuickPhysics extends Sprite
{
private static const NUM_CIRCLES:int = 7;
private var circles:Vector.<Circle>;
private var lines:Vector.<Line>;
private var simulator:Physics;
private var renderer:Renderer;
public function QuickPhysics()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.frameRate = 40;
circles = new Vector.<Circle>();
while( circles.length <= NUM_CIRCLES )
{
var circle:Circle = new Circle( 10 + Math.random() * 40, 1, Math.random(), Math.random() );
circle.x = 20 + Math.random() * 400;
circles.push( circle );
}
lines = Vector.<Line>( [
new Line( 0, 0, 300, 70 ),
new Line( 450, 0, 450, 100 ),
new Line( 450, 100, 150, 300 ),
new Line( -50, 200, 10, 400 ),
new Line( 10, 400, 100, 500 )
] );
simulator = new Physics();
renderer = new Renderer();
renderer.graphics = graphics;
addEventListener( Event.ENTER_FRAME, onEnterFrame );
}
private function onEnterFrame( event:Event ) : void
{
simulator.step( 1 / stage.frameRate, circles, lines );
renderer.render( circles, lines );
}
}
}
import __AS3__.vec.Vector;
import flash.display.*;
import flash.geom.Point;
const G:Number = 9.81 * 40; //pixel per meter ratio
const EPSILON:Number = 1.5;
class Element
{
public var coeffRest:Number;
public var coeffFric:Number;
public function Element( coeffRest:Number = 0.5, coeffFric:Number = 0.5 )
{
this.coeffRest = coeffRest;
this.coeffFric = coeffFric;
}
}
class Line extends Element
{
public var ax:Number, ay:Number, bx:Number, by:Number;
public function Line( ax:Number, ay:Number, bx:Number, by:Number, coeffRest:Number = 0.5, coeffFric:Number = 0.5 )
{
super( coeffRest, coeffFric );
this.ax = ax;
this.ay = ay;
this.bx = bx;
this.by = by;
}
}
class Circle extends Element
{
public var radius:Number;
public var x:Number = 0;
public var y:Number = 0;
public var vx:Number = 0;
public var vy:Number = 0;
public var theta:Number = 0;
public var av:Number = 0;
public var mass:Number;
public var inertiaTensor:Number;
public function Circle( radius:Number = 10, density:Number = 1.0, coeffRest:Number = 0.5, coeffFric:Number = 0.5 )
{
super( coeffRest, coeffFric );
this.radius = radius;
this.density = density;
}
public function set density( value:Number ) : void
{
var area:Number = Math.PI * radius * radius;
mass = value * area;
inertiaTensor = ( 2 / 5 ) * mass * radius * radius;
}
public function update( dt:Number ) : void
{
vy += G * dt;
x += vx * dt;
y += vy * dt;
theta += av * dt;
}
}
class Renderer
{
public static const HEX:uint = 0xFFFFFF;
public var graphics:Graphics;
public function render( circles:Vector.<Circle>, lines:Vector.<Line> ) : void
{
graphics.clear();
graphics.lineStyle( 1, HEX );
for each( var circle:Circle in circles )
{
renderCircle( circle );
}
for each( var line:Line in lines )
{
renderLine( line );
}
}
public function renderCircle( circle:Circle ) : void
{
graphics.drawCircle( circle.x, circle.y, circle.radius );
graphics.moveTo( circle.x, circle.y );
graphics.lineTo( circle.x + Math.cos( circle.theta ) * circle.radius, circle.y + Math.sin( circle.theta ) * circle.radius );
}
public function renderLine( line:Line ) : void
{
graphics.moveTo( line.ax, line.ay );
graphics.lineTo( line.bx, line.by );
}
}
class Physics
{
public function step( dt:Number, circles:Vector.<Circle>, lines:Vector.<Line> ) : void
{
for each( var circle:Circle in circles )
{
circle.update( dt );
}
resolveCollisions( circles, lines );
}
private function resolveCollisions( circles:Vector.<Circle>, lines:Vector.<Line> ) : void
{
var ioP:Point = new Point();
var ioN:Point = new Point();
for each( var circle:Circle in circles )
{
for each( var line:Line in lines )
{
if( areColliding( circle, line, ioP, ioN ) )
{
resolveCollision( circle, line, ioP.x, ioP.y, ioN.x, ioN.y );
}
}
wrap( circle );
}
}
private function wrap( circle:Circle ) : void
{
if( circle.y > 500 )
{
circle.y = -40;
circle.x = 20 + Math.random() * 400;
circle.vx = circle.vy = circle.av = 0;
}
}
private function areColliding( circle:Circle, line:Line, ioP:Point, ioN:Point ) : Boolean
{
var abx:Number = line.bx - line.ax;
var aby:Number = line.by - line.ay;
var acx:Number = circle.x - line.ax;
var acy:Number = circle.y - line.ay;
var ACDotAB:Number = acx * abx + acy * aby;
var t:Number = ACDotAB / ( abx * abx + aby * aby );
if( t < 0 ) t = 0;
else if( t > 1 ) t = 1;
var cpx:Number = line.ax + abx * t;
var cpy:Number = line.ay + aby * t;
var cnx:Number = circle.x - cpx;
var cny:Number = circle.y - cpy;
if( cnx * cnx + cny * cny <= circle.radius * circle.radius )
{
ioP.x = cpx;
ioP.y = cpy;
ioN.x = cnx;
ioN.y = cny;
return true;
}
return false;
}
private function resolveCollision( circle:Circle, line:Line, px:Number, py:Number, nx:Number, ny:Number ) : void
{
var cibx:Number = -nx;
var ciby:Number = -ny;
var length:Number = Math.sqrt( cibx * cibx + ciby * ciby );
nx /= length;
ny /= length;
var diff:Number = circle.radius - length;
circle.x += nx * diff;
circle.y += ny * diff;
var perpX:Number = -ciby;
var perpY:Number = cibx;
var perpDotNorm:Number = perpX * nx + perpY * ny;
var denom:Number = nx * ( nx / circle.mass ) + ny * ( ny / circle.mass );
denom += ( perpDotNorm * perpDotNorm ) / circle.inertiaTensor;
var rvx:Number = circle.vx + perpX * circle.av;
var rvy:Number = circle.vy + perpY * circle.av;
var rvNorm:Number = rvx * nx + rvy * ny;
var coeffRest:Number = rvNorm > -EPSILON ? 0 : ( line.coeffRest + circle.coeffRest ) * 0.5;
var impulse:Number = ( -( 1 + coeffRest ) * rvNorm ) / denom;
var dx:Number = nx * ( impulse / circle.mass );
var dy:Number = ny * ( impulse / circle.mass );
var dq:Number = ( perpX * nx * impulse + perpY * ny * impulse ) / circle.inertiaTensor;
var tangX:Number = -ny;
var tangY:Number = nx;
var cpTang:Number = perpX * tangX + perpY * tangY;
denom = tangX * ( tangX / circle.mass ) + tangY * ( tangY / circle.mass );
denom += ( cpTang * cpTang ) / circle.inertiaTensor;
var rvTang:Number = rvx * tangX + rvy * tangY;
impulse = -rvTang / denom * ( circle.coeffFric + line.coeffFric ) * 0.5;
dx += tangX * ( impulse / circle.mass );
dy += tangY * ( impulse / circle.mass );
dq += ( perpX * tangX * impulse + perpY * tangY * impulse ) / circle.inertiaTensor;
circle.vx += dx;
circle.vy += dy;
circle.av += dq;
}
}