OLD | NEW |
| (Empty) |
1 // Copyright 2011 (c) 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 FlockingGeese application object. Encapsulates three principle app | |
8 * elements: the NaCl module which runs the C++ implementation of the flocking | |
9 * algorithm, a canvas used by the JavaScript implementation, and a DOM | |
10 * element that contains the info display (which includes things like the | |
11 * speedometer, control buttons, etc.). | |
12 */ | |
13 | |
14 goog.provide('FlockingGeese'); | |
15 | |
16 goog.require('Flock'); | |
17 goog.require('Goose'); | |
18 goog.require('LoadProgress'); | |
19 goog.require('Speedometer'); | |
20 goog.require('Speedometer.Attributes'); | |
21 goog.require('goog.Disposable'); | |
22 goog.require('goog.array'); | |
23 goog.require('goog.dom'); | |
24 goog.require('goog.events.EventType'); | |
25 goog.require('goog.math.Vec2'); | |
26 goog.require('goog.style'); | |
27 | |
28 /** | |
29 * Constructor for the FlockingGeese class. Use the run() method to populate | |
30 * the object with controllers and wire up the events. | |
31 * @param {string} containerId The DOM id of the application's container. | |
32 * @constructor | |
33 * @extends {goog.Disposable} | |
34 */ | |
35 FlockingGeese = function(containerId) { | |
36 goog.Disposable.call(this); | |
37 | |
38 /** | |
39 * The DOM element that contains the application. | |
40 * @type {Element} | |
41 * @private | |
42 */ | |
43 this.container_ = document.getElementById(containerId); | |
44 | |
45 /** | |
46 * The flock of geese as implemented in JavaScript. | |
47 * @type {Flock} | |
48 * @private | |
49 */ | |
50 this.flock_ = new Flock(); | |
51 | |
52 /** | |
53 * Bit that tells whether the JS simulation is running or not. | |
54 * @type {bool} | |
55 * @private | |
56 */ | |
57 this.javascriptSimRunning_ = false; | |
58 | |
59 /** | |
60 * The NaCl module. This is an <EMBED> element that contains the NaCl | |
61 * module. Set during moduleDidLoad(). | |
62 * @type {Element} | |
63 * @private | |
64 */ | |
65 this.naclModule_ = null; | |
66 | |
67 /** | |
68 * The load progress bar. | |
69 * @type {LoadProgress} | |
70 * @private | |
71 */ | |
72 this.progressBar_ = new LoadProgress(); | |
73 | |
74 /** | |
75 * The speedometer. | |
76 * @type {Speedometer} | |
77 * @private | |
78 */ | |
79 this.speedometer_ = new Speedometer(); | |
80 | |
81 /** | |
82 * The simluation mode slider. This becomes a Slider object. | |
83 * @type {Slider} | |
84 * @private | |
85 */ | |
86 this.simModeSlider_ = null; | |
87 | |
88 /** | |
89 * The flock size slider. This becomes a Slider object. | |
90 * @type {Slider} | |
91 * @private | |
92 */ | |
93 this.flockSizeSlider_ = null; | |
94 | |
95 /** | |
96 * The current simulation mode. Starts in 'JavaScript'. | |
97 * @type {string} | |
98 * @private | |
99 */ | |
100 this.simulationMode_ = FlockingGeese.SimulationMode.JAVASCRIPT; | |
101 | |
102 /** | |
103 * Event handling. | |
104 */ | |
105 this.mouseMoveListener_ = null; | |
106 this.mouseUpListener_ = null; | |
107 } | |
108 goog.inherits(FlockingGeese, goog.Disposable); | |
109 | |
110 /** | |
111 * Method signatures that are sent from the NaCl module. A signature is really | |
112 * just a method id - later we can upgrade this to accept (say) full JSON | |
113 * strings and parse them, but for now a simple solution will work. The strings | |
114 * need to match the corresponding ones labeled "output method ids" in | |
115 * flocking_geese_app.cc. | |
116 * @enum {Object} | |
117 * @private | |
118 */ | |
119 FlockingGeese.prototype.MethodSignatures_ = { | |
120 SET_SIMULATION_INFO: 'setSimulationInfo', | |
121 FRAME_RATE: 'frameRate' | |
122 }; | |
123 | |
124 /** | |
125 * The ids used for elements in the DOM. The FlockingGeese application | |
126 * expects these elements to exist. | |
127 * @enum {string} | |
128 */ | |
129 FlockingGeese.DomIds = { | |
130 // The elements for the flock size slider. Contained in the info panel. | |
131 FLOCK_SIZE_SLIDER: 'flock_size_slider', | |
132 FLOCK_SIZE_SLIDER_RULER: 'flock_size_slider_ruler', | |
133 FLOCK_SIZE_SLIDER_THUMB: 'flock_size_slider_thumb', | |
134 // The <FORM> containing various controls. | |
135 CONTROLS_FORM: 'game_controls_form', | |
136 // The <CANVAS> where the JS implementation draws its geese. | |
137 GOOSE_CANVAS: 'flocking_geese_canvas', | |
138 // The <EMBED> element containing the NaCl module. | |
139 NACL_MODULE: 'flocking_geese_nacl_module', | |
140 // The <DIV> containing the simulation drawing area (either the NaCl module | |
141 // or the <CANVAS>. | |
142 NACL_VIEW: 'nacl_flocking_geese', | |
143 // The slider buttons used to select sim type (NaCL vs. JavaScript). | |
144 // Contained in the info panel. | |
145 SIM_MODE_SELECT: 'sim_mode_select', | |
146 SIM_SELECT_RULER: 'sim_select_ruler', | |
147 SIM_SELECT_THUMB: 'sim_select_thumb', | |
148 // The text that displays the speed difference. | |
149 SPEED_DIFFERENCE_LABEL: 'speed_difference', | |
150 // The speedometer <CANVAS> element. Contained in the info panel. | |
151 SPEEDOMETER: 'speedometer_canvas' | |
152 }; | |
153 | |
154 /** | |
155 * The list of simulation modes. These are the values assigned to the | |
156 * simulation mode radio buttons. | |
157 * @enum {string} | |
158 */ | |
159 FlockingGeese.SimulationMode = { | |
160 NACL: 'NaCl', | |
161 JAVASCRIPT: 'JavaScript' | |
162 }; | |
163 | |
164 /** | |
165 * The list of meter names. | |
166 * @enum {string} | |
167 */ | |
168 FlockingGeese.MeterNames = { | |
169 NACL: 'NaCl', | |
170 JAVASCRIPT: 'JavaScript' | |
171 }; | |
172 | |
173 /** | |
174 * Override of disposeInternal() to dispose of retained objects. | |
175 * @override | |
176 */ | |
177 FlockingGeese.prototype.disposeInternal = function() { | |
178 this.terminate(); | |
179 FlockingGeese.superClass_.disposeInternal.call(this); | |
180 } | |
181 | |
182 /** | |
183 * Set up all the DOM elements and wire together the events. | |
184 */ | |
185 FlockingGeese.prototype.initializeApplication = function() { | |
186 // Listen for 'unload' in order to terminate cleanly. | |
187 goog.events.listen(window, goog.events.EventType.UNLOAD, this.terminate); | |
188 var naclMeterAttribs = {}; | |
189 naclMeterAttribs[Speedometer.Attributes.THEME] = Speedometer.Themes.GREEN; | |
190 naclMeterAttribs[Speedometer.Attributes.ODOMETER_LEFT] = 84; | |
191 naclMeterAttribs[Speedometer.Attributes.ODOMETER_TOP] = 156; | |
192 this.speedometer_.addMeterWithName(FlockingGeese.MeterNames.NACL, | |
193 naclMeterAttribs); | |
194 var jsMeterAttribs = {}; | |
195 jsMeterAttribs[Speedometer.Attributes.THEME] = Speedometer.Themes.RED; | |
196 jsMeterAttribs[Speedometer.Attributes.ODOMETER_LEFT] = 84; | |
197 jsMeterAttribs[Speedometer.Attributes.ODOMETER_TOP] = 192; | |
198 this.speedometer_.addMeterWithName(FlockingGeese.MeterNames.JAVASCRIPT, | |
199 jsMeterAttribs); | |
200 this.speedometer_.setMaximumSpeed(10000.0); // Measured in frames per second. | |
201 | |
202 var speedometerCanvas = | |
203 document.getElementById(FlockingGeese.DomIds.SPEEDOMETER); | |
204 this.speedometer_.setCanvas(speedometerCanvas); | |
205 this.speedometer_.reset(); | |
206 this.speedometer_.render(this.speedometerCanvas_); | |
207 | |
208 // Wire up the various controls. | |
209 goog.events.listen(this.container_, goog.events.EventType.MOUSEDOWN, | |
210 this.mouseDown_, true, this); | |
211 | |
212 var simSelectSlider = | |
213 document.getElementById(FlockingGeese.DomIds.SIM_MODE_SELECT); | |
214 var simSelectRuler = | |
215 document.getElementById(FlockingGeese.DomIds.SIM_SELECT_RULER); | |
216 var simSelectThumb = | |
217 document.getElementById(FlockingGeese.DomIds.SIM_SELECT_THUMB); | |
218 this.simModeSlider_ = new Slider( | |
219 [{simMode: FlockingGeese.SimulationMode.NACL}, | |
220 {simMode: FlockingGeese.SimulationMode.JAVASCRIPT} | |
221 ]); | |
222 this.simModeSlider_.decorate(simSelectSlider, simSelectRuler, simSelectThumb); | |
223 this.simModeSlider_.rulerLength = 80; | |
224 this.simModeSlider_.rulerOffset = 12; | |
225 this.simModeSlider_.slideToStep(1); | |
226 goog.events.listen(this.simModeSlider_, SliderEvent.EventType.CHANGE, | |
227 this.toggleSimMode, false, this); | |
228 | |
229 var flockSizeSlider = | |
230 document.getElementById(FlockingGeese.DomIds.FLOCK_SIZE_SLIDER); | |
231 var sliderRuler = document.getElementById( | |
232 FlockingGeese.DomIds.FLOCK_SIZE_SLIDER_RULER); | |
233 var sliderThumb = document.getElementById( | |
234 FlockingGeese.DomIds.FLOCK_SIZE_SLIDER_THUMB); | |
235 this.flockSizeSlider_ = new Slider( | |
236 [{gooseCount: 50, meterLimit: 30000.0}, | |
237 {gooseCount: 100, meterLimit: 8000.0}, | |
238 {gooseCount: 500, meterLimit: 400.0}, | |
239 {gooseCount: 1000, meterLimit: 100.0} | |
240 ]); | |
241 this.flockSizeSlider_.decorate(flockSizeSlider, sliderRuler, sliderThumb); | |
242 this.flockSizeSlider_.rulerLength = 110; | |
243 this.flockSizeSlider_.rulerOffset = 18; | |
244 this.flockSizeSlider_.slideToStep(0); | |
245 goog.events.listen(this.flockSizeSlider_, SliderEvent.EventType.CHANGE, | |
246 this.selectFlockSize, false, this); | |
247 | |
248 // Populate the JavaScript verison of the simulation. | |
249 var flockSize = 0; | |
250 if (this.flockSizeSlider_) { | |
251 var stepObject = this.flockSizeSlider_.objectAtStepValue(); | |
252 flockSize = stepObject.gooseCount; | |
253 this.speedometer_.setMaximumSpeed(stepObject.meterLimit); | |
254 } | |
255 var initialLocation = this.getCanvasCenter_(); | |
256 this.flock_.resetFlock(flockSize, initialLocation); | |
257 } | |
258 | |
259 /** | |
260 * Called by the module loading function once the module has been loaded. | |
261 * @param {?String} opt_naclModuleId The id of an <EMBED> element which | |
262 * contains the NaCl module. If unspecified, defaults to |NACL_MODULE|. | |
263 */ | |
264 FlockingGeese.prototype.moduleDidLoad = function(opt_naclModuleId) { | |
265 // Set up the view controller, it contains the NaCl module. | |
266 var naclModuleId = opt_naclModuleId || FlockingGeese.DomIds.NACL_MODULE; | |
267 this.naclModule_ = document.getElementById(naclModuleId); | |
268 if (this.flockSizeSlider_) { | |
269 this.invokeNaClMethod('resetFlock', | |
270 {'size' : this.flockSizeSlider_.objectAtStepValue().gooseCount}); | |
271 } | |
272 // Hide the load progress bar and make the module element visible. | |
273 this.progressBar_.setVisible(false); | |
274 this.naclModule_.style.visibility = 'inherit'; | |
275 // If the NaCL module is being displayed, then start the simulation. | |
276 var naclView = document.getElementById(FlockingGeese.DomIds.NACL_VIEW); | |
277 if (naclView.style.visibility == 'visible') { | |
278 this.invokeNaClMethod('runSimulation'); | |
279 } | |
280 } | |
281 | |
282 /** | |
283 * Asserts that cond is true; issues an alert and throws an Error otherwise. | |
284 * @param {bool} cond The condition. | |
285 * @param {String} message The error message issued if cond is false. | |
286 */ | |
287 FlockingGeese.prototype.assert = function(cond, message) { | |
288 if (!cond) { | |
289 message = "Assertion failed: " + message; | |
290 alert(message); | |
291 throw new Error(message); | |
292 } | |
293 } | |
294 | |
295 /** | |
296 * Change the flock size. | |
297 * @param {!goog.events.Event} changeEvent The CHANGE event that triggered this | |
298 * handler. | |
299 */ | |
300 FlockingGeese.prototype.selectFlockSize = function(changeEvent) { | |
301 changeEvent.stopPropagation(); | |
302 var initialLocation = this.getCanvasCenter_(); | |
303 var stepObject = | |
304 this.flockSizeSlider_.objectAtStepValue(changeEvent.stepValue); | |
305 var newFlockSize = stepObject.gooseCount; | |
306 this.speedometer_.setMaximumSpeed(stepObject.meterLimit); | |
307 // Reset the speedometers to 0. | |
308 this.speedometer_.updateMeterNamed(FlockingGeese.MeterNames.NACL, 0); | |
309 this.speedometer_.updateMeterNamed(FlockingGeese.MeterNames.JAVASCRIPT, 0); | |
310 this.speedometer_.reset(); | |
311 this.flock_.resetFlock(newFlockSize, initialLocation); | |
312 this.invokeNaClMethod('resetFlock', {'size' : newFlockSize}); | |
313 this.updateSpeedDifference_(); | |
314 } | |
315 | |
316 /** | |
317 * Toggle the simulation mode. | |
318 * @param {!goog.events.Event} changeEvent The CHANGE event that triggered this | |
319 * handler. | |
320 */ | |
321 FlockingGeese.prototype.toggleSimMode = function(changeEvent) { | |
322 changeEvent.stopPropagation(); | |
323 var simModeObject = | |
324 this.simModeSlider_.objectAtStepValue(changeEvent.stepValue); | |
325 var simMode = simModeObject.simMode; | |
326 if (simMode == this.simulationMode_) | |
327 return; | |
328 this.simulationMode_ = simMode; | |
329 if (simMode == FlockingGeese.SimulationMode.NACL) { | |
330 // When toggling to 'NaCl', turn off the JavaScript simulation timer and | |
331 // swap the DOM elements to display the NaCl implementation. | |
332 this.stopJavaScriptSimulation_(); | |
333 var flockingCanvas = | |
334 document.getElementById(FlockingGeese.DomIds.GOOSE_CANVAS); | |
335 flockingCanvas.style.visibility = 'hidden'; | |
336 var naclView = | |
337 document.getElementById(FlockingGeese.DomIds.NACL_VIEW); | |
338 naclView.style.visibility = 'visible'; | |
339 // Start up the NaCl simulation. | |
340 this.invokeNaClMethod('runSimulation'); | |
341 } else { | |
342 // In the 'on' state, the simulation runs the NaCl implementation. | |
343 this.invokeNaClMethod('pauseSimulation'); | |
344 this.startJavaScriptSimulation_(); | |
345 var flockingCanvas = | |
346 document.getElementById(FlockingGeese.DomIds.GOOSE_CANVAS); | |
347 flockingCanvas.style.visibility = 'visible'; | |
348 var naclView = | |
349 document.getElementById(FlockingGeese.DomIds.NACL_VIEW); | |
350 naclView.style.visibility = 'hidden'; | |
351 } | |
352 } | |
353 | |
354 /** | |
355 * Format a method invocation and call postMessage with the formatted method | |
356 * string. This calls the NaCl module with the invocation string. Note that | |
357 * this is an asynchronous call into the NaCl module. | |
358 * @param {!string} methodName The name of the method. This must match a | |
359 * published method name in the NaCl module. | |
360 * @param {?Object} parameters A dictionary that maps parameter names to | |
361 * values. All parameter values are passed a strings. | |
362 */ | |
363 FlockingGeese.prototype.invokeNaClMethod = | |
364 function(methodName, opt_parameters) { | |
365 if (!this.naclModule_) { | |
366 return; | |
367 } | |
368 var methodInvocation = methodName | |
369 if (opt_parameters) { | |
370 for (param in opt_parameters) { | |
371 methodInvocation += ' ' + param + ':' + opt_parameters[param] | |
372 } | |
373 } | |
374 this.naclModule_.postMessage(methodInvocation); | |
375 } | |
376 | |
377 /** | |
378 * Handle a message posted by the NaCl module. An incomming NaCl message is | |
379 * formatted like the outgoing message in invokeNaClMethod() (see above). | |
380 * There is a method name, followed by a space-separated list of named | |
381 * parameters. The parameters are separated from their values by a ':'. | |
382 * @param {Event} messageEvent The Event wrapping the message. | |
383 */ | |
384 FlockingGeese.prototype.handleNaClMessage = function(messageEvent) { | |
385 var methodInvocation = messageEvent.data.split(' '); | |
386 // The first element of |methodInvocation| is the method name. Use this as | |
387 // the key to look up the rest of the signature. | |
388 if (methodInvocation[0] == this.MethodSignatures_.SET_SIMULATION_INFO) { | |
389 for (var paramCount = 1; | |
390 paramCount < methodInvocation.length; | |
391 paramCount++) { | |
392 var parameter = methodInvocation[paramCount].split(':'); | |
393 // The first element is the parameter name, the second element is its | |
394 // value. | |
395 if (parameter[0] == this.MethodSignatures_.FRAME_RATE) { | |
396 var frameRate = parseFloat(parameter[1]); | |
397 this.speedometer_.updateMeterNamed(FlockingGeese.MeterNames.NACL, | |
398 frameRate); | |
399 this.updateSpeedDifference_(); | |
400 } | |
401 } | |
402 } else { | |
403 var error = 'Unrecognized NaCl message: ' + messageEvent.data; | |
404 console.log(error); | |
405 throw new Error(error); | |
406 } | |
407 } | |
408 | |
409 /** | |
410 * The run() method starts and 'runs' the FlockingGeese app. An <EMBED> tag is | |
411 * injected into the <DIV> element |opt_viewDivName| which causes the NaCl | |
412 * module to be loaded. Once loaded, the moduleDidLoad() method is called via | |
413 * a 'load' event handler that is attached to the <DIV> element. | |
414 * @param {?String} opt_viewDivName The id of a DOM element in which to | |
415 * embed the NaCl module. If unspecified, defaults to DomIds.VIEW. The | |
416 * DOM element must exist. | |
417 */ | |
418 FlockingGeese.prototype.run = function(opt_viewDivName) { | |
419 var viewDivName = opt_viewDivName || FlockingGeese.DomIds.NACL_VIEW; | |
420 var viewDiv = document.getElementById(viewDivName); | |
421 this.assert(viewDiv, "Missing DOM element '" + viewDivName + "'"); | |
422 | |
423 this.initializeApplication(); | |
424 | |
425 // A small handler for the 'load' event. It stops propagation of the 'load' | |
426 // event and then calls moduleDidLoad(). The handler is bound to |this| so | |
427 // that the calling context of the closure makes |this| point to this | |
428 // instance of the Applicaiton object. | |
429 var loadEventHandler = function(loadEvent) { | |
430 this.moduleDidLoad(FlockingGeese.DomIds.NACL_MODULE); | |
431 } | |
432 | |
433 // Note that the <EMBED> element is wrapped inside a <DIV>, which has a 'load' | |
434 // event listener attached. This method is used instead of attaching the | |
435 // 'load' event listener directly to the <EMBED> element to ensure that the | |
436 // listener is active before the NaCl module 'load' event fires. | |
437 viewDiv.addEventListener('load', goog.bind(loadEventHandler, this), true); | |
438 // Attach a listener for messages coming from the NaCl module. | |
439 viewDiv.addEventListener('message', | |
440 goog.bind(this.handleNaClMessage, this), | |
441 true); | |
442 // Handle the load progress. | |
443 viewDiv.addEventListener( | |
444 'progress', | |
445 goog.bind(this.progressBar_.handleProgressEvent, this.progressBar_), | |
446 true); | |
447 | |
448 var viewSize = goog.style.getSize(viewDiv); | |
449 viewDiv.appendChild(this.progressBar_.createDom()); | |
450 var naclDom = goog.dom.createDom('embed', { | |
451 'id': FlockingGeese.DomIds.NACL_MODULE, | |
452 'class': 'autosize-view', | |
453 'width': viewSize.width, | |
454 'height': viewSize.height, | |
455 'src': 'flocking_geese.nmf', | |
456 'type': 'application/x-nacl', | |
457 'style': 'visibility: inherit' | |
458 }); | |
459 viewDiv.appendChild(naclDom); | |
460 this.startJavaScriptSimulation_(); | |
461 } | |
462 | |
463 /** | |
464 * Run one tick of the simulation and then render the new flock. Update the | |
465 * speedometer with new timing values. | |
466 */ | |
467 FlockingGeese.prototype.simulationTick = function() { | |
468 var flockingCanvas = | |
469 document.getElementById(FlockingGeese.DomIds.GOOSE_CANVAS); | |
470 // Clear the canvas | |
471 var context2d = flockingCanvas.getContext('2d'); | |
472 var flockBox = new goog.math.Rect(0, | |
473 0, | |
474 flockingCanvas.width, | |
475 flockingCanvas.height); | |
476 context2d.fillStyle = 'white'; | |
477 context2d.fillRect(flockBox.top, | |
478 flockBox.left, | |
479 flockBox.width, | |
480 flockBox.height); | |
481 var frameRate = this.flock_.flock(flockBox); | |
482 this.flock_.render(flockingCanvas); | |
483 this.speedometer_.updateMeterNamed(FlockingGeese.MeterNames.JAVASCRIPT, | |
484 frameRate); | |
485 this.updateSpeedDifference_(); | |
486 | |
487 if (this.javascriptSimRunning_) { | |
488 var self = this; | |
489 window.webkitRequestAnimationFrame(function() { | |
490 self.simulationTick(); | |
491 }); | |
492 } | |
493 } | |
494 | |
495 /** | |
496 * Shut down the application instance. This unhooks all the event listeners | |
497 * and deletes the objects created in moduleDidLoad(). | |
498 */ | |
499 FlockingGeese.prototype.terminate = function() { | |
500 goog.events.removeAll(); | |
501 this.stopJavaScriptSimulation_(); | |
502 } | |
503 | |
504 /** | |
505 * Handle a mousedown on the application view. | |
506 * @param {Event} mouseEvent The event that triggered this handler. | |
507 */ | |
508 FlockingGeese.prototype.mouseDown_ = function(mouseEvent) { | |
509 mouseEvent.preventDefault(); | |
510 mouseEvent.stopPropagation(); | |
511 // Listen for mouse-move and mouse-up events. Dragging stops when the | |
512 // mouse-up event arrives. | |
513 if (this.mouseMoveListener_ == null) { | |
514 this.mouseMoveListener_ = goog.events.listen( | |
515 this.container_, | |
516 goog.events.EventType.MOUSEMOVE, | |
517 this.mouseMove_, true, this); | |
518 } | |
519 if (this.mouseUpListener_ == null) { | |
520 this.mouseUpListener_ = goog.events.listen( | |
521 window, | |
522 goog.events.EventType.MOUSEUP, | |
523 this.mouseUp_, true, this); | |
524 } | |
525 this.setAttractorFromEvent_(mouseEvent); | |
526 } | |
527 | |
528 /** | |
529 * Handle a mousemove on the application view. | |
530 * @param {Event} mouseEvent The event that triggered this handler. | |
531 */ | |
532 FlockingGeese.prototype.mouseMove_ = function(mouseEvent) { | |
533 this.setAttractorFromEvent_(mouseEvent); | |
534 } | |
535 | |
536 /** | |
537 * Handle a mouseup on the application view. | |
538 * @param {Event} mouseEvent The event that triggered this handler. | |
539 */ | |
540 FlockingGeese.prototype.mouseUp_ = function(mouseEvent) { | |
541 if (this.mouseMoveListener_ != null) { | |
542 goog.events.unlistenByKey(this.mouseMoveListener_); | |
543 this.mouseMoveListener_ = null; | |
544 } | |
545 if (this.mouseUpListener_ != null) { | |
546 goog.events.unlistenByKey(this.mouseUpListener_); | |
547 this.mouseUpListener_ = null; | |
548 } | |
549 } | |
550 | |
551 /** | |
552 * Set the attractor position from the coordinates in a browser event. | |
553 * @param {Event} event The event. | |
554 */ | |
555 FlockingGeese.prototype.setAttractorFromEvent_ = function(event) { | |
556 var origin = goog.style.getPageOffset(this.container_); | |
557 var attractor = new goog.math.Vec2(event.clientX - origin.x, | |
558 event.clientY - origin.y); | |
559 if (this.flock_.attractors.length == 0) { | |
560 this.flock_.attractors.push(attractor); | |
561 } | |
562 this.flock_.attractors[0] = attractor; | |
563 this.invokeNaClMethod('setAttractorLocation x:' + attractor.x + | |
564 ' y:' + attractor.y); | |
565 } | |
566 | |
567 /** | |
568 * Start up the JavaScript implementation of the simulation by enabling the | |
569 * simulation timer. | |
570 * @private | |
571 */ | |
572 FlockingGeese.prototype.startJavaScriptSimulation_ = function() { | |
573 // Start up the JavaScript implementation of the simulation. | |
574 if (this.javascriptSimRunning_) | |
575 return; // Don't run a tick if the sim is already running. | |
576 this.javascriptSimRunning_ = true; | |
577 this.simulationTick(); | |
578 } | |
579 | |
580 /** | |
581 * Stop the JavaScript implementation of the simulation by clearing the | |
582 * simulation timer. | |
583 * @private | |
584 */ | |
585 FlockingGeese.prototype.stopJavaScriptSimulation_ = function() { | |
586 // Stop the JavaScript implementation of the simulation. | |
587 this.javascriptSimRunning_ = false; | |
588 } | |
589 | |
590 /** | |
591 * Recompute the speed difference between JavaScript and Native Client. | |
592 * Color the result depending on which is faster. | |
593 * @private | |
594 */ | |
595 FlockingGeese.prototype.updateSpeedDifference_ = function() { | |
596 var speedDifferenceLabel = | |
597 document.getElementById(FlockingGeese.DomIds.SPEED_DIFFERENCE_LABEL); | |
598 var naclSpeed = this.speedometer_.valueForMeterNamed( | |
599 FlockingGeese.MeterNames.NACL, | |
600 Speedometer.Attributes.ODOMETER_DISPLAY_VALUE); | |
601 var jsSpeed = this.speedometer_.valueForMeterNamed( | |
602 FlockingGeese.MeterNames.JAVASCRIPT, | |
603 Speedometer.Attributes.ODOMETER_DISPLAY_VALUE); | |
604 if (naclSpeed == 0 || jsSpeed == 0) { | |
605 speedDifferenceLabel.innerHTML = 'Not available…'; | |
606 speedDifferenceLabel.style.fontStyle = 'italic'; | |
607 speedDifferenceLabel.style.color = '#949596'; | |
608 } else { | |
609 var diff = naclSpeed / jsSpeed; | |
610 speedDifferenceLabel.innerHTML = diff.toFixed(1) + 'X'; | |
611 speedDifferenceLabel.style.fontStyle = 'normal'; | |
612 speedDifferenceLabel.style.color = diff > 1.0 ? '#009933' : '#FF0033'; | |
613 } | |
614 } | |
615 | |
616 /** | |
617 * Find the center point of the simulation canvas. If the a canvas element | |
618 * with id |GOOSE_CANVAS| doesn't exist, return (0, 0). | |
619 * @return {goog.math.Vec2} The center of the simulation canvas, measured in | |
620 * pixels. | |
621 * @private | |
622 */ | |
623 FlockingGeese.prototype.getCanvasCenter_ = function() { | |
624 var flockingCanvas = | |
625 document.getElementById(FlockingGeese.DomIds.GOOSE_CANVAS); | |
626 if (flockingCanvas) | |
627 return new goog.math.Vec2(flockingCanvas.width / 2, | |
628 flockingCanvas.height / 2); | |
629 else | |
630 return new goog.math.Vec2(0, 0); | |
631 } | |
OLD | NEW |