Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(614)

Side by Side Diff: experimental/flocking_geese/js/goose.js

Issue 10928195: First round of dead file removal (Closed) Base URL: https://github.com/samclegg/nativeclient-sdk.git@master
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @file
7 * The Goose object. This implements a single goose that flocks with other
8 * geese in the flocking simulation. Taken almost directly from
9 * http://processingjs.org/learning/topic/flocking with references to
10 * http://harry.me/2011/02/17/neat-algorithms---flocking.
11 */
12
13
14 goog.provide('Goose');
15
16 goog.require('goog.Disposable');
17 goog.require('goog.math.Vec2');
18
19 /**
20 * A Goose. Starts the goose with a location and a velocity.
21 * @param {?goog.math.Vec2} opt_location The initial location of this
22 * goose. If not given, then the goose is started at (0, 0).
23 * @param {?goog.math.Vec2} opt_velocity The initial velocity of this goose.
24 * If not given, then the goose is assigned a random velocity.
25 * @constructor
26 * @extends {goog.Disposable}
27 */
28 Goose = function(opt_location, opt_velocity) {
29 goog.Disposable.call(this);
30 /**
31 * The goose's current location.
32 * @type {goog.math.Vec2}
33 * @private
34 */
35 if (opt_location) {
36 this.location_ = opt_location.clone();
37 } else {
38 this.location_ = new goog.math.Vec2(0, 0);
39 }
40
41 /**
42 * The goose's current velocity.
43 * @type {goog.math.Vec2}
44 * @private
45 */
46 if (opt_velocity) {
47 this.velocity_ = opt_velocity.clone();
48 } else {
49 this.velocity_ = new goog.math.Vec2.randomUnit();
50 }
51 };
52 goog.inherits(Goose, goog.Disposable);
53
54 /**
55 * The maximum speed of a goose. Measured in meters/second.
56 * @type {number}
57 */
58 Goose.prototype.MAX_SPEED = 2.0;
59
60 /**
61 * The maximum force that can be applied to turn a goose when computing the
62 * aligment. Measured in meters/second/second.
63 * @type {number}
64 */
65 Goose.prototype.MAX_TURNING_FORCE = 0.05;
66
67 /**
68 * The neighbour radius of a goose. Only geese within this radius will affect
69 * the flocking computations of this goose. Measured in pixels.
70 * @type {number}
71 */
72 Goose.prototype.NEIGHBOUR_RADIUS = 64.0;
73
74 /**
75 * The minimum distance that a goose can be from this goose. If another goose
76 * comes within this distance of this goose, the flocking algorithm tries to
77 * move the geese apart. Measured in pixels.
78 * @type {number}
79 */
80 Goose.prototype.PERSONAL_SPACE = 32.0;
81
82 /**
83 * The distance at which attractors have effect on a goose's direction.
84 * @type {number}
85 */
86 Goose.prototype.ATTRACTOR_RADIUS = 320.0;
87
88 /**
89 * The goose will try to turn towards geese within this distance (computed
90 * during the cohesion phase). Measured in pixels.
91 * @type {number}
92 */
93 Goose.prototype.MAX_TURNING_DISTANCE = 100.0;
94
95 /**
96 * The weights used when computing the weighted sum the three flocking
97 * components.
98 * @type {number}
99 */
100 Goose.prototype.SEPARATION_WEIGHT = 2.0;
101 Goose.prototype.ALIGNMENT_WEIGHT = 1.0;
102 Goose.prototype.COHESION_WEIGHT = 1.0;
103
104 /**
105 * Override of disposeInternal() to dispose of retained objects and unhook all
106 * events.
107 * @override
108 */
109 Goose.prototype.disposeInternal = function() {
110 this.domElement_ = null;
111 Speedometer.superClass_.disposeInternal.call(this);
112 }
113
114 /**
115 * Accessor for location.
116 * @return {goog.math.Vec2} The current location.
117 */
118 Goose.prototype.location = function() {
119 return this.location_;
120 }
121
122 /**
123 * Accessor for velocity.
124 * @return {goog.math.Vec2} The current velocity.
125 */
126 Goose.prototype.velocity = function() {
127 return this.velocity_;
128 }
129
130 /**
131 * Run one tick of the simulation. Compute a new acceleration based on the
132 * flocking algorithm (see Goose.flock()) and update the goose's location
133 * by integrating acceleration and velocity.
134 * @param {!Array.<Goose>} geese The list of all the geese in the flock.
135 * @param {!Array.<goog.math.Vec2>} attractors The list of attractors.
136 * Geese have affinity for these points.
137 * @param {?goog.math.Rect} opt_flockBox The geese will stay inside of this
138 * box. If the parameter is not given, the geese don't have boundaries.
139 */
140 Goose.prototype.simulationTick = function(geese, attractors, opt_flockBox) {
141 var acceleration = this.flock(geese, attractors);
142 this.velocity_.add(acceleration);
143 // Limit the velocity to a maximum speed.
144 this.velocity_.clamp(this.MAX_SPEED);
145 this.location_.add(this.velocity_);
146 // Wrap the goose location to the flock box.
147 if (opt_flockBox) {
148 if (this.location_.x < opt_flockBox.left) {
149 this.location_.x = opt_flockBox.left + opt_flockBox.width;
150 }
151 if (this.location_.x > (opt_flockBox.left + opt_flockBox.width)) {
152 this.location_.x = opt_flockBox.left;
153 }
154 if (this.location_.y < opt_flockBox.top) {
155 this.location_.y = opt_flockBox.top + opt_flockBox.height;
156 }
157 if (this.location_.y > (opt_flockBox.top + opt_flockBox.height)) {
158 this.location_.y = opt_flockBox.top;
159 }
160 }
161 }
162
163 /**
164 * Implement the flocking algorithm in five steps:
165 * 1. Compute the separation component,
166 * 2. Compute the alignment component,
167 * 3. Compute the flock cohesion component.
168 * 4. Compute the effect of the attractors and blend this in with the
169 * cohesion component.
170 * 5. Create a weighted sum of the three components and use this as the
171 * new acceleration for the goose.
172 * This is an O(n^2) version of the algorithm. There are ways to speed this
173 * up using spatial coherence techniques, but this version is much simpler.
174 * @param {!Array.<Goose>} geese The list of all the neighbouring geese (in
175 * this implementation, this is all the geese in the flock).
176 * @param {!Array.<goog.math.Vec2>} attractors The list of attractors.
177 * Geese have affinity for these points.
178 * @return {goog.math.Vec2} The acceleration vector for this goose based on the
179 * flocking algorithm.
180 */
181 Goose.prototype.flock = function(geese, attractors) {
182 // Loop over all the neighbouring geese in the flock, accumulating
183 // the separation mean, the alignment mean and the cohesion mean.
184 var separationCount = 0;
185 var separation = new goog.math.Vec2(0, 0);
186 var alignCount = 0;
187 var alignment = new goog.math.Vec2(0, 0);
188 var cohesionCount = 0;
189 var cohesion = new goog.math.Vec2(0, 0);
190 for (var i = 0; i < geese.length; i++) {
191 var goose = geese[i];
192 // Compute the distance from this goose to its neighbour.
193 var gooseDirection = goog.math.Vec2.difference(
194 this.location_, goose.location());
195 var distance = gooseDirection.magnitude();
196 separationCount = this.accumulateSeparation_(
197 distance, gooseDirection, separation, separationCount);
198 alignCount = this.accumulateAlignment_(
199 distance, goose, alignment, alignCount);
200 cohesionCount = this.accumulateCohesion_(
201 distance, goose, cohesion, cohesionCount);
202 }
203 // Compute the means and create a weighted sum. This becomes the goose's new
204 // acceleration.
205 if (separationCount > 0) {
206 separation.scale(1.0 / separationCount);
207 }
208 if (alignCount > 0) {
209 alignment.scale(1.0 / alignCount);
210 // Limit the effect that alignment has on the final acceleration. The
211 // alignment component can overpower the others if there is a big
212 // difference between this goose's velocity and its neighbours'.
213 alignment.clamp(this.MAX_TURNING_FORCE);
214 }
215
216 // Compute the effect of the attractors and blend this in with the flock
217 // cohesion component. An attractor has to be within ATTRACTOR_RADIUS to
218 // effect the heading of a goose.
219 for (var i = 0; i < attractors.length; i++) {
220 var attractorDirection = goog.math.Vec2.difference(
221 attractors[i], this.location_);
222 var distance = attractorDirection.magnitude();
223 if (distance < this.ATTRACTOR_RADIUS) {
224 attractorDirection.scale(1000); // Each attractor acts like 1000 geese.
225 cohesion.add(attractorDirection);
226 cohesionCount += 1;
227 }
228 }
229
230 // If there is a non-0 cohesion component, steer the goose so that it tries
231 // to follow the flock.
232 if (cohesionCount > 0) {
233 cohesion.scale(1.0 / cohesionCount);
234 cohesion = this.turnTowardsTarget(cohesion);
235 }
236
237 // Compute the weighted sum.
238 separation.scale(this.SEPARATION_WEIGHT);
239 alignment.scale(this.ALIGNMENT_WEIGHT);
240 cohesion.scale(this.COHESION_WEIGHT);
241 var weightedSum = cohesion.clone();
242 weightedSum.add(alignment);
243 weightedSum.add(separation);
244 return weightedSum;
245 }
246
247 /**
248 * Add a neighbouring goose's contribution to the separation mean. Only
249 * consider geese that have moved inside of this goose's personal space.
250 * Modifies the separation accumulator |separation| in-place.
251 * @param {!number} distance The distance from this goose to the neighbouring
252 * goose.
253 * @param {!goog.math.Vec2} gooseDirection The direction vector from this
254 * goose to the neighbour.
255 * @param {!goog.math.Vec2} separation The accumulated separation from all the
256 * neighbouring geese.
257 * @param {!number} separationCount The current number of geese that have
258 * contributed to the separation component so far.
259 * @return {!number} The new count of geese that contribute to the separation
260 * component. If the goose under consideration does not contribute, this
261 * value is the same as |separationCount|.
262 * @private
263 */
264 Goose.prototype.accumulateSeparation_ = function(distance,
265 gooseDirection,
266 separation,
267 separationCount) {
268 if (distance > 0 && distance < this.PERSONAL_SPACE) {
269 var weightedDirection = gooseDirection;
270 weightedDirection.normalize();
271 weightedDirection.scale(1.0 / distance);
272 separation.add(weightedDirection);
273 separationCount++;
274 }
275 return separationCount;
276 }
277
278 /**
279 * Add a neighbouring goose's contribution to the alignment mean. Alignment is
280 * the average velocity of the neighbours. Only consider geese that are within
281 * |NEIGHBOUR_RADIUS|. Modifies the alignment accumulator |alignment| in-place.
282 * @param {!number} distance The distance from this goose to the neighbouring
283 * goose.
284 * @param {!Goose} goose The neighbouring goose under consideration.
285 * @param {!goog.math.Vec2} alignment The accumulated alignment from all the
286 * neighbouring geese.
287 * @param {!number} alignCount The current number of geese that have
288 * contributed to the alignment component so far.
289 * @return {!number} The new count of geese that contribute to the alignment
290 * component. If the goose under consideration does not contribute, this
291 * value is the same as |alignCount|.
292 * @private
293 */
294 Goose.prototype.accumulateAlignment_ = function(distance,
295 goose,
296 alignment,
297 alignCount) {
298 if (distance > 0 && distance < this.NEIGHBOUR_RADIUS) {
299 alignment.add(goose.velocity());
300 alignCount++;
301 }
302 return alignCount;
303 }
304
305 /**
306 * Add a neighbouring goose's contribution to the cohesion mean. Cohesion is
307 * based on the average location of the neighbours. The goose attempts to
308 * point to this average location. Only consider geese that are within
309 * |NEIGHBOUR_RADIUS|. Modifies the cohesion accumulator |cohesion| in-place.
310 * @param {!number} distance The distance from this goose to the neighbouring
311 * goose.
312 * @param {!Goose} goose The neighbouring goose under consideration.
313 * @param {!goog.math.Vec2} cohesion The accumulated cohesion from all the
314 * neighbouring geese.
315 * @param {!number} cohesionCount The current number of geese that have
316 * contributed to the cohesion component so far.
317 * @return {!number} The new count of geese that contribute to the cohesion
318 * component. If the goose under consideration does not contribute, this
319 * value is the same as |cohesionCount|.
320 * @private
321 */
322 Goose.prototype.accumulateCohesion_ = function(distance,
323 goose,
324 cohesion,
325 cohesionCount) {
326 if (distance > 0 && distance < this.NEIGHBOUR_RADIUS) {
327 cohesion.add(goose.location());
328 cohesionCount++;
329 }
330 return cohesionCount;
331 }
332
333 /**
334 * Turn the goose towards a target. The amount of turning force is clamped
335 * to |MAX_TURNING_FORCE|.
336 * @param {!goog.math.Vec2} target Turn the goose towards this target.
337 * @return {goog.math.Vec2} A vector representing the new direction of the
338 * goose.
339 */
340 Goose.prototype.turnTowardsTarget = function(target) {
341 var desiredDirection = goog.math.Vec2.difference(target, this.location_);
342 var distance = desiredDirection.magnitude();
343 var newDirection = new goog.math.Vec2(0, 0);
344 if (distance > 0) {
345 desiredDirection.normalize();
346 // If the target is within the turning affinity distance, then make the
347 // desired direction based on distance to the target. Otherwise, base
348 // the desired direction on MAX_SPEED.
349 if (distance < this.MAX_TURNING_DISTANCE) {
350 // Some pretty arbitrary dampening.
351 desiredDirection.scale(this.MAX_SPEED * distance / 100.0);
352 } else {
353 desiredDirection.scale(this.MAX_SPEED);
354 }
355 newDirection = goog.math.Vec2.difference(desiredDirection, this.velocity_);
356 newDirection.clamp(this.MAX_TURNING_FORCE);
357 }
358 return newDirection;
359 }
360
361 /**
362 * Clamp a vector to a maximum magnitude. Works on the vector in-place.
363 * @param {!goog.math.Vec2} v The vector.
364 * @param {!number} max_mag The maximum magnitude of the vector.
365 */
366 goog.math.Vec2.prototype.clamp = function(max_mag) {
367 var mag = this.magnitude();
368 if (mag > max_mag && max_mag != 0) {
369 this.normalize();
370 this.scale(max_mag);
371 }
372 }
373
374 /**
375 * Compute the "heading" of a vector - this is the angle in radians between
376 * the vector and the x-axis.
377 * @return {!number} The "heading" angle in radians.
378 */
379 goog.math.Vec2.prototype.heading = function() {
380 var angle = Math.atan2(this.y, this.x);
381 return angle;
382 }
383
OLDNEW
« no previous file with comments | « experimental/flocking_geese/js/frame_counter.js ('k') | experimental/flocking_geese/js/load_progress.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698