/**
* Physics
* A requirified port of Traer Physics from Processing to JavaScript.
* Copyright (C) 2012 jonobr1
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o this.originalPositions.length) {
this.originalPositions.push(new Vector());
this.originalVelocities.push(new Vector());
this.k1Forces.push(new Vector());
this.k1Velocities.push(new Vector());
this.k2Forces.push(new Vector());
this.k2Velocities.push(new Vector());
this.k3Forces.push(new Vector());
this.k3Velocities.push(new Vector());
this.k4Forces.push(new Vector());
this.k4Velocities.push(new Vector());
}
return this;
},
step: function(dt) {
var s = this.s;
var p, x, y;
this.allocateParticles();
_.each(s.particles, function(p, i) {
if (!p.fixed) {
this.originalPositions[i].copy(p.position);
this.originalVelocities[i].copy(p.velocity);
}
p.force.clear();
}, this);
// K1
s.applyForces();
_.each(s.particles, function(p, i) {
if (!p.fixed) {
this.k1Forces[i].copy(p.force);
this.k1Velocities[i].copy(p.velocity);
}
p.force.clear();
}, this);
// K2
_.each(s.particles, function(p, i) {
if (!p.fixed) {
var op = this.originalPositions[i];
var k1v = this.k1Velocities[i];
x = op.x + k1v.x * 0.5 * dt;
y = op.y + k1v.y * 0.5 * dt;
p.position.set(x, y);
var ov = this.originalVelocities[i];
var k1f = this.k1Forces[i];
x = ov.x + k1f.x * 0.5 * dt / p.mass;
y = ov.y + k1f.y * 0.5 * dt / p.mass;
p.velocity.set(x, y);
}
}, this);
s.applyForces();
_.each(s.particles, function(p, i) {
if (!p.fixed) {
this.k2Forces[i].copy(p.force);
this.k2Velocities[i].copy(p.velocity);
}
p.force.clear();
}, this);
// K3
_.each(s.particles, function(p, i) {
if (!p.fixed) {
var op = this.originalPositions[i];
var k2v = this.k2Velocities[i];
p.position.set(op.x + k2v.x * 0.5 * dt, op.y + k2v.y * 0.5 * dt);
var ov = this.originalVelocities[i];
var k2f = this.k2Forces[i];
p.velocity.set(ov.x + k2f.x * 0.5 * dt / p.mass, ov.y + k2f.y * 0.5 * dt / p.mass);
}
}, this);
s.applyForces();
_.each(s.particles, function(p, i) {
if (!p.fixed) {
this.k3Forces[i].copy(p.force);
this.k3Velocities[i].copy(p.velocity);
}
p.force.clear();
}, this);
// K4
_.each(s.particles, function(p, i) {
if (!p.fixed) {
var op = this.originalPositions[i];
var k3v = this.k3Velocities[i];
p.position.set(op.x + k3v.x * dt, op.y + k3v.y * dt)
var ov = this.originalVelocities[i];
var k3f = this.k3Forces[i];
p.velocity.set(ov.x + k3f.x * dt / p.mass, ov.y + k3f.y * dt / p.mass);
}
}, this);
s.applyForces();
_.each(s.particles, function(p, i) {
if (!p.fixed) {
this.k4Forces[i].copy(p.force);
this.k4Velocities[i].copy(p.velocity);
}
}, this);
// TOTAL
_.each(s.particles, function(p, i) {
p.age += dt;
if (!p.fixed) {
var op = this.originalPositions[i];
var k1v = this.k1Velocities[i];
var k2v = this.k2Velocities[i];
var k3v = this.k3Velocities[i];
var k4v = this.k4Velocities[i];
var x = op.x + dt / 6.0 * (k1v.x + 2.0 * k2v.x + 2.0 * k3v.x + k4v.x);
var y = op.y + dt / 6.0 * (k1v.y + 2.0 * k2v.y + 2.0 * k3v.y + k4v.y);
p.position.set(x, y);
var ov = this.originalVelocities[i];
var k1f = this.k1Forces[i];
var k2f = this.k2Forces[i];
var k3f = this.k3Forces[i];
var k4f = this.k4Forces[i];
x = ov.x + dt / (6.0 * p.mass) * (k1f.x + 2.0 * k2f.x + 2.0 * k3f.x + k4f.x);
y = ov.y + dt / (6.0 * p.mass) * (k1f.y + 2.0 * k2f.y + 2.0 * k3f.y + k4f.y);
p.velocity.set(x, y);
}
}, this);
return this;
}
});
module.exports = Integrator;
},{"./common":8,"./vector":12}],3:[function(require,module,exports){
var _ = require('./common')
, Vector = require('./vector')
;
function Particle(mass) {
this.position = new Vector();
this.velocity = new Vector();
this.force = new Vector();
this.mass = mass;
this.fixed = false;
this.age = 0;
this.dead = false;
}
_.extend(Particle.prototype, {
/**
* Get the distance between two particles.
*/
distanceTo: function(p) {
return this.position.distanceTo(p.position);
},
/**
* Make the particle fixed in 2D space.
*/
makeFixed: function() {
this.fixed = true;
this.velocity.clear();
return this;
},
/**
* Reset a particle.
*/
reset: function() {
this.age = 0;
this.dead = false;
this.position.clear();
this.velocity.clear();
this.force.clear();
this.mass = 1.0;
return this;
},
/**
* Returns a boolean describing whether the particle is in movement.
*/
resting: function() {
return this.fixed || this.velocity.isZero() && this.force.isZero();
}
});
module.exports = Particle;
},{"./common":8,"./vector":12}],4:[function(require,module,exports){
var _ = require('./common')
, Vector = require('./Vector')
, Particle = require('./Particle')
, Spring = require('./Spring')
, Attraction = require('./Attraction')
, Integrator = require('./Integrator')
;
/**
* traer.js
* A particle-based physics engine ported from Jeff Traer's Processing
* library to JavaScript. This version is intended for use with the
* HTML5 canvas element. It is dependent on Three.js' Vector2 class,
* but can be overridden with any Vector2 class with the methods included.
*
* @author Jeffrey Traer Bernstein (original Java library)
* @author Adam Saponara (JavaScript port)
* @author Jono Brandel (requirified/optimization port)
* @author David Schoonover (Node/CommonJS/Browserify port)
*
* @version 0.3
* @date March 25, 2012
*/
/**
* The whole kit and kaboodle.
*
* @class
*/
function ParticleSystem() {
this.__equilibriumCriteria = { particles: true, springs: true, attractions: true };
this.__equilibrium = false; // are we at equilibrium?
this.__optimized = false;
this.particles = [];
this.springs = [];
this.attractions = [];
this.forces = [];
this.integrator = new Integrator(this);
this.hasDeadParticles = false;
var args = arguments.length;
if (args === 1) {
this.gravity = new Vector(0, arguments[0]);
this.drag = ParticleSystem.DEFAULT_DRAG;
} else if (args === 2) {
this.gravity = new Vector(0, arguments[0]);
this.drag = arguments[1];
} else if (args === 3) {
this.gravity = new Vector(arguments[0], arguments[1]);
this.drag = arguments[3];
} else {
this.gravity = new Vector(0, ParticleSystem.DEFAULT_GRAVITY);
this.drag = ParticleSystem.DEFAULT_DRAG;
}
}
_.extend(ParticleSystem, {
DEFAULT_GRAVITY: 0,
DEFAULT_DRAG: 0.001,
Attraction: Attraction,
Integrator: Integrator,
Particle: Particle,
Spring: Spring,
Vector: Vector
});
_.extend(ParticleSystem.prototype, {
/**
* Set whether to optimize the simulation. This enables the check of whether
* particles are moving.
*/
optimize: function(b) {
this.__optimized = !!b;
return this;
},
/**
* Set the gravity of the ParticleSystem.
*/
setGravity: function(x, y) {
this.gravity.set(x, y);
return this;
},
/**
* Sets the criteria for equilibrium
*/
setEquilibriumCriteria: function(particles, springs, attractions) {
this.__equilibriumCriteria.particles = !!particles;
this.__equilibriumCriteria.springs = !!springs;
this.__equilibriumCriteria.attractions = !!attractions;
},
/**
* Update the integrator
*/
tick: function() {
this.integrator.step(arguments.length === 0 ? 1 : arguments[0]);
if (this.__optimized) {
this.__equilibrium = !this.needsUpdate();
}
return this;
},
/**
* Checks all springs and attractions to see if the contained particles are
* inert / resting and returns a boolean.
*/
needsUpdate: function() {
var i = 0;
if(this.__equilibriumCriteria.particles) {
for (i = 0, l = this.particles.length; i < l; i++) {
if (!this.particles[i].resting()) {
return true;
}
}
}
if(this.__equilibriumCriteria.springs) {
for (i = 0, l = this.springs.length; i < l; i++) {
if (!this.springs[i].resting()) {
return true;
}
}
}
if(this.__equilibriumCriteria.attractions) {
for (i = 0, l = this.attractions.length; i < l; i++) {
if (!this.attractions[i].resting()) {
return true;
}
}
}
return false;
},
/**
* Add a particle to the ParticleSystem.
*/
addParticle: function(p) {
this.particles.push(p);
return this;
},
/**
* Add a spring to the ParticleSystem.
*/
addSpring: function(s) {
this.springs.push(s);
return this;
},
/**
* Add an attraction to the ParticleSystem.
*/
addAttraction: function(a) {
this.attractions.push(a);
return this;
},
/**
* Makes and then adds Particle to ParticleSystem.
*/
makeParticle: function(m, x, y) {
var mass = _.isNumber(m) ? m : 1.0;
var x = x || 0;
var y = y || 0;
var p = new Particle(mass);
p.position.set(x, y);
this.addParticle(p);
return p;
},
/**
* Makes and then adds Spring to ParticleSystem.
*/
makeSpring: function(a, b, k, d, l) {
var s = new Spring(a, b, k, d, l);
this.addSpring(s);
return s;
},
/**
* Makes and then adds Attraction to ParticleSystem.
*/
makeAttraction: function(a, b, k, d) {
var a = new Attraction(a, b, k, d);
this.addAttraction(a);
return a;
},
/**
* Wipe the ParticleSystem clean.
*/
clear: function() {
this.particles.length = 0;
this.springs.length = 0;
this.attractions.length = 0;
},
/**
* Calculate and apply forces.
*/
applyForces: function() {
if (!this.gravity.isZero()) {
_.each(this.particles, function(p) {
p.force.addSelf(this.gravity);
}, this);
}
var t = new Vector();
_.each(this.particles, function(p) {
t.set(p.velocity.x * -1 * this.drag, p.velocity.y * -1 * this.drag);
p.force.addSelf(t);
}, this);
_.each(this.springs, function(s) {
s.update();
});
_.each(this.attractions, function(a) {
a.update();
});
_.each(this.forces, function(f) {
f.update();
});
return this;
},
/**
* Clear all particles in the system.
*/
clearForces: function() {
_.each(this.particles, function(p) {
p.clear();
});
return this;
}
});
module.exports = ParticleSystem;
},{"./Attraction":1,"./Integrator":2,"./Particle":3,"./Spring":6,"./Vector":7,"./common":8}],5:[function(require,module,exports){
var _ = require('./common')
, raf = require('./requestAnimationFrame')
, ParticleSystem = require('./ParticleSystem')
;
var updates = [];
/**
* Extended singleton instance of ParticleSystem with convenience methods for
* Request Animation Frame.
* @class
*/
function Physics() {
var _this = this;
this.playing = false;
ParticleSystem.apply(this, arguments);
this.animations = [];
this.equilibriumCallbacks = [];
update.call(this);
}
_.extend(Physics, ParticleSystem, {
superclass: ParticleSystem
});
_.extend(Physics.prototype, ParticleSystem.prototype, {
/**
* Play the animation loop. Doesn't affect whether in equilibrium or not.
*/
play: function() {
if (this.playing) {
return this;
}
this.playing = true;
this.__equilibrium = false;
update.call(this);
return this;
},
/**
* Pause the animation loop. Doesn't affect whether in equilibrium or not.
*/
pause: function() {
this.playing = false;
return this;
},
/**
* Toggle between playing and pausing the simulation.
*/
toggle: function() {
if (this.playing) {
this.pause();
} else {
this.play();
}
return this;
},
onUpdate: function(func) {
if (_.indexOf(this.animations, func) >= 0 || !_.isFunction(func)) {
return this;
}
this.animations.push(func);
return this;
},
onEquilibrium: function(func) {
if (_.indexOf(this.equilibriumCallbacks, func) >= 0 || !_.isFunction(func)) {
return this;
}
this.equilibriumCallbacks.push(func);
return this;
},
/**
* Call update after values in the system have changed and this will fire
* it's own Request Animation Frame to update until things have settled
* to equilibrium — at which point the system will stop updating.
*/
update: function() {
if (!this.__equilibrium) {
return this;
}
this.__equilibrium = false;
if (this.playing) {
update.call(this);
}
return this;
}
});
function update() {
var _this = this;
this.tick();
_.each(this.animations, function(a) {
a();
});
if ((this.__optimized && !this.__equilibrium || !this.__optimized) && this.playing) {
raf(function() {
update.call(_this);
});
}
if (this.__optimized && this.__equilibrium){
_.each(this.equilibriumCallbacks, function(a) {
a();
});
}
}
module.exports = Physics;
},{"./ParticleSystem":4,"./common":8,"./requestAnimationFrame":11}],6:[function(require,module,exports){
var _ = require('./common')
, Vector = require('./vector')
;
function Spring(a, b, k, d, l) {
this.constant = k;
this.damping = d;
this.length = l;
this.a = a;
this.b = b;
this.on = true;
}
_.extend(Spring.prototype, {
/**
* Returns the distance between particle a and particle b
* in 2D space.
*/
currentLength: function() {
return this.a.position.distanceTo(this.b.position);
},
/**
* Update spring logic.
*/
update: function() {
var a = this.a;
var b = this.b;
if (!(this.on && (!a.fixed || !b.fixed))) return this;
var a2b = new Vector().sub(a.position, b.position);
var d = a2b.length();
if (d === 0) {
a2b.clear();
} else {
a2b.divideScalar(d); // Essentially normalize
}
var fspring = -1 * (d - this.length) * this.constant;
var va2b = new Vector().sub(a.velocity, b.velocity);
var fdamping = -1 * this.damping * va2b.dot(a2b);
var fr = fspring + fdamping;
a2b.multiplyScalar(fr);
if (!a.fixed) {
a.force.addSelf(a2b);
}
if (!b.fixed) {
b.force.subSelf(a2b);
}
return this;
},
/**
* Returns a boolean describing whether the spring is resting or not.
* Convenient for knowing whether or not the spring needs another update
* tick.
*/
resting: function() {
var a = this.a;
var b = this.b;
var l = this.length;
return !this.on || (a.fixed && b.fixed)
|| (a.fixed && (l === 0 ? b.position.equals(a.position) : b.position.distanceTo(a.position) <= l) && b.resting())
|| (b.fixed && (l === 0 ? a.position.equals(b.position) : a.position.distanceTo(b.position) <= l) && a.resting());
}
});
module.exports = Spring;
},{"./common":8,"./vector":12}],7:[function(require,module,exports){
/**
* @author mr.doob / http://mrdoob.com/
* @author philogb / http://blog.thejit.org/
* @author egraether / http://egraether.com/
* @author zz85 / http://www.lab4games.net/zz85/blog
* @author jonobr1 / http://jonobr1.com/
*/
var _ = require('./common');
/**
* A two dimensional vector.
*/
function Vector(x, y) {
this.x = x || 0;
this.y = y || 0;
}
_.extend(Vector.prototype, {
set: function(x, y) {
this.x = x;
this.y = y;
return this;
},
copy: function(v) {
this.x = v.x;
this.y = v.y;
return this;
},
clear: function() {
this.x = 0;
this.y = 0;
return this;
},
clone: function() {
return new Vector(this.x, this.y);
},
add: function(v1, v2) {
this.x = v1.x + v2.x;
this.y = v1.y + v2.y;
return this;
},
addSelf: function(v) {
this.x += v.x;
this.y += v.y;
return this;
},
sub: function(v1, v2) {
this.x = v1.x - v2.x;
this.y = v1.y - v2.y;
return this;
},
subSelf: function(v) {
this.x -= v.x;
this.y -= v.y;
return this;
},
multiplySelf: function(v) {
this.x *= v.x;
this.y *= v.y;
return this;
},
multiplyScalar: function(s) {
this.x *= s;
this.y *= s;
return this;
},
multiplyScalarXY: function(sx,sy) {
this.x *= sx;
this.y *= sy;
return this;
},
divideScalar: function(s) {
if (s) {
this.x /= s;
this.y /= s;
} else {
this.set(0, 0);
}
return this;
},
negate: function() {
return this.multiplyScalar(-1);
},
dot: function(v) {
return this.x * v.x + this.y * v.y;
},
lengthSquared: function() {
return this.x * this.x + this.y * this.y;
},
length: function() {
return Math.sqrt(this.lengthSquared());
},
normalize: function() {
return this.divideScalar(this.length());
},
distanceTo: function(v) {
return Math.sqrt(this.distanceToSquared(v));
},
distanceToSquared: function(v) {
var dx = this.x - v.x, dy = this.y - v.y;
return dx * dx + dy * dy;
},
setLength: function(l) {
return this.normalize().multiplyScalar(l);
},
equals: function(v) {
return (this.distanceTo(v) < 0.0001 /* almost same position */);
},
lerp: function(v, t) {
var x = (v.x - this.x) * t + this.x;
var y = (v.y - this.y) * t + this.y;
return this.set(x, y);
},
isZero: function() {
return (this.length() < 0.0001 /* almost zero */ );
}
});
module.exports = Vector;
},{"./common":8}],8:[function(require,module,exports){
/**
* Pulled only what's needed from:
*
* Underscore.js 1.3.3
* (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
* http://documentcloud.github.com/underscore
*/
var breaker = {};
var ArrayProto = Array.prototype;
var ObjProto = Object.prototype;
var hasOwnProperty = ObjProto.hasOwnProperty;
var slice = ArrayProto.slice;
var nativeForEach = ArrayProto.forEach;
var nativeIndexOf = ArrayProto.indexOf;
var toString = ObjProto.toString;
var has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
var each = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (_.has(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
};
var identity = function(value) {
return value;
};
var sortedIndex = function(array, obj, iterator) {
iterator || (iterator = identity);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
return low;
};
module.exports = {
has: has,
each: each,
extend: function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
obj[prop] = source[prop];
}
});
return obj;
},
indexOf: function(array, item, isSorted) {
if (array == null) return -1;
var i, l;
if (isSorted) {
i = sortedIndex(array, item);
return array[i] === item ? i : -1;
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
return -1;
},
sortedIndex: sortedIndex,
identity: identity,
isNumber: function(obj) {
return toString.call(obj) == '[object Number]';
},
isFunction: function(obj) {
return toString.call(obj) == '[object Function]' || typeof obj == 'function';
},
isUndefined: function(obj) {
return obj === void 0;
},
isNull: function(obj) {
return obj === null;
}
};
},{}],"H99CHA":[function(require,module,exports){
var root = (function(){ return this; })()
, previousShortcut = root.Physics
;
module.exports = root.Physics = require('./Physics');
},{"./Physics":5}],"physics":[function(require,module,exports){
module.exports=require('H99CHA');
},{}],11:[function(require,module,exports){
/*
* Requirified version of Paul Irish's request animation frame.
* http://paulirish.com/2011/requestanimationframe-for-smart-animating/
*/
module.exports =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
},{}],12:[function(require,module,exports){
module.exports=require(7)
},{"./common":8}]},{},["H99CHA"])