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