1 /** 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2016 DeNA Co., Ltd. 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 THE 22 * SOFTWARE. 23 */ 24 25 /// <reference path="base.js"/> 26 /// <reference path="point.js"/> 27 /// <reference path="bounding_box.js"/> 28 29 /** 30 * A class that represents an affine transformation compatible with the one used 31 * by the CanvasRenderingContext2D interface, e.g. a 3x3 matrix listed below: 32 * | a c tx | 33 * | b d ty | 34 * | 0 0 1 | 35 * @constructor 36 */ 37 createjs.Transform = function() { 38 }; 39 40 /** 41 * The horizontal scale. 42 * @type {number} 43 */ 44 createjs.Transform.prototype.a = 1; 45 46 /** 47 * The horizontal skew. 48 * @type {number} 49 */ 50 createjs.Transform.prototype.b = 0; 51 52 /** 53 * The vertical skew. 54 * @type {number} 55 */ 56 createjs.Transform.prototype.c = 0; 57 58 /** 59 * The vertical scale. 60 * @type {number} 61 */ 62 createjs.Transform.prototype.d = 1; 63 64 /** 65 * The horizontal translation. 66 * @type {number} 67 */ 68 createjs.Transform.prototype.tx = 0; 69 70 /** 71 * The vertical translation. 72 * @type {number} 73 */ 74 createjs.Transform.prototype.ty = 0; 75 76 /** 77 * Whether this transform is invertible. 78 * @type {number} 79 */ 80 createjs.Transform.prototype.invertible = 1; 81 82 /** 83 * Prepends the specified transform with this transform. This method multiplies 84 * this transformation matrix with the specified one as listed in the following 85 * formula. 86 * a0 = transform.a, b0 = transform.b, c0 = transform.c, d0 = transform.d, 87 * tx0 = transform.tx, ty0 = transfor.ty, 88 * a = this.a, b = this.b, c = this.c, d = this.d, tx = this.tx, ty = this.ty. 89 * | a0 c0 tx0 | | a c tz | 90 * | b0 d0 ty0 | * | b d ty | 91 * | 0 0 1 | | 0 0 1 | 92 * | a0*a+c0*b a0*c+c0*d a0*tx+c0*ty+tx0 | 93 * = | b0*a+d0*b b0*c+d0*d b0*tx+d0*ty+ty0 | 94 * | 0 0 1 | 95 * @param {createjs.Transform} transform 96 * @private 97 */ 98 createjs.Transform.prototype.prepend_ = function(transform) { 99 /// <param type="createjs.Transform" name="transform"/> 100 var a = this.a; 101 var b = this.b; 102 var c = this.c; 103 var d = this.d; 104 var tx = this.tx; 105 var ty = this.ty; 106 this.a = transform.a * a + transform.c * b; 107 this.b = transform.b * a + transform.d * b; 108 this.c = transform.a * c + transform.c * d; 109 this.d = transform.b * c + transform.d * d; 110 this.tx = transform.a * tx + transform.c * ty + transform.tx; 111 this.ty = transform.b * tx + transform.d * ty + transform.ty; 112 }; 113 114 /** 115 * Generates matrix properties from transform properties used by display 116 * objects, and appends them with this matrix. 117 * @param {createjs.Point} position 118 * @param {createjs.Point} scale 119 * @param {number} rotation 120 * @param {createjs.Point} skew 121 * @param {createjs.Point} registration 122 * @const 123 */ 124 createjs.Transform.prototype.set = 125 function(position, scale, rotation, skew, registration) { 126 /// <param type="createjs.Point" name="position"/> 127 /// <param type="createjs.Point" name="scale"/> 128 /// <param type="number" name="rotation"/> 129 /// <param type="createjs.Point" name="skew"/> 130 /// <param type="createjs.Point" name="registration"/> 131 132 // Create a scale matrix. 133 // | a c 0 | | scale.x 0 0 | 134 // | b d 0 | = | 0 scale.y 0 | 135 // | 0 0 1 | | 0 0 1 | 136 var a = scale.x; 137 var b = 0; 138 var c = 0; 139 var d = scale.y; 140 if (rotation) { 141 // Multiply a rotation matrix with a scale one as listed in the following 142 // formula. 143 // | a c 0 | | cos(r) -sin(r) 0 | | a 0 0 | 144 // | b d 0 | = | sin(r) cos(r) 0 | * | 0 d 0 | 145 // | 0 0 1 | | 0 0 1 | | 0 0 1 | 146 // | cos(r)*a -sin(r)*d 0 | 147 // = | sin(r)*a cos(r)*d 0 | 148 // | 0 0 1 | 149 var cos = createjs.cos(rotation); 150 var sin = createjs.sin(rotation); 151 b = sin * a; 152 c = -sin * d; 153 a = cos * a; 154 d = cos * d; 155 } 156 if (skew.x || skew.y) { 157 // Multiply a skew matrix with a rotation one as listed in the following 158 // formula. 159 // | a c 0 | | cos(sy) -sin(sx) 0 | | a c 0 | 160 // | b d 0 | = | sin(sy) cos(sx) 0 | * | b d 0 | 161 // | 0 0 1 | | 0 0 1 | | 0 0 1 | 162 // | cos(sy)*a-sin(sx)*b cos(sy)*c-sin(sx)*d 0 | 163 // = | sin(sy)*a+cos(sx)*b sin(sy)*c+cos(sx)*d 0 | 164 // | 0 0 1 | 165 var a0 = createjs.cos(skew.y); 166 var b0 = createjs.sin(skew.y); 167 var c0 = -createjs.sin(skew.x); 168 var d0 = createjs.cos(skew.x); 169 var a1 = a; 170 var b1 = b; 171 var c1 = c; 172 var d1 = d; 173 a = a0 * a1 + c0 * b1; 174 b = b0 * a1 + d0 * b1; 175 c = a0 * c1 + c0 * d1; 176 d = b0 * c1 + d0 * d1; 177 } 178 this.a = a; 179 this.b = b; 180 this.c = c; 181 this.d = d; 182 this.tx = position.x; 183 this.ty = position.y; 184 if (registration.x || registration.y) { 185 this.tx -= registration.x * this.a + registration.y * this.c; 186 this.ty -= registration.x * this.b + registration.y * this.d; 187 } 188 }; 189 190 /** 191 * Copies the specified transform. 192 * @param {createjs.Transform} transform 193 * @const 194 */ 195 createjs.Transform.prototype.copyTransform = function(transform) { 196 /// <param type="createjs.Transform" name="transform"/> 197 this.a = transform.a; 198 this.b = transform.b; 199 this.c = transform.c; 200 this.d = transform.d; 201 this.tx = transform.tx; 202 this.ty = transform.ty; 203 }; 204 205 /** 206 * Generates matrix properties from transform properties used by display 207 * objects, and appends them with this matrix. 208 * @param {createjs.Transform} transform 209 * @param {createjs.Point} position 210 * @param {createjs.Point} scale 211 * @param {number} rotation 212 * @param {createjs.Point} skew 213 * @param {createjs.Point} registration 214 * @const 215 */ 216 createjs.Transform.prototype.appendTransform = 217 function(transform, position, scale, rotation, skew, registration) { 218 /// <param type="createjs.Transform" name="transform"/> 219 /// <param type="createjs.Point" name="position"/> 220 /// <param type="createjs.Point" name="scale"/> 221 /// <param type="number" name="rotation"/> 222 /// <param type="createjs.Point" name="skew"/> 223 /// <param type="createjs.Point" name="registration"/> 224 this.set(position, scale, rotation, skew, registration); 225 this.prepend_(transform); 226 this.invertible = (this.a * this.d - this.b * this.c) ? 1 : 0; 227 }; 228 229 /** 230 * Returns an inverse transformation of this transformation. 231 * @return {createjs.Transform} 232 * @const 233 */ 234 createjs.Transform.prototype.getInverse = function() { 235 /// <returns type="createjs.Transform"/> 236 if (!this.invertible) { 237 return null; 238 } 239 var idet = 1 / (this.a * this.d - this.b * this.c); 240 var transform = new createjs.Transform(); 241 transform.a = this.d * idet; 242 transform.b = -this.b * idet; 243 transform.c = -this.c * idet; 244 transform.d = this.a * idet; 245 transform.tx = (this.c * this.ty - this.d * this.tx) * idet; 246 transform.ty = -(this.a * this.ty - this.b * this.tx) * idet; 247 return transform; 248 }; 249 250 /** 251 * Applies this transformation to the specified point and returns the 252 * transformed point as listed in the following formula. 253 * | x' | | a c tx | | x | 254 * | y' | = | b d ty | * | y | 255 * | 1 | | 0 0 1 | | 1 | 256 * | a*x+c*y+tx | 257 * = | b*x+d*y+ty | 258 * | 1 | 259 * This method is used for converting a point in a local coordinate to a point 260 * in the global coordinate, and vice versa. 261 * @param {createjs.Point} point 262 * @return {createjs.Point} 263 * @const 264 */ 265 createjs.Transform.prototype.transformPoint = function(point) { 266 /// <param type="createjs.Point" name="point"></param> 267 /// <returns type="createjs.Point"/> 268 var x = point.x; 269 var y = point.y; 270 point.x = this.a * x + this.c * y + this.tx; 271 point.y = this.b * x + this.d * y + this.ty; 272 return point; 273 }; 274 275 /** 276 * Transforms a bounding box in the coordinate system of the owner object to a 277 * box in the global coordinate system, which is used by the createjs.Renderer 278 * interface. This method applies an affine transformation to the corner points 279 * of the given box to get their global positions to create its transformed 280 * bounding box. 281 * @param {createjs.BoundingBox} box 282 * @param {createjs.BoundingBox} output 283 * @protected 284 * @const 285 */ 286 createjs.Transform.prototype.transformBox = function(box, output) { 287 /// <param type="createjs.BoundingBox" name="box"></param> 288 /// <param type="createjs.BoundingBox" name="output"></param> 289 var minX = box.minX; 290 var minY = box.minY; 291 var maxX = box.maxX; 292 var maxY = box.maxY; 293 if (!this.b && !this.c) { 294 // This transform does not have skew factors and we can transform just the 295 // top-left corner and the bottom-right one to get its transformed bounding 296 // box. 297 // | x0 | | this.a this.c tx | | box.minX | 298 // | y0 | = | this.b this.d ty | * | box.minY | 299 // | 1 | | 0 0 1 | | 1 | 300 // | this.a * box.minX + this.c * box.minY + tx | 301 // = | this.b * box.minX + this.d * box.minY + ty | 302 // | 1 | 303 // | x1 | | this.a this.c tx | | box.maxX | 304 // | y1 | = | this.b this.d ty | * | box.maxY | 305 // | 1 | | 0 0 1 | | 1 | 306 // | this.a * box.maxX + this.c * box.maxY + tx | 307 // = | this.b * box.maxX + this.d * box.maxY + ty | 308 // | 1 | 309 var x0 = this.a * minX + this.c * minY; 310 var x1 = this.a * maxX + this.c * maxY; 311 if (x0 < x1) { 312 output.minX = x0; 313 output.maxX = x1; 314 } else { 315 output.minX = x1; 316 output.maxX = x0; 317 } 318 var y0 = this.b * minX + this.d * minY; 319 var y1 = this.b * maxX + this.d * maxY; 320 if (y0 < y1) { 321 output.minY = y0; 322 output.maxY = y1; 323 } else { 324 output.minY = y1; 325 output.maxY = y0; 326 } 327 } else { 328 var x0 = this.a * minX + this.c * minY; 329 var x1 = this.a * maxX + this.c * minY; 330 var x2 = this.a * minX + this.c * maxY; 331 var x3 = this.a * maxX + this.c * maxY; 332 output.minX = createjs.min(createjs.min(createjs.min(x0, x1), x2), x3); 333 output.maxX = createjs.max(createjs.max(createjs.max(x0, x1), x2), x3); 334 var y0 = this.b * minX + this.d * minY; 335 var y1 = this.b * maxX + this.d * minY; 336 var y2 = this.b * minX + this.d * maxY; 337 var y3 = this.b * maxX + this.d * maxY; 338 output.minY = createjs.min(createjs.min(createjs.min(y0, y1), y2), y3); 339 output.maxY = createjs.max(createjs.max(createjs.max(y0, y1), y2), y3); 340 } 341 output.minX += this.tx; 342 output.maxX += this.tx; 343 output.minY += this.ty; 344 output.maxY += this.ty; 345 }; 346