APE: Collision Within an Elliptical Spring Constraint
Recently I was using ActionScript Physics Engine to create a simple 2-D arcade-style game. I looked into building an elliptical spring constraint in APE to serve as the boundaries of the game. In the end I decided not to use the elliptical boundaries, but they are easy enough to code up. Here’s a quick demo, and the ActionScript source code:
package
{
import flash.display.Sprite;
import flash.events.Event;
import org.cove.ape.*;
public class Main extends Sprite
{
public var defaultGroup:Group;
var car:SpringConstraint;
var ellipsePoints:Array; // points that define the outer ellipse
var wheel1:WheelParticle;
public function Main()
{
stage.frameRate = 30;
APEngine.init(1/4);
// set the default diplay container
APEngine.container = this;
defaultGroup = new Group();
defaultGroup.collideInternal = true;
init();
APEngine.addGroup(defaultGroup);
stage.addEventListener(Event.ENTER_FRAME, run);
}
public function init():void
{
ellipsePoints = new Array();
ellipsePoints = drawEllipse(calculateEllipse(380, 80, 60, 36, -24, 32));
addBall();
}
public function addBall():void
{
var circle = new CircleParticle(
160, // x
210, // y
40, // radius
false, // fixed
10, // mass
1.0, // elasticty
0 // friction
);
circle.setStyle(0, 0, 0, Math.random() * 0xffffff);
defaultGroup.addParticle(circle);
circle.velocity = (new Vector(20, 1));
}
/*
* This functions returns an array containing the points to draw an
* ellipse.
*
* @param x {double} X coordinate
* @param y {double} Y coordinate
* @param a {double} Semimajor axis
* @param b {double} Semiminor axis
* @param angle {double} Angle of the ellipse
*/
private function calculateEllipse(x, y, a, b, angle, steps)
{
if (steps == null)
steps = 36;
var points:Array = new Array();
// Angle is given by Degree Value
var beta = -angle * (Math.PI / 180); //(Math.PI/180) converts Degree Value into Radians
var sinbeta = Math.sin(beta);
var cosbeta = Math.cos(beta);
for (var i = 0; i < 360; i += 360 / steps)
{
var alpha = i * (Math.PI / 180) ;
var sinalpha = Math.sin(alpha);
var cosalpha = Math.cos(alpha);
var x = x + (a * cosalpha * cosbeta - b * sinalpha * sinbeta);
var y = y + (a * cosalpha * sinbeta + b * sinalpha * cosbeta);
var o:Object = new Object();
o.x = x;
o.y = y;
points.push(o);
}
return points;
}
private function drawEllipse(points:Array):Array
{
var circles:Array = new Array();
var i:Number;
for( i= 0; i < points.length; i++)
{
// CircleParticle(x, y, radius, fixed, mass, elasticity, friction)
var c:CircleParticle = new CircleParticle(points[i].x, points[i].y, 0, true, 0.4, 0, 0);
defaultGroup.addParticle(c);
circles.push(c);
if(i > 0)
{
var s:SpringConstraint = new SpringConstraint(circles[i-1], circles[i], 1, true, 1, 1, true);
defaultGroup.addConstraint(s);
}
}
// create the final constraint to close the ellipse:
var t:SpringConstraint = new SpringConstraint(circles[i-1], circles[0], 1, true, 1, 1, true);
defaultGroup.addConstraint(t);
return circles;
}
private function run(e:Event):void
{
APEngine.step();
APEngine.paint();
}
}
}
Anatomy of an Ellipse:

Drawing with ActionScript Physics Engine
I’ve created a quick demo with APE that allows the user to draw rectangle particles with the mouse. New wheel particles are created every 2 seconds. Particles are removed if they fall outside the stage area so that the demo will not leak memory.
Main.as:
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
import org.cove.ape.*;
// Draw APE Rectangle Particles
// built with Actionscript Physics Engine v0.45
// http://www.cove.org/ape/
public class Main extends Sprite
{
private var myRectangle:RectangleParticle
private var wheel:WheelParticle;
private var defaultGroup:Group;
private var beginX:Number; // mouse drag top left x position
private var beginY:Number; // mouse drag top left y position
private var endX:Number; // mouse drag bottom right x position
private var endY:Number; // mouse drag bottom right y position
private const bound:Number = 600;
public function Main()
{
init();
}
private function init():void
{
stage.frameRate = 65;
// Initialize the engine. The argument here is the time step value.
// Higher values scale the forces in the sim, making it appear to run
// faster or slower. Lower values result in more accurate simulations.
APEngine.init(1/4);
APEngine.damping = 0.98;
// set the default diplay container
APEngine.container = this;
APEngine.addMasslessForce(new Vector(0,8));
defaultGroup = new Group();
defaultGroup.collideInternal = true;
//RectangleParticle(x, y, width, height, rotation, fixed, mass, elasticity, friction)
var floor:RectangleParticle = new RectangleParticle(300,490, 600, 20, 0.0, true, 10, 0.4, 0.55);
floor.setStyle(0, 0, 0, Math.random() * 0x111111);
defaultGroup.addParticle(floor);
APEngine.addGroup(defaultGroup);
addEventListener(Event.ENTER_FRAME, run);
stage.addEventListener(MouseEvent.MOUSE_DOWN, beginDrawRect);
stage.addEventListener(MouseEvent.MOUSE_UP, endDrawRect);
setTimer(); // start creating wheel particles
}
private function beginDrawRect(m:MouseEvent):void
{
beginX = mouseX;
beginY = mouseY;
}
private function endDrawRect(m:MouseEvent):void
{
endX = mouseX;
endY = mouseY;
// if we have 2 positive numbers, draw:
if (endX - beginX > 1 && endY - beginY > 1)
{
drawRectangle();
}
}
private function drawRectangle():void
{
//RectangleParticle(x, y, width, height, rotation, fixed, mass, elasticity, friction)
// the more area a rectangle has the more mass we give it:
myRectangle = new RectangleParticle(beginX + ( (endX - beginX)/2 ), beginY + ((endY - beginY)/2), endX - beginX, endY - beginY, 0 , false, (endX - beginX) * (endY - beginY) , 0.4, 0.1);
myRectangle.setStyle(0, 0, 0, Math.random() * 0xffffff);
defaultGroup.addParticle(myRectangle);
}
private function createParticle(e:TimerEvent):void
{
//WheelParticle(x, y, radius, fixed, mass, elasticity, friction, traction)
var rand:Number = Math.random();
// the more area a wheel has the more mass we give it:
wheel = new WheelParticle(rand * stage.stageWidth, -20, rand * 55, false, rand * 55, 0.4, 0.1);
wheel.setStyle(0, 0, 0, Math.round(Math.random() * 0xffffff));
defaultGroup.addParticle(wheel);
checkForBounds();
}
// Check location of particles & remove if out of bounds so that we don't leak memory:
function checkForBounds()
{
/* particles:Array [read-only] The Array of all AbstractParticle
instances added to the AbstractCollection */
for (var i:Number = defaultGroup.particles.length -1; i > 0 ; --i){
/* Check to see if particles is past the 'bound' number, if is then remove it from system
py & px give the X & Y location of the partical, view AbstractParticle in APE api */
if ( defaultGroup.particles[i].py > bound )
{
defaultGroup.removeParticle( defaultGroup.particles[i] ); // Removes an AbstractParticle from the AbstractCollection.
}
}
}
private function setTimer():void
{
var myTimer:Timer= new Timer(2000, 0);
myTimer.addEventListener("timer", createParticle);
myTimer.start();
}
private function run(e:Event):void
{
APEngine.step();
APEngine.paint();
}
}
}
Web Design: Zip Project
I don’t do a lot of web design these days. I spend most of my time coding. But I was recently at a client site finishing up an ActionScript 3 project. They were in a pinch and I was asked to whip up some quick screen designs for a web-based business process automation solution my client was submitting a proposal for. These were created very quickly in Adobe Fireworks. I’ve removed information here that might be that my client might consider sensitive, changed the name of the project etc. etc.
The final images were dropped into web pages and linked together with image maps. Not exactly an elegant solution, but more than adequate for a proposal. See the full-sized click through.

