📄 simulation.as
字号:
package flare.physics
{
import flash.geom.Rectangle;
/**
* A physical simulation involving particles, springs, and forces.
* Useful for simulating a range of physical effects or layouts.
*/
public class Simulation
{
private var _particles:Array = new Array();
private var _springs:Array = new Array();
private var _forces:Array = new Array();
private var _bounds:Rectangle = null;
/** The default gravity force for this simulation. */
public function get gravityForce():GravityForce {
return _forces[0] as GravityForce;
}
/** The default n-body force for this simulation. */
public function get nbodyForce():NBodyForce {
return _forces[1] as NBodyForce;
}
/** The default drag force for this simulation. */
public function get dragForce():DragForce {
return _forces[2] as DragForce;
}
/** The default spring force for this simulation. */
public function get springForce():SpringForce {
return _forces[3] as SpringForce;
}
/** Sets a bounding box for particles in this simulation.
* Null (the default) indicates no boundaries. */
public function get bounds():Rectangle { return _bounds; }
public function set bounds(b:Rectangle):void {
if (_bounds == b) return;
if (b == null) { _bounds = null; return; }
if (_bounds == null) { _bounds = new Rectangle(); }
// ensure x is left-most and y is top-most
_bounds.x = b.x + (b.width < 0 ? b.width : 0);
_bounds.width = (b.width < 0 ? -1 : 1) * b.width;
_bounds.y = b.y + (b.width < 0 ? b.height : 0);
_bounds.height = (b.height < 0 ? -1 : 1) * b.height;
}
/**
* Creates a new physics simulation.
* @param gx the gravitational acceleration along the x dimension
* @param gy the gravitational acceleration along the y dimension
* @param drag the default drag (viscosity) co-efficient
* @param attraction the gravitational attraction (or repulsion, for
* negative values) between particles.
*/
public function Simulation(gx:Number=0, gy:Number=0,
drag:Number=0.1, attraction:Number=-5)
{
_forces.push(new GravityForce(gx, gy));
_forces.push(new NBodyForce(attraction));
_forces.push(new DragForce(drag));
_forces.push(new SpringForce());
}
// -- Init Simulation -------------------------------------------------
/**
* Adds a custom force to the force simulation.
* @param force the force to add
*/
public function addForce(force:IForce):void
{
_forces.push(force);
}
/**
* Returns the force at the given index.
* @param idx the index of the force to look up
* @return the force at the specified index
*/
public function getForceAt(idx:int):IForce
{
return _forces[idx];
}
/**
* Adds a new particle to the simulation.
* @param mass the mass (charge) of the particle
* @param x the particle's starting x position
* @param y the particle's starting y position
* @return the added particle
*/
public function addParticle(mass:Number, x:Number, y:Number):Particle
{
var p:Particle = getParticle(mass, x, y);
_particles.push(p);
return p;
}
/**
* Removes a particle from the simulation. Any springs attached to
* the particle will also be removed.
* @param idx the index of the particle in the particle list
* @return true if removed, false otherwise.
*/
public function removeParticle(idx:uint):Boolean
{
var p:Particle = _particles[idx];
if (p == null) return false;
// remove springs
for (var i:uint = _springs.length; --i >= 0; ) {
var s:Spring = _springs[i];
if (s.p1 == p || s.p2 == p)
removeSpring(i);
}
// remove from particles
reclaimParticle(p);
_particles.splice(idx, 1);
return true;
}
/**
* Adds a spring to the simulation
* @param p1 the first particle attached to the spring
* @param p2 the second particle attached to the spring
* @param restLength the rest length of the spring
* @param tension the tension of the spring
* @param damping the damping (friction) co-efficient of the spring
* @return the added spring
*/
public function addSpring(p1:Particle, p2:Particle, restLength:Number,
tension:Number, damping:Number):Spring
{
var s:Spring = getSpring(p1, p2, restLength, tension, damping);
p1.degree++;
p2.degree++;
_springs.push(s);
return s;
}
/**
* Removes a spring from the simulation.
* @param idx the index of the spring in the spring list
* @return true if removed, false otherwise
*/
public function removeSpring(idx:uint):Boolean
{
if (idx >= _springs.length) return false;
var s:Spring = _springs[idx];
s.p1.degree--;
s.p2.degree--;
reclaimSpring(s);
_springs.splice(idx, 1);
return true;
}
/**
* Returns the particle list. This is the same array instance backing
* the simulation, so edit the array with caution.
* @return the particle list
*/
public function get particles():Array {
return _particles;
}
/**
* Returns the spring list. This is the same array instance backing
* the simulation, so edit the array with caution.
* @return the spring list
*/
public function get springs():Array {
return _springs;
}
// -- Run Simulation --------------------------------------------------
/**
* Advance the simulation for the specified time interval.
* @param dt the time interval to step the simulation (default 1)
*/
public function tick(dt:Number=1):void
{
var p:Particle, s:Spring, i:uint, ax:Number, ay:Number;
var dt1:Number = dt/2, dt2:Number = dt*dt/2;
// remove springs connected to dead particles
for (i=_springs.length; --i>=0;) {
s = _springs[i];
if (s.die || s.p1.die || s.p2.die) {
s.p1.degree--;
s.p2.degree--;
reclaimSpring(s);
_springs.splice(i, 1);
}
}
// update particles using Verlet integration
for (i=_particles.length; --i>=0;) {
p = _particles[i];
p.age += dt;
if (p.die) { // remove dead particles
reclaimParticle(p);
_particles.splice(i, 1);
} else if (p.fixed) {
p.vx = p.vy = 0;
} else {
ax = p.fx / p.mass; ay = p.fy / p.mass;
p.x += p.vx*dt + ax*dt2;
p.y += p.vy*dt + ay*dt2;
p._vx = p.vx + ax*dt1;
p._vy = p.vy + ay*dt1;
}
}
// evaluate the forces
eval();
// update particle velocities
for (i=_particles.length; --i>=0;) {
p = _particles[i];
if (!p.fixed) {
ax = dt1 / p.mass;
p.vx = p._vx + p.fx * ax;
p.vy = p._vy + p.fy * ax;
}
}
// enfore bounds
if (_bounds) enforceBounds();
}
private function enforceBounds():void {
var minX:Number = _bounds.x;
var maxX:Number = _bounds.x + _bounds.width;
var minY:Number = _bounds.y;
var maxY:Number = _bounds.y + _bounds.height;
for each (var p:Particle in _particles) {
if (p.x < minX) {
p.x = minX; p.vx = 0;
} else if (p.x > maxX) {
p.x = maxX; p.vx = 0;
}
if (p.y < minY) {
p.y = minY; p.vy = 0;
}
else if (p.y > maxY) {
p.y = maxY; p.vy = 0;
}
}
}
/**
* Evaluates the set of forces in the simulation.
*/
public function eval():void {
var i:uint, p:Particle;
// reset forces
for (i=_particles.length; --i >= 0; ) {
p = _particles[i];
p.fx = p.fy = 0;
}
// collect forces
for (i=0; i<_forces.length; ++i) {
IForce(_forces[i]).apply(this);
}
}
// -- Particle Pool ---------------------------------------------------
/** The maximum number of items stored in a simulation object pool. */
public static var objectPoolLimit:int = 5000;
protected static var _ppool:Array = new Array();
protected static var _spool:Array = new Array();
/**
* Returns a particle instance, pulling a recycled particle from the
* object pool if available.
* @param mass the mass (charge) of the particle
* @param x the particle's starting x position
* @param y the particle's starting y position
* @return a particle instance
*/
protected static function getParticle(mass:Number, x:Number, y:Number):Particle
{
if (_ppool.length > 0) {
var p:Particle = _ppool.pop();
p.init(mass, x, y);
return p;
} else {
return new Particle(mass, x, y);
}
}
/**
* Returns a spring instance, pulling a recycled spring from the
* object pool if available.
* @param p1 the first particle attached to the spring
* @param p2 the second particle attached to the spring
* @param restLength the rest length of the spring
* @param tension the tension of the spring
* @param damping the damping (friction) co-efficient of the spring
* @return a spring instance
*/
protected static function getSpring(p1:Particle, p2:Particle,
restLength:Number, tension:Number, damping:Number):Spring
{
if (_spool.length > 0) {
var s:Spring = _spool.pop();
s.init(p1, p2, restLength, tension, damping);
return s;
} else {
return new Spring(p1, p2, restLength, tension, damping);
}
}
/**
* Reclaims a particle, adding it to the object pool for recycling
* @param p the particle to reclaim
*/
protected static function reclaimParticle(p:Particle):void
{
if (_ppool.length < objectPoolLimit) {
_ppool.push(p);
}
}
/**
* Reclaims a spring, adding it to the object pool for recycling
* @param s the spring to reclaim
*/
protected static function reclaimSpring(s:Spring):void
{
if (_spool.length < objectPoolLimit) {
_spool.push(s);
}
}
} // end of class Simulation
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -