-
-
Save gskinner/1dfb0d274c2fd3aa5ede to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head lang="en"> | |
<meta charset="UTF-8"> | |
<title></title> | |
</head> | |
<body> | |
<!-- include the performance framework first, so it can initialize and inject its UI --> | |
<script src="perfFramework.js"></script> | |
<!-- this test will use PreloadJS to load required assets --> | |
<script src="../../examples/assets/preloadjs-NEXT.min.js"></script> | |
<script> | |
function runTest(version, qs) { | |
var ns = checkVersion("0.5") ? createjs : window; | |
var canvas = document.getElementById("testCanvas"); | |
var w=canvas.width, h=canvas.height; | |
var stage = new ns.Stage("testCanvas"); | |
stage.tickOnUpdate = false; | |
time("total"); | |
time("instantiate"); | |
var arr = [], l=100000; | |
for (var i=0; i<l; i++) { | |
arr.push(new ns.Matrix2D()); | |
} | |
endTime("instantiate", true); | |
time("props"); | |
for (var i=0; i<l; i++) { | |
var mtx = arr[i]; | |
var props = i%16; | |
if (props&1) { mtx.compositeOperation = "foo"; } | |
if (props&2) { mtx.alpha = Math.random(); } | |
if (props&4) { mtx.shadow = {}; } | |
if (props&8) { mtx.visible = false; } | |
} | |
endTime("props", true); | |
time("calculate"); | |
var sum = 0; | |
for (var i=0; i<l; i+=2) { | |
var mtx0 = arr[i], mtx1 = arr[i+1]; | |
mtx0.appendMatrix(mtx1); | |
mtx1.appendMatrix(mtx0); | |
} | |
endTime("calculate", true); | |
endTime("total", true); | |
endTest(); | |
} | |
</script> | |
<p>Tests performance of creating, populating, and appending a transform to 100k Matrix2D instances.</p> | |
<canvas id="testCanvas" width="900" height="450"></canvas> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Matrix2D | |
* Visit http://createjs.com/ for documentation, updates and examples. | |
* | |
* Copyright (c) 2010 gskinner.com, inc. | |
* | |
* Permission is hereby granted, free of charge, to any person | |
* obtaining a copy of this software and associated documentation | |
* files (the "Software"), to deal in the Software without | |
* restriction, including without limitation the rights to use, | |
* copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following | |
* conditions: | |
* | |
* The above copyright notice and this permission notice shall be | |
* included in all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
* OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
/** | |
* @module EaselJS | |
*/ | |
// namespace: | |
this.createjs = this.createjs||{}; | |
(function() { | |
"use strict"; | |
/** | |
* Represents an affine transformation matrix, and provides tools for constructing and concatenating matrixes. | |
* @class Matrix2D | |
* @param {Number} [a=1] Specifies the a property for the new matrix. | |
* @param {Number} [b=0] Specifies the b property for the new matrix. | |
* @param {Number} [c=0] Specifies the c property for the new matrix. | |
* @param {Number} [d=1] Specifies the d property for the new matrix. | |
* @param {Number} [tx=0] Specifies the tx property for the new matrix. | |
* @param {Number} [ty=0] Specifies the ty property for the new matrix. | |
* @constructor | |
**/ | |
var Matrix2D = function(a, b, c, d, tx, ty) { | |
this.initialize(a, b, c, d, tx, ty); | |
}; | |
var p = Matrix2D.prototype; | |
Matrix2D.prototype.constructor = Matrix2D; | |
// static public properties: | |
/** | |
* An identity matrix, representing a null transformation. | |
* @property identity | |
* @static | |
* @type Matrix2D | |
* @readonly | |
**/ | |
Matrix2D.identity = null; // set at bottom of class definition. | |
/** | |
* Multiplier for converting degrees to radians. Used internally by Matrix2D. | |
* @property DEG_TO_RAD | |
* @static | |
* @final | |
* @type Number | |
* @readonly | |
**/ | |
Matrix2D.DEG_TO_RAD = Math.PI/180; | |
// public properties: | |
/** | |
* Position (0, 0) in a 3x3 affine transformation matrix. | |
* @property a | |
* @type Number | |
**/ | |
//p.a = 1; | |
/** | |
* Position (0, 1) in a 3x3 affine transformation matrix. | |
* @property b | |
* @type Number | |
**/ | |
//p.b = 0; | |
/** | |
* Position (1, 0) in a 3x3 affine transformation matrix. | |
* @property c | |
* @type Number | |
**/ | |
// p.c = 0; | |
/** | |
* Position (1, 1) in a 3x3 affine transformation matrix. | |
* @property d | |
* @type Number | |
**/ | |
// p.d = 1; | |
/** | |
* Position (2, 0) in a 3x3 affine transformation matrix. | |
* @property tx | |
* @type Number | |
**/ | |
// p.tx = 0; | |
/** | |
* Position (2, 1) in a 3x3 affine transformation matrix. | |
* @property ty | |
* @type Number | |
**/ | |
// p.ty = 0; | |
/** | |
* Property representing the alpha that will be applied to a display object. This is not part of matrix | |
* operations, but is used for operations like getConcatenatedMatrix to provide concatenated alpha values. | |
* @property alpha | |
* @type Number | |
**/ | |
//p.alpha = 1; | |
/** | |
* Property representing the shadow that will be applied to a display object. This is not part of matrix | |
* operations, but is used for operations like getConcatenatedMatrix to provide concatenated shadow values. | |
* @property shadow | |
* @type Shadow | |
**/ | |
//p.shadow = null; | |
/** | |
* Property representing the compositeOperation that will be applied to a display object. This is not part of | |
* matrix operations, but is used for operations like getConcatenatedMatrix to provide concatenated | |
* compositeOperation values. You can find a list of valid composite operations at: | |
* <a href="https://developer.mozilla.org/en/Canvas_tutorial/Compositing">https://developer.mozilla.org/en/Canvas_tutorial/Compositing</a> | |
* @property compositeOperation | |
* @type String | |
**/ | |
//p.compositeOperation = null; | |
/** | |
* Property representing the value for visible that will be applied to a display object. This is not part of matrix | |
* operations, but is used for operations like getConcatenatedMatrix to provide concatenated visible values. | |
* @property visible | |
* @type Boolean | |
**/ | |
//p.visible = true; | |
// constructor: | |
/** | |
* Initialization method. Can also be used to reinitialize the instance. | |
* @method initialize | |
* @param {Number} [a=1] Specifies the a property for the new matrix. | |
* @param {Number} [b=0] Specifies the b property for the new matrix. | |
* @param {Number} [c=0] Specifies the c property for the new matrix. | |
* @param {Number} [d=1] Specifies the d property for the new matrix. | |
* @param {Number} [tx=0] Specifies the tx property for the new matrix. | |
* @param {Number} [ty=0] Specifies the ty property for the new matrix. | |
* @return {Matrix2D} This instance. Useful for chaining method calls. | |
*/ | |
p.initialize = function(a, b, c, d, tx, ty) { | |
this.a = (a == null) ? 1 : a; | |
this.b = b || 0; | |
this.c = c || 0; | |
this.d = (d == null) ? 1 : d; | |
this.tx = tx || 0; | |
this.ty = ty || 0; | |
this.alpha = 1; | |
this.shadow = null; | |
this.compositeOperation = null; | |
this.visible = true; | |
return this; | |
}; | |
// public methods: | |
/** | |
* Concatenates the specified matrix properties with this matrix. All parameters are required. | |
* @method prepend | |
* @param {Number} a | |
* @param {Number} b | |
* @param {Number} c | |
* @param {Number} d | |
* @param {Number} tx | |
* @param {Number} ty | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.prepend = function(a, b, c, d, tx, ty) { | |
var tx1 = this.tx; | |
if (a != 1 || b != 0 || c != 0 || d != 1) { | |
var a1 = this.a; | |
var c1 = this.c; | |
this.a = a1*a+this.b*c; | |
this.b = a1*b+this.b*d; | |
this.c = c1*a+this.d*c; | |
this.d = c1*b+this.d*d; | |
} | |
this.tx = tx1*a+this.ty*c+tx; | |
this.ty = tx1*b+this.ty*d+ty; | |
return this; | |
}; | |
/** | |
* Appends the specified matrix properties with this matrix. All parameters are required. | |
* @method append | |
* @param {Number} a | |
* @param {Number} b | |
* @param {Number} c | |
* @param {Number} d | |
* @param {Number} tx | |
* @param {Number} ty | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.append = function(a, b, c, d, tx, ty) { | |
var a1 = this.a; | |
var b1 = this.b; | |
var c1 = this.c; | |
var d1 = this.d; | |
this.a = a*a1+b*c1; | |
this.b = a*b1+b*d1; | |
this.c = c*a1+d*c1; | |
this.d = c*b1+d*d1; | |
this.tx = tx*a1+ty*c1+this.tx; | |
this.ty = tx*b1+ty*d1+this.ty; | |
return this; | |
}; | |
/** | |
* Prepends the specified matrix with this matrix. | |
* @method prependMatrix | |
* @param {Matrix2D} matrix | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.prependMatrix = function(matrix) { | |
this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); | |
this.prependProperties(matrix.alpha, matrix.shadow, matrix.compositeOperation, matrix.visible); | |
return this; | |
}; | |
/** | |
* Appends the specified matrix with this matrix. | |
* @method appendMatrix | |
* @param {Matrix2D} matrix | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.appendMatrix = function(matrix) { | |
this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); | |
this.appendProperties(matrix.alpha, matrix.shadow, matrix.compositeOperation, matrix.visible); | |
return this; | |
}; | |
/** | |
* Generates matrix properties from the specified display object transform properties, and prepends them with this matrix. | |
* For example, you can use this to generate a matrix from a display object: var mtx = new Matrix2D(); | |
* mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation); | |
* @method prependTransform | |
* @param {Number} x | |
* @param {Number} y | |
* @param {Number} scaleX | |
* @param {Number} scaleY | |
* @param {Number} rotation | |
* @param {Number} skewX | |
* @param {Number} skewY | |
* @param {Number} regX Optional. | |
* @param {Number} regY Optional. | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.prependTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { | |
if (rotation%360) { | |
var r = rotation*Matrix2D.DEG_TO_RAD; | |
var cos = Math.cos(r); | |
var sin = Math.sin(r); | |
} else { | |
cos = 1; | |
sin = 0; | |
} | |
if (regX || regY) { | |
// append the registration offset: | |
this.tx -= regX; this.ty -= regY; | |
} | |
if (skewX || skewY) { | |
// TODO: can this be combined into a single prepend operation? | |
skewX *= Matrix2D.DEG_TO_RAD; | |
skewY *= Matrix2D.DEG_TO_RAD; | |
this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0); | |
this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y); | |
} else { | |
this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y); | |
} | |
return this; | |
}; | |
/** | |
* Generates matrix properties from the specified display object transform properties, and appends them with this matrix. | |
* For example, you can use this to generate a matrix from a display object: var mtx = new Matrix2D(); | |
* mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation); | |
* @method appendTransform | |
* @param {Number} x | |
* @param {Number} y | |
* @param {Number} scaleX | |
* @param {Number} scaleY | |
* @param {Number} rotation | |
* @param {Number} skewX | |
* @param {Number} skewY | |
* @param {Number} regX Optional. | |
* @param {Number} regY Optional. | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.appendTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) { | |
if (rotation%360) { | |
var r = rotation*Matrix2D.DEG_TO_RAD; | |
var cos = Math.cos(r); | |
var sin = Math.sin(r); | |
} else { | |
cos = 1; | |
sin = 0; | |
} | |
if (skewX || skewY) { | |
// TODO: can this be combined into a single append? | |
skewX *= Matrix2D.DEG_TO_RAD; | |
skewY *= Matrix2D.DEG_TO_RAD; | |
this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y); | |
this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0); | |
} else { | |
this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y); | |
} | |
if (regX || regY) { | |
// prepend the registration offset: | |
this.tx -= regX*this.a+regY*this.c; | |
this.ty -= regX*this.b+regY*this.d; | |
} | |
return this; | |
}; | |
/** | |
* Applies a rotation transformation to the matrix. | |
* @method rotate | |
* @param {Number} angle The angle in radians. To use degrees, multiply by <code>Math.PI/180</code>. | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.rotate = function(angle) { | |
var cos = Math.cos(angle); | |
var sin = Math.sin(angle); | |
var a1 = this.a; | |
var c1 = this.c; | |
var tx1 = this.tx; | |
this.a = a1*cos-this.b*sin; | |
this.b = a1*sin+this.b*cos; | |
this.c = c1*cos-this.d*sin; | |
this.d = c1*sin+this.d*cos; | |
this.tx = tx1*cos-this.ty*sin; | |
this.ty = tx1*sin+this.ty*cos; | |
return this; | |
}; | |
/** | |
* Applies a skew transformation to the matrix. | |
* @method skew | |
* @param {Number} skewX The amount to skew horizontally in degrees. | |
* @param {Number} skewY The amount to skew vertically in degrees. | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
*/ | |
p.skew = function(skewX, skewY) { | |
skewX = skewX*Matrix2D.DEG_TO_RAD; | |
skewY = skewY*Matrix2D.DEG_TO_RAD; | |
this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), 0, 0); | |
return this; | |
}; | |
/** | |
* Applies a scale transformation to the matrix. | |
* @method scale | |
* @param {Number} x The amount to scale horizontally | |
* @param {Number} y The amount to scale vertically | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.scale = function(x, y) { | |
this.a *= x; | |
this.d *= y; | |
this.c *= x; | |
this.b *= y; | |
this.tx *= x; | |
this.ty *= y; | |
return this; | |
}; | |
/** | |
* Translates the matrix on the x and y axes. | |
* @method translate | |
* @param {Number} x | |
* @param {Number} y | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.translate = function(x, y) { | |
this.tx += x; | |
this.ty += y; | |
return this; | |
}; | |
/** | |
* Sets the properties of the matrix to those of an identity matrix (one that applies a null transformation). | |
* @method identity | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.identity = function() { | |
this.alpha = this.a = this.d = 1; | |
this.b = this.c = this.tx = this.ty = 0; | |
this.shadow = this.compositeOperation = null; | |
this.visible = true; | |
return this; | |
}; | |
/** | |
* Inverts the matrix, causing it to perform the opposite transformation. | |
* @method invert | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
**/ | |
p.invert = function() { | |
var a1 = this.a; | |
var b1 = this.b; | |
var c1 = this.c; | |
var d1 = this.d; | |
var tx1 = this.tx; | |
var n = a1*d1-b1*c1; | |
this.a = d1/n; | |
this.b = -b1/n; | |
this.c = -c1/n; | |
this.d = a1/n; | |
this.tx = (c1*this.ty-d1*tx1)/n; | |
this.ty = -(a1*this.ty-b1*tx1)/n; | |
return this; | |
}; | |
/** | |
* Returns true if the matrix is an identity matrix. | |
* @method isIdentity | |
* @return {Boolean} | |
**/ | |
p.isIdentity = function() { | |
return this.tx == 0 && this.ty == 0 && this.a == 1 && this.b == 0 && this.c == 0 && this.d == 1; | |
}; | |
/** | |
* Transforms a point according to this matrix. | |
* @method transformPoint | |
* @param {Number} x The x component of the point to transform. | |
* @param {Number} y The y component of the point to transform. | |
* @param {Point | Object} [pt] An object to copy the result into. If omitted a generic object with x/y properties will be returned. | |
* @return {Point} This matrix. Useful for chaining method calls. | |
**/ | |
p.transformPoint = function(x, y, pt) { | |
pt = pt||{}; | |
pt.x = x*this.a+y*this.c+this.tx; | |
pt.y = x*this.b+y*this.d+this.ty; | |
return pt; | |
}; | |
/** | |
* Decomposes the matrix into transform properties (x, y, scaleX, scaleY, and rotation). Note that this these values | |
* may not match the transform properties you used to generate the matrix, though they will produce the same visual | |
* results. | |
* @method decompose | |
* @param {Object} target The object to apply the transform properties to. If null, then a new object will be returned. | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
*/ | |
p.decompose = function(target) { | |
// TODO: it would be nice to be able to solve for whether the matrix can be decomposed into only scale/rotation | |
// even when scale is negative | |
if (target == null) { target = {}; } | |
target.x = this.tx; | |
target.y = this.ty; | |
target.scaleX = Math.sqrt(this.a * this.a + this.b * this.b); | |
target.scaleY = Math.sqrt(this.c * this.c + this.d * this.d); | |
var skewX = Math.atan2(-this.c, this.d); | |
var skewY = Math.atan2(this.b, this.a); | |
if (skewX == skewY) { | |
target.rotation = skewY/Matrix2D.DEG_TO_RAD; | |
if (this.a < 0 && this.d >= 0) { | |
target.rotation += (target.rotation <= 0) ? 180 : -180; | |
} | |
target.skewX = target.skewY = 0; | |
} else { | |
target.skewX = skewX/Matrix2D.DEG_TO_RAD; | |
target.skewY = skewY/Matrix2D.DEG_TO_RAD; | |
} | |
return target; | |
}; | |
/** | |
* Reinitializes all matrix properties to those specified. | |
* @method reinitialize | |
* @param {Number} [a=1] Specifies the a property for the new matrix. | |
* @param {Number} [b=0] Specifies the b property for the new matrix. | |
* @param {Number} [c=0] Specifies the c property for the new matrix. | |
* @param {Number} [d=1] Specifies the d property for the new matrix. | |
* @param {Number} [tx=0] Specifies the tx property for the new matrix. | |
* @param {Number} [ty=0] Specifies the ty property for the new matrix. | |
* @param {Number} [alpha=1] desired alpha value | |
* @param {Shadow} [shadow=null] desired shadow value | |
* @param {String} [compositeOperation=null] desired composite operation value | |
* @param {Boolean} [visible=true] desired visible value | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
*/ | |
p.reinitialize = function(a, b, c, d, tx, ty, alpha, shadow, compositeOperation, visible) { | |
this.initialize(a,b,c,d,tx,ty); | |
this.alpha = alpha == null ? 1 : alpha; | |
this.shadow = shadow; | |
this.compositeOperation = compositeOperation; | |
this.visible = visible == null ? true : visible; | |
return this; | |
}; | |
/** | |
* Copies all properties from the specified matrix to this matrix. | |
* @method copy | |
* @param {Matrix2D} matrix The matrix to copy properties from. | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
*/ | |
p.copy = function(matrix) { | |
return this.reinitialize(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty, matrix.alpha, matrix.shadow, matrix.compositeOperation, matrix.visible); | |
}; | |
/** | |
* Appends the specified visual properties to the current matrix. | |
* @method appendProperties | |
* @param {Number} alpha desired alpha value | |
* @param {Shadow} shadow desired shadow value | |
* @param {String} compositeOperation desired composite operation value | |
* @param {Boolean} visible desired visible value | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
*/ | |
p.appendProperties = function(alpha, shadow, compositeOperation, visible) { | |
this.alpha *= alpha; | |
this.shadow = shadow || this.shadow; | |
this.compositeOperation = compositeOperation || this.compositeOperation; | |
this.visible = this.visible && visible; | |
return this; | |
}; | |
/** | |
* Prepends the specified visual properties to the current matrix. | |
* @method prependProperties | |
* @param {Number} alpha desired alpha value | |
* @param {Shadow} shadow desired shadow value | |
* @param {String} compositeOperation desired composite operation value | |
* @param {Boolean} visible desired visible value | |
* @return {Matrix2D} This matrix. Useful for chaining method calls. | |
*/ | |
p.prependProperties = function(alpha, shadow, compositeOperation, visible) { | |
this.alpha *= alpha; | |
this.shadow = this.shadow || shadow; | |
this.compositeOperation = this.compositeOperation || compositeOperation; | |
this.visible = this.visible && visible; | |
return this; | |
}; | |
/** | |
* Returns a clone of the Matrix2D instance. | |
* @method clone | |
* @return {Matrix2D} a clone of the Matrix2D instance. | |
**/ | |
p.clone = function() { | |
return (new Matrix2D()).copy(this); | |
}; | |
/** | |
* Returns a string representation of this object. | |
* @method toString | |
* @return {String} a string representation of the instance. | |
**/ | |
p.toString = function() { | |
return "[Matrix2D (a="+this.a+" b="+this.b+" c="+this.c+" d="+this.d+" tx="+this.tx+" ty="+this.ty+")]"; | |
}; | |
// this has to be populated after the class is defined: | |
Matrix2D.identity = new Matrix2D(); | |
createjs.Matrix2D = Matrix2D; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment