1 /** 2 * Constructs a new empty simulation. 3 * 4 * @param {array} particles 5 * @returns {pv.Simulation} a new simulation for the specified particles. 6 * @see pv.Simulation 7 */ 8 pv.simulation = function(particles) { 9 return new pv.Simulation(particles); 10 }; 11 12 /** 13 * Constructs a new simulation for the specified particles. 14 * 15 * @class Represents a particle simulation. Particles are massive points in 16 * two-dimensional space. Forces can be applied to these particles, causing them 17 * to move. Constraints can also be applied to restrict particle movement, for 18 * example, constraining particles to a fixed position, or simulating collision 19 * between circular particles with area. 20 * 21 * <p>The simulation uses <a 22 * href="http://en.wikipedia.org/wiki/Verlet_integration">Position Verlet</a> 23 * integration, due to the ease with which <a 24 * href="http://www.teknikus.dk/tj/gdc2001.htm">geometric constraints</a> can be 25 * implemented. For each time step, Verlet integration is performed, new forces 26 * are accumulated, and then constraints are applied. 27 * 28 * <p>The simulation makes two simplifying assumptions: all particles are 29 * equal-mass, and the time step of the simulation is fixed. It would be easy to 30 * incorporate variable-mass particles as a future enhancement. Variable time 31 * steps are also possible, but are likely to introduce instability in the 32 * simulation. 33 * 34 * <p>This class can be used directly to simulate particle interaction. 35 * Alternatively, for network diagrams, see {@link pv.Layout.Force}. 36 * 37 * @param {array} particles an array of {@link pv.Particle}s to simulate. 38 * @see pv.Layout.Force 39 * @see pv.Force 40 * @see pv.Constraint 41 */ 42 pv.Simulation = function(particles) { 43 for (var i = 0; i < particles.length; i++) this.particle(particles[i]); 44 }; 45 46 /** 47 * The particles in the simulation. Particles are stored as a linked list; this 48 * field represents the first particle in the simulation. 49 * 50 * @field 51 * @type pv.Particle 52 * @name pv.Simulation.prototype.particles 53 */ 54 55 /** 56 * The forces in the simulation. Forces are stored as a linked list; this field 57 * represents the first force in the simulation. 58 * 59 * @field 60 * @type pv.Force 61 * @name pv.Simulation.prototype.forces 62 */ 63 64 /** 65 * The constraints in the simulation. Constraints are stored as a linked list; 66 * this field represents the first constraint in the simulation. 67 * 68 * @field 69 * @type pv.Constraint 70 * @name pv.Simulation.prototype.constraints 71 */ 72 73 /** 74 * Adds the specified particle to the simulation. 75 * 76 * @param {pv.Particle} p the new particle. 77 * @returns {pv.Simulation} this. 78 */ 79 pv.Simulation.prototype.particle = function(p) { 80 p.next = this.particles; 81 /* Default velocities and forces to zero if unset. */ 82 if (isNaN(p.px)) p.px = p.x; 83 if (isNaN(p.py)) p.py = p.y; 84 if (isNaN(p.fx)) p.fx = 0; 85 if (isNaN(p.fy)) p.fy = 0; 86 this.particles = p; 87 return this; 88 }; 89 90 /** 91 * Adds the specified force to the simulation. 92 * 93 * @param {pv.Force} f the new force. 94 * @returns {pv.Simulation} this. 95 */ 96 pv.Simulation.prototype.force = function(f) { 97 f.next = this.forces; 98 this.forces = f; 99 return this; 100 }; 101 102 /** 103 * Adds the specified constraint to the simulation. 104 * 105 * @param {pv.Constraint} c the new constraint. 106 * @returns {pv.Simulation} this. 107 */ 108 pv.Simulation.prototype.constraint = function(c) { 109 c.next = this.constraints; 110 this.constraints = c; 111 return this; 112 }; 113 114 /** 115 * Apply constraints, and then set the velocities to zero. 116 * 117 * @returns {pv.Simulation} this. 118 */ 119 pv.Simulation.prototype.stabilize = function(n) { 120 var c; 121 if (!arguments.length) n = 3; // TODO use cooling schedule 122 for (var i = 0; i < n; i++) { 123 var q = new pv.Quadtree(this.particles); 124 for (c = this.constraints; c; c = c.next) c.apply(this.particles, q); 125 } 126 for (var p = this.particles; p; p = p.next) { 127 p.px = p.x; 128 p.py = p.y; 129 } 130 return this; 131 }; 132 133 /** 134 * Advances the simulation one time-step. 135 */ 136 pv.Simulation.prototype.step = function() { 137 var p, f, c; 138 139 /* 140 * Assumptions: 141 * - The mass (m) of every particles is 1. 142 * - The time step (dt) is 1. 143 */ 144 145 /* Position Verlet integration. */ 146 for (p = this.particles; p; p = p.next) { 147 var px = p.px, py = p.py; 148 p.px = p.x; 149 p.py = p.y; 150 p.x += p.vx = ((p.x - px) + p.fx); 151 p.y += p.vy = ((p.y - py) + p.fy); 152 } 153 154 /* Apply constraints, then accumulate new forces. */ 155 var q = new pv.Quadtree(this.particles); 156 for (c = this.constraints; c; c = c.next) c.apply(this.particles, q); 157 for (p = this.particles; p; p = p.next) p.fx = p.fy = 0; 158 for (f = this.forces; f; f = f.next) f.apply(this.particles, q); 159 }; 160