```  1 /**
2  * Constructs a new spring force with the specified constant. The links
3  * associated with this spring force must be specified before the spring force
4  * can be applied.
5  *
6  * @class Implements a spring force, per Hooke's law. The spring force can be
7  * configured with a tension constant, rest length, and damping factor. The
8  * tension and damping will automatically be normalized using the inverse square
9  * root of the maximum link degree of attached nodes; this makes springs weaker
10  * between nodes of high link degree.
11  *
12  * <p>Unlike other forces (such as charge and drag forces) which may be applied
13  * globally, spring forces are only applied between linked particles. Therefore,
14  * an array of links must be specified before this force can be applied; the
16  * {@link pv.Layout.Force} for an example of using spring and charge forces for
17  * network layout.
18  *
19  * @extends pv.Force
20  * @param {number} k the spring constant.
21  * @see #constant
23  */
24 pv.Force.spring = function(k) {
25   var d = .1, // default damping factor
26       l = 20, // default rest length
28       kl, // per-spring normalization
29       force = {};
30
31   if (!arguments.length) k = .1; // default spring constant (tension)
32
33   /**
34    * Sets or gets the links associated with this spring force. Unlike other
35    * forces (such as charge and drag forces) which may be applied globally,
36    * spring forces are only applied between linked particles. Therefore, an
37    * array of links must be specified before this force can be applied; the
39    *
40    * @function
42    * @param {array} x the new array of links.
43    * @returns {pv.Force.spring} this, or the current array of links.
44    */
46     if (arguments.length) {
48       kl = x.map(function(l) {
49           return 1 / Math.sqrt(Math.max(
52         });
53       return force;
54     }
56   };
57
58   /**
59    * Sets or gets the spring constant. The default value is 0.1; greater values
60    * will result in stronger tension. The spring tension is automatically
61    * normalized using the inverse square root of the maximum link degree of
62    * attached nodes.
63    *
64    * @function
65    * @name pv.Force.spring.prototype.constant
66    * @param {number} x the new spring constant.
67    * @returns {pv.Force.spring} this, or the current spring constant.
68    */
69   force.constant = function(x) {
70     if (arguments.length) {
71       k = Number(x);
72       return force;
73     }
74     return k;
75   };
76
77   /**
78    * The spring damping factor, in the range [0,1]. Damping functions
79    * identically to drag forces, damping spring bounciness by applying a force
80    * in the opposite direction of attached nodes' velocities. The default value
81    * is 0.1. The spring damping is automatically normalized using the inverse
82    * square root of the maximum link degree of attached nodes.
83    *
84    * @function
85    * @name pv.Force.spring.prototype.damping
86    * @param {number} x the new spring damping factor.
87    * @returns {pv.Force.spring} this, or the current spring damping factor.
88    */
89   force.damping = function(x) {
90     if (arguments.length) {
91       d = Number(x);
92       return force;
93     }
94     return d;
95   };
96
97   /**
98    * The spring rest length. The default value is 20 pixels.
99    *
100    * @function
101    * @name pv.Force.spring.prototype.length
102    * @param {number} x the new spring rest length.
103    * @returns {pv.Force.spring} this, or the current spring rest length.
104    */
105   force.length = function(x) {
106     if (arguments.length) {
107       l = Number(x);
108       return force;
109     }
110     return l;
111   };
112
113   /**
114    * Applies this force to the specified particles.
115    *
116    * @function
117    * @name pv.Force.spring.prototype.apply
118    * @param {pv.Particle} particles particles to which to apply this force.
119    */
120   force.apply = function(particles) {
121     for (var i = 0; i < links.length; i++) {
124           dx = a.x - b.x,
125           dy = a.y - b.y,
126           dn = Math.sqrt(dx * dx + dy * dy),
127           dd = dn ? (1 / dn) : 1,
128           ks = k * kl[i], // normalized tension
129           kd = d * kl[i], // normalized damping
130           kk = (ks * (dn - l) + kd * (dx * (a.vx - b.vx) + dy * (a.vy - b.vy)) * dd) * dd,
131           fx = -kk * (dn ? dx : (.01 * (.5 - Math.random()))),
132           fy = -kk * (dn ? dy : (.01 * (.5 - Math.random())));
133       a.fx += fx;
134       a.fy += fy;
135       b.fx -= fx;
136       b.fy -= fy;
137     }
138   };
139
140   return force;
141 };
142 ```