```  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 ```