1 /**
  2 * Matrix.js v1.1.0
  3 * 
  4 * Copyright (c) 2010 STRd6
  5 *
  6 * Permission is hereby granted, free of charge, to any person obtaining a copy
  7 * of this software and associated documentation files (the "Software"), to deal
  8 * in the Software without restriction, including without limitation the rights
  9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10 * copies of the Software, and to permit persons to whom the Software is
 11 * furnished to do so, subject to the following conditions:
 12 *
 13 * The above copyright notice and this permission notice shall be included in
 14 * all copies or substantial portions of the Software.
 15 *
 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22 * THE SOFTWARE.
 23 *
 24 * Loosely based on flash:
 25 * http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/geom/Matrix.html
 26 */
 27 (function() {
 28   /**
 29    * Create a new point with given x and y coordinates. If no arguments are given
 30    * defaults to (0, 0).
 31    * @name Point
 32    * @param {Number} [x]
 33    * @param {Number} [y]
 34    * @constructor
 35    */
 36   function Point(x, y) {
 37     return {
 38       /**
 39        * The x coordinate of this point.
 40        * @name x
 41        * @fieldOf Point#
 42        */
 43       x: x || 0,
 44       /**
 45        * The y coordinate of this point.
 46        * @name y
 47        * @fieldOf Point#
 48        */
 49       y: y || 0,
 50       /**
 51        * Adds a point to this one and returns the new point.
 52        * @name add
 53        * @methodOf Point#
 54        *
 55        * @param {Point} other The point to add this point to.
 56        * @returns A new point, the sum of both.
 57        * @type Point
 58        */
 59       add: function(other) {
 60         return Point(this.x + other.x, this.y + other.y);
 61       }
 62     }
 63   }
 64 
 65   /**
 66    * @param {Point} p1
 67    * @param {Point} p2
 68    * @returns The Euclidean distance between two points.
 69    */
 70   Point.distance = function(p1, p2) {
 71     return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
 72   };
 73 
 74   /**
 75    * If you have two dudes, one standing at point p1, and the other
 76    * standing at point p2, then this method will return the direction
 77    * that the dude standing at p1 will need to face to look at p2.
 78    * @param {Point} p1 The starting point.
 79    * @param {Point} p2 The ending point.
 80    * @returns The direction from p1 to p2 in radians.
 81    */
 82   Point.direction = function(p1, p2) {
 83     return Math.atan2(
 84       p2.y - p1.y,
 85       p2.x - p1.x
 86     );
 87   }
 88 
 89   /**
 90    * <pre>
 91    *  _        _
 92    * | a  c tx  |
 93    * | b  d ty  |
 94    * |_0  0  1 _|
 95    * </pre>
 96    * Creates a matrix for 2d affine transformations.
 97    *
 98    * concat, inverse, rotate, scale and translate return new matrices with the
 99    * transformations applied. The matrix is not modified in place.
100    *
101    * Returns the identity matrix when called with no arguments.
102    * @name Matrix
103    * @param {Number} [a]
104    * @param {Number} [b]
105    * @param {Number} [c]
106    * @param {Number} [d]
107    * @param {Number} [tx]
108    * @param {Number} [ty]
109    * @constructor
110    */
111   function Matrix(a, b, c, d, tx, ty) {
112     a = a !== undefined ? a : 1;
113     d = d !== undefined ? d : 1;
114 
115     return {
116       /**
117        * @name a
118        * @fieldOf Matrix#
119        */
120       a: a,
121       /**
122        * @name b
123        * @fieldOf Matrix#
124        */
125       b: b || 0,
126       /**
127        * @name c
128        * @fieldOf Matrix#
129        */
130       c: c || 0,
131       /**
132        * @name d
133        * @fieldOf Matrix#
134        */
135       d: d,
136       /**
137        * @name tx
138        * @fieldOf Matrix#
139        */
140       tx: tx || 0,
141       /**
142        * @name ty
143        * @fieldOf Matrix#
144        */
145       ty: ty || 0,
146 
147       /**
148        * Returns the result of this matrix multiplied by another matrix
149        * combining the geometric effects of the two. In mathematical terms, 
150        * concatenating two matrixes is the same as combining them using matrix multiplication.
151        * If this matrix is A and the matrix passed in is B, the resulting matrix is A x B
152        * http://mathworld.wolfram.com/MatrixMultiplication.html
153        * @name concat
154        * @methodOf Matrix#
155        *
156        * @param {Matrix} matrix The matrix to multiply this matrix by.
157        * @returns The result of the matrix multiplication, a new matrix.
158        * @type Matrix
159        */
160       concat: function(matrix) {
161         return Matrix(
162           this.a * matrix.a + this.c * matrix.b,
163           this.b * matrix.a + this.d * matrix.b,
164           this.a * matrix.c + this.c * matrix.d,
165           this.b * matrix.c + this.d * matrix.d,
166           this.a * matrix.tx + this.c * matrix.ty + this.tx,
167           this.b * matrix.tx + this.d * matrix.ty + this.ty
168         );
169       },
170 
171       /**
172        * Given a point in the pretransform coordinate space, returns the coordinates of 
173        * that point after the transformation occurs. Unlike the standard transformation 
174        * applied using the transformPoint() method, the deltaTransformPoint() method's 
175        * transformation does not consider the translation parameters tx and ty.
176        * @name deltaTransformPoint
177        * @methodOf Matrix#
178        * @see #transformPoint
179        *
180        * @return A new point transformed by this matrix ignoring tx and ty.
181        * @type Point
182        */
183       deltaTransformPoint: function(point) {
184         return Point(
185           this.a * point.x + this.c * point.y,
186           this.b * point.x + this.d * point.y
187         );
188       },
189 
190       /**
191        * Returns the inverse of the matrix.
192        * http://mathworld.wolfram.com/MatrixInverse.html
193        * @name inverse
194        * @methodOf Matrix#
195        *
196        * @returns A new matrix that is the inverse of this matrix.
197        * @type Matrix
198        */
199       inverse: function() {
200         var determinant = this.a * this.d - this.b * this.c;
201         return Matrix(
202           this.d / determinant,
203           -this.b / determinant,
204           -this.c / determinant,
205           this.a / determinant,
206           (this.c * this.ty - this.d * this.tx) / determinant,
207           (this.b * this.tx - this.a * this.ty) / determinant
208         );
209       },
210 
211       /**
212        * Returns a new matrix that corresponds this matrix multiplied by a
213        * a rotation matrix.
214        * @name rotate
215        * @methodOf Matrix#
216        * @see Matrix.rotation
217        *
218        * @param {Number} theta Amount to rotate in radians.
219        * @param {Point} [aboutPoint] The point about which this rotation occurs. Defaults to (0,0).
220        * @returns A new matrix, rotated by the specified amount.
221        * @type Matrix
222        */
223       rotate: function(theta, aboutPoint) {
224         return this.concat(Matrix.rotation(theta, aboutPoint));
225       },
226 
227       /**
228        * Returns a new matrix that corresponds this matrix multiplied by a
229        * a scaling matrix.
230        * @name scale
231        * @methodOf Matrix#
232        * @see Matrix.scale
233        *
234        * @param {Number} sx
235        * @param {Number} [sy]
236        * @param {Point} [aboutPoint] The point that remains fixed during the scaling
237        * @type Matrix
238        */
239       scale: function(sx, sy, aboutPoint) {
240         return this.concat(Matrix.scale(sx, sy, aboutPoint));
241       },
242 
243       /**
244        * Returns the result of applying the geometric transformation represented by the 
245        * Matrix object to the specified point.
246        * @name transformPoint
247        * @methodOf Matrix#
248        * @see #deltaTransformPoint
249        *
250        * @returns A new point with the transformation applied.
251        * @type Point
252        */
253       transformPoint: function(point) {
254         return Point(
255           this.a * point.x + this.c * point.y + this.tx,
256           this.b * point.x + this.d * point.y + this.ty
257         );
258       },
259 
260       /**
261        * Translates the matrix along the x and y axes, as specified by the tx and ty parameters.
262        * @name translate
263        * @methodOf Matrix#
264        * @see Matrix.translation
265        *
266        * @param {Number} tx The translation along the x axis.
267        * @param {Number} ty The translation along the y axis.
268        * @returns A new matrix with the translation applied.
269        * @type Matrix
270        */
271       translate: function(tx, ty) {
272         return this.concat(Matrix.translation(tx, ty));
273       }
274     }
275   }
276 
277   /**
278    * Creates a matrix transformation that corresponds to the given rotation,
279    * around (0,0) or the specified point.
280    * @see Matrix#rotate
281    *
282    * @param {Number} theta Rotation in radians.
283    * @param {Point} [aboutPoint] The point about which this rotation occurs. Defaults to (0,0).
284    * @returns 
285    * @type Matrix
286    */
287   Matrix.rotation = function(theta, aboutPoint) {
288     var rotationMatrix = Matrix(
289       Math.cos(theta),
290       Math.sin(theta),
291       -Math.sin(theta),
292       Math.cos(theta)
293     );
294 
295     if(aboutPoint) {
296       rotationMatrix =
297         Matrix.translation(aboutPoint.x, aboutPoint.y).concat(
298           rotationMatrix
299         ).concat(
300           Matrix.translation(-aboutPoint.x, -aboutPoint.y)
301         );
302     }
303 
304     return rotationMatrix;
305   };
306 
307   /**
308    * Returns a matrix that corresponds to scaling by factors of sx, sy along
309    * the x and y axis respectively.
310    * If only one parameter is given the matrix is scaled uniformly along both axis.
311    * If the optional aboutPoint parameter is given the scaling takes place
312    * about the given point.
313    * @see Matrix#scale
314    *
315    * @param {Number} sx The amount to scale by along the x axis or uniformly if no sy is given.
316    * @param {Number} [sy] The amount to scale by along the y axis.
317    * @param {Point} [aboutPoint] The point about which the scaling occurs. Defaults to (0,0).
318    * @returns A matrix transformation representing scaling by sx and sy.
319    * @type Matrix
320    */
321   Matrix.scale = function(sx, sy, aboutPoint) {
322     sy = sy || sx;
323 
324     var scaleMatrix = Matrix(sx, 0, 0, sy);
325 
326     if(aboutPoint) {
327       scaleMatrix =
328         Matrix.translation(aboutPoint.x, aboutPoint.y).concat(
329           scaleMatrix
330         ).concat(
331           Matrix.translation(-aboutPoint.x, -aboutPoint.y)
332         );
333     }
334 
335     return scaleMatrix;
336   };
337 
338   /**
339    * Returns a matrix that corresponds to a translation of tx, ty.
340    * @see Matrix#translate
341    *
342    * @param {Number} tx The amount to translate in the x direction.
343    * @param {Number} ty The amount to translate in the y direction.
344    * @return A matrix transformation representing a translation by tx and ty.
345    * @type Matrix
346    */
347   Matrix.translation = function(tx, ty) {
348     return Matrix(1, 0, 0, 1, tx, ty);
349   };
350 
351   /**
352    * A constant representing the identity matrix.
353    * @name IDENTITY
354    * @fieldOf Matrix
355    */
356   Matrix.IDENTITY = Matrix();
357   /**
358    * A constant representing the horizontal flip transformation matrix.
359    * @name HORIZONTAL_FLIP
360    * @fieldOf Matrix
361    */
362   Matrix.HORIZONTAL_FLIP = Matrix(-1, 0, 0, 1);
363   /**
364    * A constant representing the vertical flip transformation matrix.
365    * @name VERTICAL_FLIP
366    * @fieldOf Matrix
367    */
368   Matrix.VERTICAL_FLIP = Matrix(1, 0, 0, -1);
369   
370   // Export to window
371   window["Point"] = Point;
372   window["Matrix"] = Matrix;
373 }());
374