source: Dev/trunk/d3/examples/voroboids/boid.js @ 76

Last change on this file since 76 was 76, checked in by fpvanagthoven, 14 years ago

d3

File size: 5.9 KB
Line 
1// Boid flocking based on http://harry.me/2011/02/17/neat-algorithms---flocking
2var boid = (function() {
3  function boid() {
4    var position = [0, 0],
5        velocity = [0, 0],
6        gravityCenter = null,
7        neighborRadius = 50,
8        maxForce = .1,
9        maxSpeed = 1,
10        separationWeight = 2,
11        alignmentWeight = 1,
12        cohesionWeight = 1,
13        desiredSeparation = 10;
14
15    function boid(neighbors) {
16      var accel = flock(neighbors);
17      d3_ai_boidWrap(position);
18      velocity[0] += accel[0];
19      velocity[1] += accel[1];
20      if (gravityCenter) {
21        var g = d3_ai_boidGravity(gravityCenter, position, neighborRadius);
22        velocity[0] += g[0];
23        velocity[1] += g[1];
24      }
25      d3_ai_boidLimit(velocity, maxSpeed);
26      position[0] += velocity[0];
27      position[1] += velocity[1];
28      return position;
29    }
30
31    function flock(neighbors) {
32      var separation = [0, 0],
33          alignment = [0, 0],
34          cohesion = [0, 0],
35          separationCount = 0,
36          alignmentCount = 0,
37          cohesionCount = 0,
38          i = -1,
39          l = neighbors.length;
40      while (++i < l) {
41        var n = neighbors[i];
42        if (n === this) continue;
43        var npos = n.position(),
44            d = d3_ai_boidDistance(position, npos);
45        if (d > 0) {
46          if (d < desiredSeparation) {
47            var tmp = d3_ai_boidNormalize(d3_ai_boidSubtract(position.slice(), npos));
48            separation[0] += tmp[0] / d;
49            separation[1] += tmp[1] / d;
50            separationCount++;
51          }
52          if (d < neighborRadius) {
53            var nvel = n.velocity();
54            alignment[0] += nvel[0];
55            alignment[1] += nvel[1];
56            alignmentCount++;
57            cohesion[0] += npos[0];
58            cohesion[1] += npos[1];
59            cohesionCount++;
60          }
61        }
62      }
63
64      if (separationCount > 0) {
65        separation[0] /= separationCount;
66        separation[1] /= separationCount;
67      }
68
69      if (alignmentCount > 0) {
70        alignment[0] /= alignmentCount;
71        alignment[1] /= alignmentCount;
72      }
73      d3_ai_boidLimit(alignment, maxForce);
74
75      if (cohesionCount > 0) {
76        cohesion[0] /= cohesionCount;
77        cohesion[1] /= cohesionCount;
78      } else {
79        cohesion = position.slice();
80      }
81      cohesion = steerTo(cohesion);
82
83      return [
84        separation[0] * separationWeight +
85         alignment[0] * alignmentWeight +
86          cohesion[0] * cohesionWeight,
87        separation[1] * separationWeight +
88         alignment[1] * alignmentWeight +
89          cohesion[1] * cohesionWeight
90      ];
91    }
92
93    function steerTo(target) {
94      var desired = d3_ai_boidSubtract(target, position),
95          d = d3_ai_boidMagnitude(desired);
96
97      if (d > 0) {
98        d3_ai_boidNormalize(desired);
99
100        // Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
101        var mul = maxSpeed * (d < 100 ? d / 100 : 1);
102        desired[0] *= mul;
103        desired[1] *= mul;
104
105        // Steering = Desired minus Velocity
106        var steer = d3_ai_boidSubtract(desired, velocity);
107        d3_ai_boidLimit(steer, maxForce)  // Limit to maximum steering force
108      } else {
109        steer = [0, 0];
110      }
111      return steer;
112    }
113
114    boid.position = function(x) {
115      if (!arguments.length) return position;
116      position = x;
117      return boid;
118    }
119
120    boid.velocity = function(x) {
121      if (!arguments.length) return velocity;
122      velocity = x;
123      return boid;
124    }
125
126    boid.gravityCenter = function(x) {
127      if (!arguments.length) return gravityCenter;
128      gravityCenter = x;
129      return boid;
130    }
131
132    boid.neighborRadius = function(x) {
133      if (!arguments.length) return neighborRadius;
134      neighborRadius = x;
135      return boid;
136    }
137
138    boid.maxForce = function(x) {
139      if (!arguments.length) return maxForce;
140      maxForce = x;
141      return boid;
142    }
143
144    boid.maxSpeed = function(x) {
145      if (!arguments.length) return maxSpeed;
146      maxSpeed = x;
147      return boid;
148    }
149
150    boid.separationWeight = function(x) {
151      if (!arguments.length) return separationWeight;
152      separationWeight = x;
153      return boid;
154    }
155
156    boid.alignmentWeight = function(x) {
157      if (!arguments.length) return alignmentWeight;
158      alignmentWeight = x;
159      return boid;
160    }
161
162    boid.cohesionWeight = function(x) {
163      if (!arguments.length) return cohesionWeight;
164      cohesionWeight = x;
165      return boid;
166    }
167
168    boid.desiredSeparation = function(x) {
169      if (!arguments.length) return desiredSeparation;
170      desiredSeparation = x;
171      return boid;
172    }
173
174    return boid;
175  }
176
177  function d3_ai_boidNormalize(a) {
178    var m = d3_ai_boidMagnitude(a);
179    if (m > 0) {
180      a[0] /= m;
181      a[1] /= m;
182    }
183    return a;
184  }
185
186  function d3_ai_boidWrap(position) {
187    if (position[0] > w) position[0] = 0;
188    else if (position[0] < 0) position[0] = w;
189    if (position[1] > h) position[1] = 0;
190    else if (position[1] < 0) position[1] = h;
191  }
192
193  function d3_ai_boidGravity(center, position, neighborRadius) {
194    if (center[0] != null) {
195      var m = d3_ai_boidSubtract(center.slice(), position),
196          d = d3_ai_boidMagnitude(m) - 10;
197      if (d > 0 && d < neighborRadius * 5) {
198        d3_ai_boidNormalize(m);
199        m[0] /= d;
200        m[1] /= d;
201        return m;
202      }
203    }
204    return [0, 0];
205  }
206
207  function d3_ai_boidDistance(a, b) {
208    var dx = a[0] - b[0],
209        dy = a[1] - b[1];
210    if (dx > w / 2) dx = w - dx;
211    if (dy > h / 2) dy = h - dy;
212    return Math.sqrt(dx * dx + dy * dy);
213  }
214
215  function d3_ai_boidSubtract(a, b) {
216    a[0] -= b[0];
217    a[1] -= b[1];
218    return a;
219  }
220
221  function d3_ai_boidMagnitude(v) {
222    return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
223  }
224
225  function d3_ai_boidLimit(a, max) {
226    if (d3_ai_boidMagnitude(a) > max) {
227      d3_ai_boidNormalize(a);
228      a[0] *= max;
229      a[1] *= max;
230    }
231    return a;
232  }
233
234  return boid;
235})();
Note: See TracBrowser for help on using the repository browser.