1 /**
  2  * Copyright (C) 2009-2011 Klaus Reimer <k@ailis.de>
  3  * See LICENSE.txt for licensing information.
  4  * 
  5  * @require twodee.js
  6  */
  7 
  8 /**
  9  * Constructs a new physics model.
 10  * 
 11  * @constructor
 12  * @class
 13  * A physics model.
 14  */
 15 twodee.Physics = function()
 16 {
 17     this.velocity = new twodee.Vector();
 18     this.acceleration = new twodee.Vector();
 19     twodee.Physics.counter++;
 20 };
 21 
 22 /**
 23  * Instance counter.
 24  * 
 25  * @type {number}
 26  */
 27 twodee.Physics.counter = 0;
 28 
 29 /** 
 30  * A static temporary vector for speed optimization.
 31  * 
 32  * @private
 33  * @type {twodee.Vector}
 34  */
 35 twodee.Physics.V = new twodee.Vector();
 36 
 37 /**
 38  * The velocity vector. Length is units per second.
 39  * 
 40  * @private
 41  * @type {twodee.Vector}
 42  */
 43 twodee.Physics.prototype.velocity = null;
 44 
 45 /**
 46  * The minimum velocity in units per second.
 47  * 
 48  * @private
 49  * @type {number}
 50  */
 51 twodee.Physics.prototype.minVelocity = Number.NEGATIVE_INFINITY;
 52 
 53 /**
 54  * The maximum velocity in units per second.
 55  * 
 56  * @private
 57  * @type {number}
 58  */
 59 twodee.Physics.prototype.maxVelocity = Number.POSITIVE_INFINITY;
 60 
 61 /**
 62  * The acceleration vector.Length is units per square second.
 63  * 
 64  * @private
 65  * @type {twodee.Vector}
 66  */
 67 twodee.Physics.prototype.acceleration = null;
 68 
 69 /**
 70  * The spin in anti-clockwise RAD per second.
 71  * 
 72  * @private
 73  * @type {number}
 74  */
 75 twodee.Physics.prototype.spin = 0;
 76 
 77 /**
 78  * The spin acceleration in anti-clockwise RAD per square second.
 79  * 
 80  * @private
 81  * @type {number}
 82  */
 83 twodee.Physics.prototype.spinAcceleration = 0;
 84 
 85 /** 
 86  * The minimum spin acceleration.
 87  * 
 88  * @private
 89  * @type {number}
 90  */
 91 twodee.Physics.prototype.minSpin = Number.NEGATIVE_INFINITY;
 92 
 93 /** 
 94  * The maximum spin acceleration. 
 95  * 
 96  * @private
 97  * @type {number} 
 98  */
 99 twodee.Physics.prototype.maxSpin = Number.POSITIVE_INFINITY;
100 
101 /**
102  * The scaling in multitudes per second.
103  * 
104  * @private
105  * @type {number}
106  */
107 twodee.Physics.prototype.scaling = 1;
108 
109 /** 
110  * The lifetime in seconds. 
111  * 
112  * @private
113  * @type {number} 
114  */
115 twodee.Physics.prototype.lifetime = Number.POSITIVE_INFINITY;
116 
117 /** 
118  * The decay time in seconds. 
119  * 
120  * @private
121  * @type {number} 
122  */
123 twodee.Physics.prototype.decay = 1;
124 
125 /**
126  * Returns the velocity vector. The length is units per second. There is no setter
127  * because you should modify the returned vector instead.
128  * 
129  * @return {twodee.Vector} The velocity vector. Never null
130  */
131 twodee.Physics.prototype.getVelocity = function()
132 {
133     return this.velocity;
134 };
135 
136 /**
137  * Sets the minimum velocity in units per second.
138  * 
139  * @param {number} minVelocity
140  *            The minimum velocity to set
141  * @return {twodee.Physics}
142  *            This physics instance for method chaining.
143  */
144 twodee.Physics.prototype.setMinVelocity = function(minVelocity)
145 {
146     this.minVelocity = minVelocity;
147     return this;
148 };
149 
150 /**
151  * Returns the minimum velocity in units per second.
152  * 
153  * @return {number} The minimum velocity
154  */
155 twodee.Physics.prototype.getMinVelocity = function()
156 {
157     return this.minVelocity;
158 };
159 
160 /**
161  * Sets the maximum velocity in units per second.
162  * 
163  * @param {number} maxVelocity
164  *            The maximum velocity to set
165  * @return {twodee.Physics}
166  *            This physics instance for method chaining.
167  */
168 twodee.Physics.prototype.setMaxVelocity = function(maxVelocity)
169 {
170     this.maxVelocity = maxVelocity;
171     return this;
172 };
173 
174 /**
175  * Returns the maximum velocity in units per second.
176  * 
177  * @return {number} The maximum velocity
178  */
179 twodee.Physics.prototype.getMaxVelocity = function()
180 {
181     return this.maxVelocity;
182 };
183 
184 /**
185  * Returns the acceleration vector. The length is units per square second.
186  * There is no setter because you should modify the returned vector instead.
187  * 
188  * @return {twodee.Vector} The acceleration vector. Never null
189  */
190 twodee.Physics.prototype.getAcceleration= function()
191 {
192     return this.acceleration;
193 };
194 
195 /**
196  * Returns the spin in anti-clockwise RAD per second.
197  * 
198  * @return {number} The current spin
199  */
200 twodee.Physics.prototype.getSpin = function()
201 {
202     return this.spin;
203 };
204 
205 /**
206  * Sets the spin in anti-clockwise RAD per second.
207  * 
208  * @param {number} spin
209  *            The spin to set
210  * @return {twodee.Physics}
211  *            This physics instance for method chaining.
212  */
213 twodee.Physics.prototype.setSpin = function(spin)
214 {
215     this.spin = spin;
216     return this;
217 };
218 
219 /**
220  * Returns the spin acceleration in anti-clockwise RAD per square second.
221  * 
222  * @return {number} The current spin acceleration
223  */
224 twodee.Physics.prototype.getSpinAcceleration = function()
225 {
226     return this.spinAcceleration;
227 };
228 
229 /**
230  * Sets the spin acceleration in anti-clockwise RAD per square second.
231  * 
232  * @param {number} spinAcceleration
233  *            The spin acceleration to set
234  * @return {twodee.Physics}
235  *            This physics instance for method chaining.
236  */
237 twodee.Physics.prototype.setSpinAcceleration = function(spinAcceleration)
238 {
239     this.spinAcceleration = spinAcceleration;
240     return this;
241 };
242 
243 /**
244  * Returns the minimum spin in anti-clockwise RAD per second.
245  * 
246  * @return {number} The minimum spin
247  */
248 twodee.Physics.prototype.getMinSpin = function()
249 {
250     return this.minSpin;
251 };
252 
253 /**
254  * Sets the minimum spin in anti-clockwise RAD per second.
255  * 
256  * @param {number} minSpin
257  *            The minimum spin to set
258  * @return {twodee.Physics}
259  *            This physics instance for method chaining.
260  */
261 twodee.Physics.prototype.setMinSpin = function(minSpin)
262 {
263     this.minSpin = minSpin;
264     return this;
265 };
266 
267 /**
268  * Returns the maximum spin in anti-clockwise RAD per second.
269  * 
270  * @return {number} The maximum spin
271  */
272 twodee.Physics.prototype.getMaxSpin = function()
273 {
274     return this.maxSpin;
275 };
276 
277 /**
278  * Sets the maximum spin in anti-clockwise RAD per second.
279  * 
280  * @param {number} maxSpin
281  *            The maximum spin to set
282  * @return {twodee.Physics}
283  *            This physics instance for method chaining.
284  */
285 twodee.Physics.prototype.setMaxSpin = function(maxSpin)
286 {
287     this.maxSpin = maxSpin;
288     return this;
289 };
290 
291 /**
292  * Returns the scaling in multitudes per second.
293  * 
294  * @return {number} The current scaling
295  */
296 twodee.Physics.prototype.getScaling = function()
297 {
298     return this.scaling;
299 };
300 
301 /**
302  * Sets the scaling in multitudes per second.
303  * 
304  * @param {number} scaling
305  *            The scaling to set
306  * @return {twodee.Physics}
307  *            This physics instance for method chaining.
308  */
309 twodee.Physics.prototype.setScaling = function(scaling)
310 {
311     this.scaling = scaling;
312     return this;
313 };
314 
315 /**
316  * Returns the lifetime in seconds. May return Infinity.
317  * 
318  * @return {number} The lifetime
319  */
320 twodee.Physics.prototype.getLifetime = function()
321 {
322     return this.lifetime;
323 };
324 
325 /**
326  * Sets the lifetime in seconds. Default value is Infinity.
327  * 
328  * @param {number} lifetime
329  *            The lifetime to set
330  * @return {twodee.Physics}
331  *            This physics instance for method chaining.
332  */
333 twodee.Physics.prototype.setLifetime = function(lifetime)
334 {
335     this.lifetime = lifetime;
336     return this;
337 };
338 
339 /**
340  * Sets the decay time in seconds. Default value is 1 second. Decay only
341  * makes sense when a life time has been set. Example: Set lifetime to 10
342  * seconds and decay to 2 seconds. The scene node will start fading away
343  * (Decreasing the opacity) at 8 seconds and will be invisible and then
344  * removed at 10 seconds.
345  * 
346  * @param {number} decay
347  *            The decay time in seconds
348  * @return {twodee.Physics}
349  *            This physics instance for method chaining.
350  */
351 twodee.Physics.prototype.setDecay = function(decay)
352 {
353     this.decay = decay;
354     return this;
355 };
356 
357 /**
358  * Returns the decay time in seconds.
359  * 
360  * @return {number} The decay time in seconds
361  */
362 twodee.Physics.prototype.getDecay = function()
363 {
364     return this.decay;
365 };
366 
367 /**
368  * Processes the physics model for the specified node and time delta.
369  * 
370  * @param {twodee.SceneNode} node
371  *            The scene node to update
372  * @param {number} delta
373  *            The time delta 
374  */
375 twodee.Physics.prototype.process = function(node, delta)
376 {
377     var spin, transform, velocity, factor, angle, v, acceleration,
378         spinAcceleration, curVelocity, maxVelocity, minVelocity, lifetime,
379         decay, scaling;
380     
381     factor = delta / 1000;
382 
383     // Process the lifetime
384     this.lifetime = lifetime = Math.max(0, this.lifetime - factor);
385     if (!lifetime)
386     {
387         node.remove();
388         return;
389     }
390     
391     // Process the decay
392     decay = this.decay;
393     if (decay > lifetime) node.setOpacity(lifetime / decay);
394 
395     // Get the current node transform and a temporary vector
396     transform = node.getTransform();    
397     v = twodee.Physics.V;
398 
399     // Process the velocity
400     velocity = this.velocity;
401     if (!velocity.isZero())
402     {
403         angle = transform.getRotationAngle();
404         velocity.copy(v).rotate(-angle);
405         transform.translate(v.x * factor, v.y * factor);
406     }    
407 
408     // Process the acceleration
409     acceleration = this.acceleration;
410     if (!acceleration.isZero())
411     {
412         velocity.add(acceleration.copy(v).scale(factor));
413         curVelocity = velocity.getLength();
414         maxVelocity = this.maxVelocity;
415         minVelocity = this.minVelocity;
416         if (curVelocity > maxVelocity)
417             velocity.scale(maxVelocity / curVelocity);
418         else if (curVelocity < minVelocity)
419             velocity.scale(minVelocity / curVelocity);
420     }
421         
422     // Process the spinning
423     spin = this.spin;
424     if (spin) transform.rotate(spin * factor);
425     
426     // Process the scaling
427     scaling = this.scaling;
428     if (scaling != 1) transform.scale(Math.pow(scaling, factor));
429 
430     // Process the spin acceleration
431     spinAcceleration = this.spinAcceleration;
432     if (spinAcceleration)
433         this.spin = Math.max(this.minSpin, Math.min(this.maxSpin, spin +
434             spinAcceleration * factor));
435 };
436