1 /**
  2  * Copyright (C) 2009-2011 Klaus Reimer <k@ailis.de>
  3  * See LICENSE.txt for licensing information.
  4  * 
  5  * @require twodee.js
  6  * @require twodee/Vector.js
  7  */
  8 
  9 /**
 10  * Constructs a new matrix initialized as an identity matrix.
 11  * 
 12  * @constructor
 13  * @class
 14  * A matrix with 3x3 entries.
 15  */
 16 twodee.Matrix = function()
 17 {
 18     twodee.Matrix.counter++;
 19     this.translation = new twodee.Vector();
 20 };
 21 
 22 /**
 23  * Instance counter.
 24  * 
 25  * @type {number}
 26  */
 27 twodee.Matrix.counter = 0;
 28 
 29 /** 
 30  * A temporary matrix for internal operations.
 31  * 
 32  * @private
 33  * @type {twodee.Matrix}
 34  */
 35 twodee.Matrix.TMP = new twodee.Matrix();
 36 
 37 /**
 38  * Cached translation vector.
 39  * @private
 40  * @type {twodee.Vector}
 41  */
 42 twodee.Matrix.prototype.translation;
 43 
 44 /**
 45  * The matrix entry 0;0.
 46  * 
 47  * @type {number}
 48  */
 49 twodee.Matrix.prototype.m00 = 1;
 50 
 51 /**
 52  * The matrix entry 0;1.
 53  * 
 54  * @type {number}
 55  */
 56 twodee.Matrix.prototype.m01 = 0;
 57 
 58 /**
 59  * The matrix entry 0;2.
 60  * 
 61  * @type {number}
 62  */
 63 twodee.Matrix.prototype.m02 = 0;
 64 
 65 /**
 66  * The matrix entry 1;0.
 67  * 
 68  * @type {number}
 69  */
 70 twodee.Matrix.prototype.m10 = 0;
 71 
 72 /**
 73  * The matrix entry 1;1.
 74  * 
 75  * @type {number}
 76  */
 77 twodee.Matrix.prototype.m11 = 1;
 78 
 79 /**
 80  * The matrix entry 1;2.
 81  * 
 82  * @type {number}
 83  */
 84 twodee.Matrix.prototype.m12 = 0;
 85 
 86 /**
 87  * The matrix entry 0;0.
 88  * 
 89  * @type {number}
 90  */
 91 twodee.Matrix.prototype.m20 = 0;
 92 
 93 /**
 94  * The matrix entry 0;1.
 95  * 
 96  * @type {number}
 97  */
 98 twodee.Matrix.prototype.m21 = 0;
 99 
100 /**
101  * The matrix entry 0;2.
102  * 
103  * @type {number}
104  */
105 twodee.Matrix.prototype.m22 = 1;
106 
107 /**
108  * Returns a copy of this matrix. If a target matrix is specified then the
109  * matrix is copied into this target matrix. If not specified then a new
110  * fresh matrix is created.
111  * 
112  * @param {twodee.Matrix} target
113  *            Optional target matrix
114  * @return {twodee.Matrix} A copy of this matrix
115  */
116 twodee.Matrix.prototype.copy = function(target)
117 {
118     return (target ? target : new twodee.Matrix()).set(
119         this.m00, this.m01, this.m02,
120         this.m10, this.m11, this.m12,
121         this.m20, this.m21, this.m22);
122 };
123 
124 /**
125  * Sets the matrix entries.
126  * 
127  * @param {number} m00
128  *            The matrix entry 0;0
129  * @param {number} m01
130  *            The matrix entry 0;1
131  * @param {number} m02
132  *            The matrix entry 0;2
133  * @param {number} m10
134  *            The matrix entry 1;0
135  * @param {number} m11
136  *            The matrix entry 1;1
137  * @param {number} m12
138  *            The matrix entry 1;2
139  * @param {number} m20
140  *            The matrix entry 2;0
141  * @param {number} m21
142  *            The matrix entry 2;1
143  * @param {number} m22
144  *            The matrix entry 2;2
145  * @return {twodee.Matrix}
146  *            This matrix
147  */
148 twodee.Matrix.prototype.set = function(m00, m01, m02, m10, m11, m12,
149     m20, m21, m22)
150 {
151     this.m00 = m00;
152     this.m01 = m01;
153     this.m02 = m02;
154     this.m10 = m10;
155     this.m11 = m11;
156     this.m12 = m12;
157     this.m20 = m20;
158     this.m21 = m21;
159     this.m22 = m22;
160     return this;
161 };
162 
163 /**
164  * Sets the matrix entries.
165  * 
166  * @param {twodee.Matrix} transform
167  *            The matrix to get the entries from
168  * @return {twodee.Matrix}
169  *            This matrix
170  */
171 twodee.Matrix.prototype.setTransform = function(transform)
172 {
173     this.m00 = transform.m00;
174     this.m01 = transform.m01;
175     this.m02 = transform.m02;
176     this.m10 = transform.m10;
177     this.m11 = transform.m11;
178     this.m12 = transform.m12;
179     this.m20 = transform.m20;
180     this.m21 = transform.m21;
181     this.m22 = transform.m22;
182     return this;    
183 };
184 
185 /**
186  * Sets the entries of this matrix to an identity matrix.
187  * 
188  * @return {twodee.Matrix}
189  *             The matrix
190  */
191 twodee.Matrix.prototype.setIdentity = function()
192 {
193     return this.set(
194         1, 0, 0,
195         0, 1, 0,
196         0, 0, 1);
197 };
198 
199 /**
200  * Sets the entries of this matrix to a rotation matrix.
201  * 
202  * @param {number} angle
203  *            The rotation angle in anti-clockwise RAD
204  * @return {twodee.Matrix}
205  *            This matrix
206  */
207 twodee.Matrix.prototype.setRotate = function(angle)
208 {
209     var s, c;
210     
211     s = Math.sin(angle);
212     c = Math.cos(angle);
213     return this.set(
214         c, -s, 0,
215         s,  c, 0,
216         0,  0, 1);
217 };
218 
219 /**
220  * Returns the rotation angle in anti-clockwise RAD.
221  * 
222  * @return {number} The rotation angle in anti-clockwise RAD
223  */
224 twodee.Matrix.prototype.getRotationAngle = function()
225 {
226     return Math.atan2(this.m10, this.m00);
227 };
228 
229 /**
230  * Sets the entries of this matrix to a scaling matrix.
231  * 
232  * @param {number} fx
233  *            The X scale factor
234  * @param {number=} fy
235  *            The Y scale factor. Optional. Defaults to fx.
236  * @return {twodee.Matrix}
237  *            This matrix
238  */
239 twodee.Matrix.prototype.setScale = function(fx, fy)
240 {
241     return this.set(
242         fx, 0, 0,
243         0, fy === undefined ? fx : fy, 0,
244         0, 0, 1);
245 };
246 
247 /**
248  * Sets the entries of this matrix to a X scaling matrix.
249  * 
250  * @param {number} f
251  *            The scale factor
252  * @return {twodee.Matrix}
253  *            This matrix
254  */
255 twodee.Matrix.prototype.setScaleX = function(f)
256 {
257     return this.set(
258         f, 0, 0,
259         0, 1, 0,
260         0, 0, 1);
261 };
262 
263 /**
264  * Sets the entries of this matrix to a Y scaling matrix.
265  * 
266  * @param {number} f
267  *            The scale factor
268  * @return {twodee.Matrix}
269  *            This matrix
270  */
271 twodee.Matrix.prototype.setScaleY = function(f)
272 {
273     return this.set(
274         1, 0, 0,
275         0, f, 0,
276         0, 0, 1);
277 };
278 
279 /**
280  * Sets the entries of this matrix to a translation matrix.
281  * 
282  * @param {number} dx
283  *            The X delta
284  * @param {number} dy
285  *            The Y delta.
286  * @return {twodee.Matrix}
287  *            This matrix
288  */
289 twodee.Matrix.prototype.setTranslate = function(dx, dy)
290 {
291     return this.set(
292         1, 0, dx,
293         0, 1, dy,
294         0, 0,  1);
295 };
296 
297 /**
298  * Returns the X translation of the matrix.
299  * 
300  * @return {number} The X translation
301  */
302 twodee.Matrix.prototype.getTranslationX = function()
303 {
304     return this.m02;
305 };
306 
307 /**
308  * Returns the Y translation of the matrix.
309  * 
310  * @return {number} The Y translation
311  */
312 twodee.Matrix.prototype.getTranslationY = function()
313 {
314     return this.m12;
315 };
316 
317 /**
318  * Returns The translation vector of the current matrix. Attention! This
319  * vector is reused and updated to the current translation whenever this
320  * method is called. So you may want to clone the vector if you need it for
321  * a longer time.
322  * 
323  * @return {twodee.Vector} The translation vector of the matrix
324  */
325 twodee.Matrix.prototype.getTranslation = function()
326 {
327     return this.translation.set(this.m02, this.m12);
328 };
329 
330 /**
331  * Sets the entries of this matrix to a X translation matrix.
332  * 
333  * @param {number} d
334  *            The delta
335  * @return {twodee.Matrix}
336  *            This matrix
337  */
338 twodee.Matrix.prototype.setTranslateX = function(d)
339 {
340     return this.set(
341         1, 0, d,
342         0, 1, 0,
343         0, 0, 1);
344 };
345 
346 /**
347  * Sets the entries of this matrix to a Y translation matrix.
348  * 
349  * @param {number} d
350  *            The delta
351  * @return {twodee.Matrix}
352  *            This matrix
353  */
354 twodee.Matrix.prototype.setTranslateY = function(d)
355 {
356     return this.set(
357         1, 0, 0,
358         0, 1, d,
359         0, 0, 1);
360 };
361 
362 /**
363  * Multiplies this matrix with the specified matrix. The result is written
364  * to this matrix.
365  * 
366  * @param {twodee.Matrix} m
367  *            The other matrix
368  * @return {twodee.Matrix}
369  *            This matrix
370  */
371 twodee.Matrix.prototype.transform = function(m)
372 {
373     return this.set(
374         this.m00 * m.m00 + this.m01 * m.m10 + this.m02 * m.m20,
375         this.m00 * m.m01 + this.m01 * m.m11 + this.m02 * m.m21,
376         this.m00 * m.m02 + this.m01 * m.m12 + this.m02 * m.m22,
377 
378         this.m10 * m.m00 + this.m11 * m.m10 + this.m12 * m.m20,
379         this.m10 * m.m01 + this.m11 * m.m11 + this.m12 * m.m21,
380         this.m10 * m.m02 + this.m11 * m.m12 + this.m12 * m.m22,
381 
382         this.m20 * m.m00 + this.m21 * m.m10 + this.m22 * m.m20,
383         this.m20 * m.m01 + this.m21 * m.m11 + this.m22 * m.m21,
384         this.m20 * m.m02 + this.m21 * m.m12 + this.m22 * m.m22);
385 };
386 
387 /**
388  * Multiplies this matrix with the specified factor.
389  * 
390  * @param {number} f
391  *            The factor
392  * @return {twodee.Matrix}
393  *            This matrix
394  */
395 twodee.Matrix.prototype.multiply = function(f)
396 {
397     this.m00 *= f;
398     this.m01 *= f;
399     this.m02 *= f;
400     this.m10 *= f;
401     this.m11 *= f;
402     this.m12 *= f;
403     this.m20 *= f;
404     this.m21 *= f;
405     this.m22 *= f;
406     return this;
407 };
408 
409 /**
410  * Divides this matrix by the specified factor.
411  * 
412  * @param {number} f
413  *            The factor
414  * @return {twodee.Matrix}
415  *            This matrix
416  */
417 twodee.Matrix.prototype.divide = function(f)
418 {
419     this.m00 /= f;
420     this.m01 /= f;
421     this.m02 /= f;
422     this.m10 /= f;
423     this.m11 /= f;
424     this.m12 /= f;
425     this.m20 /= f;
426     this.m21 /= f;
427     this.m22 /= f;
428     return this;
429 };
430 
431 /**
432  * Translates this matrix by the specified deltas
433  * 
434  * @param {number} dx
435  *            The X delta
436  * @param {number} dy
437  *            The Y delta
438  * @return {twodee.Matrix}
439  *            This matrix
440  */
441 twodee.Matrix.prototype.translate = function(dx, dy)
442 {
443     return this.transform(twodee.Matrix.TMP.setTranslate(dx, dy));
444 };
445 
446 /**
447  * X-Translates this matrix by the specified delta
448  * 
449  * @param {number} d
450  *            The delta
451  * @return {twodee.Matrix}
452  *            This matrix
453  */
454 twodee.Matrix.prototype.translateX = function(d)
455 {
456     return this.transform(twodee.Matrix.TMP.setTranslateX(d));
457 };
458 
459 /**
460  * Y-Translates this matrix by the specified delta
461  * 
462  * @param {number} d
463  *            The delta
464  * @return {twodee.Matrix}
465  *            This matrix
466  */
467 twodee.Matrix.prototype.translateY = function(d)
468 {
469     return this.transform(twodee.Matrix.TMP.setTranslateY(d));
470 };
471 
472 /**
473  * Scales this matrix by the specified factors
474  * 
475  * @param {number} fx
476  *            The X factor
477  * @param {number=} fy
478  *            The Y factor. Optional. Defaults to fx.
479  * @return {twodee.Matrix}
480  *            This matrix
481  */
482 twodee.Matrix.prototype.scale = function(fx, fy)
483 {
484     return this.transform(twodee.Matrix.TMP.setScale(fx,
485         fy === undefined ? fx : fy));
486 };
487 
488 /**
489  * X-Scales this matrix by the specified factor
490  * 
491  * @param {number} f
492  *            The factor
493  * @return {twodee.Matrix}
494  *            This matrix
495  */
496 twodee.Matrix.prototype.scaleX = function(f)
497 {
498     return this.transform(twodee.Matrix.TMP.setScaleX(f));
499 };
500 
501 /**
502  * Y-Scales this matrix by the specified factor
503  * 
504  * @param {number} f
505  *            The factor
506  * @return {twodee.Matrix}
507  *            This matrix
508  */
509 twodee.Matrix.prototype.scaleY = function(f)
510 {
511     return this.transform(twodee.Matrix.TMP.setScaleY(f));
512 };
513 
514 /**
515  * Rotates this matrix by the specified angle
516  * 
517  * @param {number} r
518  *            The angle in anti-clockwise RAD
519  * @return {twodee.Matrix}
520  *            This matrix
521  */
522 twodee.Matrix.prototype.rotate = function(r)
523 {
524     return this.transform(twodee.Matrix.TMP.setRotate(r));
525 };
526 
527 /**
528  * Returns the determinant of the matrix.
529  * 
530  * @return {number} The determinant of the matrix
531  */
532 twodee.Matrix.prototype.getDeterminant = function()
533 {
534     return this.m00 * this.m11 * this.m22 + this.m01 * this.m12 * this.m20 +
535            this.m02 * this.m10 * this.m21 - this.m00 * this.m12 * this.m21 -
536            this.m01 * this.m10 * this.m22 - this.m02 * this.m11 * this.m20;
537 };
538 
539 /**
540  * Inverts this matrix.
541  * 
542  * @return {twodee.Matrix}
543  *            This matrix
544  */
545 twodee.Matrix.prototype.invert = function()
546 {
547     var d;
548     
549     d = this.getDeterminant(); 
550     return this.set(
551             this.m11*this.m22 - this.m12*this.m21,
552             this.m02*this.m21 - this.m01*this.m22,
553             this.m01*this.m12 - this.m02*this.m11,
554             this.m12*this.m20 - this.m10*this.m22,
555             this.m00*this.m22 - this.m02*this.m20,
556             this.m02*this.m10 - this.m00*this.m12,
557             this.m10*this.m21 - this.m11*this.m20,
558             this.m01*this.m20 - this.m00*this.m21,
559             this.m00*this.m11 - this.m01*this.m10).divide(d);
560 };
561