diff --git a/src/dev.js b/src/dev.js
index 65a0bc8..0316640 100644
--- a/src/dev.js
+++ b/src/dev.js
@@ -13,6 +13,7 @@
//
//
//
+//
//
//
//
diff --git a/src/kernel.js b/src/kernel.js
index 2ad7149..ffaa682 100644
--- a/src/kernel.js
+++ b/src/kernel.js
@@ -9,7 +9,7 @@
var chrome_local_file = window.location.protocol == "file:" &&
navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
var USE_WORKER = (window.Worker !== undefined && !chrome_local_file)
-
+
var _physics = null
var _tween = null
var _fpsWindow = [] // for keeping track of the actual frame rate
@@ -36,7 +36,7 @@
var params = pSystem.parameters()
if(USE_WORKER){
- trace('using web workers')
+ trace('arbor.js/web-workers',params)
_screenInterval = setInterval(that.screenUpdate, params.timeout)
_physics = new Worker(arbor_path()+'physics/worker.js')
@@ -46,8 +46,8 @@
physics:objmerge(params,
{timeout:Math.ceil(params.timeout)}) })
}else{
- trace("couldn't use web workers, be careful...")
- _physics = Physics(params.dt, params.stiffness, params.repulsion, params.friction, that.system._updateGeometry)
+ trace('arbor.js/single-threaded',params)
+ _physics = Physics(params.dt, params.stiffness, params.repulsion, params.friction, that.system._updateGeometry, params.integrator)
that.start()
}
diff --git a/src/physics/physics.js b/src/physics/physics.js
index 38ee34d..6536f65 100644
--- a/src/physics/physics.js
+++ b/src/physics/physics.js
@@ -4,7 +4,7 @@
// the particle system itself. either run inline or in a worker (see worker.js)
//
- var Physics = function(dt, stiffness, repulsion, friction, updateFn){
+ var Physics = function(dt, stiffness, repulsion, friction, updateFn, integrator){
var bhTree = BarnesHutTree() // for computing particle repulsion
var active = {particles:{}, springs:{}}
var free = {particles:{}}
@@ -17,6 +17,7 @@
var SPEED_LIMIT = 1000 // the max particle velocity per tick
var that = {
+ integrator:['verlet','euler'].indexOf(integrator)>=0 ? integrator : 'verlet',
stiffness:(stiffness!==undefined) ? stiffness : 1000,
repulsion:(repulsion!==undefined)? repulsion : 600,
friction:(friction!==undefined)? friction : .3,
@@ -29,14 +30,14 @@
},
modifyPhysics:function(param){
- $.each(['stiffness','repulsion','friction','gravity','dt','precision'], function(i, p){
+ $.each(['stiffness','repulsion','friction','gravity','dt','precision', 'integrator'], function(i, p){
if (param[p]!==undefined){
if (p=='precision'){
that.theta = 1-param[p]
return
}
that[p] = param[p]
-
+
if (p=='stiffness'){
var stiff=param[p]
$.each(active.springs, function(id, spring){
@@ -129,10 +130,20 @@
return _epoch
},
-
tick:function(){
that.tendParticles()
- that.eulerIntegrator(that.dt)
+ if (that.integrator=='euler'){
+ that.updateForces()
+ that.updateVelocity(that.dt)
+ that.updatePosition(that.dt)
+ }else{
+ // default to verlet
+ that.updateForces();
+ that.cacheForces(); // snapshot f(t)
+ that.updatePosition(that.dt); // update position to x(t + 1)
+ that.updateForces(); // calculate f(t+1)
+ that.updateVelocity(that.dt); // update using f(t) and f(t+1)
+ }
that.tock()
},
@@ -168,8 +179,8 @@
},
- // Physics stuff
- eulerIntegrator:function(dt){
+ // Physics stuff
+ updateForces:function() {
if (that.repulsion>0){
if (that.theta>0) that.applyBarnesHutRepulsion()
else that.applyBruteForceRepulsion()
@@ -177,10 +188,15 @@
if (that.stiffness>0) that.applySprings()
that.applyCenterDrift()
if (that.gravity) that.applyCenterGravity()
- that.updateVelocity(dt)
- that.updatePosition(dt)
},
-
+
+ cacheForces:function() {
+ // keep a snapshot of the current forces for the verlet integrator
+ $.each(active.particles, function(id, point) {
+ point._F = point.f;
+ });
+ },
+
applyBruteForceRepulsion:function(){
$.each(active.particles, function(id1, point1){
$.each(active.particles, function(id2, point2){
@@ -266,6 +282,7 @@
updateVelocity:function(timestep){
// translate forces to a new velocity for this particle
+ var sum=0, max=0, n = 0;
$.each(active.particles, function(id, point) {
if (point.fixed){
point.v = new Point(0,0)
@@ -273,32 +290,43 @@
return
}
- var was = point.v.magnitude()
- point.v = point.v.add(point.f.multiply(timestep)).multiply(1-that.friction);
+ if (that.integrator=='euler'){
+ point.v = point.v.add(point.f.multiply(timestep)).multiply(1-that.friction);
+ }else{
+ point.v = point.v.add(point.f.add(point._F).multiply(timestep*0.5)).multiply(1-that.friction);
+ }
point.f.x = point.f.y = 0
var speed = point.v.magnitude()
if (speed>SPEED_LIMIT) point.v = point.v.divide(speed*speed)
+
+ var speed = point.v.magnitude();
+ var e = speed*speed
+ sum += e
+ max = Math.max(e,max)
+ n++
});
+ _energy = {sum:sum, max:max, mean:sum/n, n:n}
+
},
updatePosition:function(timestep){
// translate velocity to a position delta
- var sum=0, max=0, n = 0;
var bottomright = null
- var topleft = null
-
+ var topleft = null
+
$.each(active.particles, function(i, point) {
+
// move the node to its new position
- point.p = point.p.add(point.v.multiply(timestep));
+ if (that.integrator=='euler'){
+ point.p = point.p.add(point.v.multiply(timestep));
+ }else{
+ //this should follow the equation
+ //x(t+1) = x(t) + v(t) * timestep + 1/2 * timestep^2 * a(t)
+ var accelPart = point.f.multiply(0.5 * timestep * timestep);
+ point.p = point.p.add(point.v.multiply(timestep)).add(accelPart);
+ }
- // keep stats to report in systemEnergy
- var speed = point.v.magnitude();
- var e = speed*speed
- sum += e
- max = Math.max(e,max)
- n++
-
if (!bottomright){
bottomright = new Point(point.p.x, point.p.y)
topleft = new Point(point.p.x, point.p.y)
@@ -309,11 +337,10 @@
if (pt.x===null || pt.y===null) return
if (pt.x > bottomright.x) bottomright.x = pt.x;
if (pt.y > bottomright.y) bottomright.y = pt.y;
- if (pt.x < topleft.x) topleft.x = pt.x;
- if (pt.y < topleft.y) topleft.y = pt.y;
+ if (pt.x < topleft.x) topleft.x = pt.x;
+ if (pt.y < topleft.y) topleft.y = pt.y;
});
- _energy = {sum:sum, max:max, mean:sum/n, n:n}
_bounds = {topleft:topleft||new Point(-1,-1), bottomright:bottomright||new Point(1,1)}
},
@@ -333,4 +360,4 @@
var y = center_pt.y
var d = r*2
return new Point(x-r+Math.random()*d, y-r+Math.random()*d)
- }
+ }
\ No newline at end of file
diff --git a/src/physics/system.js b/src/physics/system.js
index b1b722f..3ce52ee 100644
--- a/src/physics/system.js
+++ b/src/physics/system.js
@@ -4,8 +4,8 @@
// the main controller object for creating/modifying graphs
//
- var ParticleSystem = function(repulsion, stiffness, friction, centerGravity, targetFps, dt, precision){
- // also callable with ({stiffness:, repulsion:, friction:, timestep:, fps:, dt:, gravity:})
+ var ParticleSystem = function(repulsion, stiffness, friction, centerGravity, targetFps, dt, precision, integrator){
+ // also callable with ({integrator:, stiffness:, repulsion:, friction:, timestep:, fps:, dt:, gravity:})
var _changes=[]
var _notification=null
@@ -17,8 +17,8 @@
var _bounds = null
var _boundsTarget = null
- if (typeof stiffness=='object'){
- var _p = stiffness
+ if (typeof repulsion=='object'){
+ var _p = repulsion
friction = _p.friction
repulsion = _p.repulsion
targetFps = _p.fps
@@ -26,8 +26,11 @@
stiffness = _p.stiffness
centerGravity = _p.gravity
precision = _p.precision
+ integrator = _p.integrator
}
+ // param validation and defaults
+ if (integrator!='verlet' && integrator!='euler') integrator='verlet'
friction = isNaN(friction) ? .5 : friction
repulsion = isNaN(repulsion) ? 1000 : repulsion
targetFps = isNaN(targetFps) ? 55 : targetFps
@@ -35,8 +38,9 @@
dt = isNaN(dt) ? 0.02 : dt
precision = isNaN(precision) ? .6 : precision
centerGravity = (centerGravity===true)
+
var _systemTimeout = (targetFps!==undefined) ? 1000/targetFps : 1000/50
- var _parameters = {repulsion:repulsion, stiffness:stiffness, friction:friction, dt:dt, gravity:centerGravity, precision:precision, timeout:_systemTimeout}
+ var _parameters = {integrator:integrator, repulsion:repulsion, stiffness:stiffness, friction:friction, dt:dt, gravity:centerGravity, precision:precision, timeout:_systemTimeout}
var _energy
var state = {
@@ -68,10 +72,10 @@
else that.parameters({timeout:1000/(newFPS||50)})
},
-
start:function(){
state.kernel.start()
},
+
stop:function(){
state.kernel.stop()
},