2011-03-06 19:50:44 -05:00
|
|
|
/*
|
2013-01-28 21:03:27 -05:00
|
|
|
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
|
2011-03-07 20:41:50 -05:00
|
|
|
* http://paperjs.org/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2013-01-28 21:03:27 -05:00
|
|
|
* Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
|
2011-03-06 19:50:44 -05:00
|
|
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-01 06:17:45 -04:00
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
2011-03-07 20:41:50 -05:00
|
|
|
* All rights reserved.
|
2011-03-06 19:50:44 -05:00
|
|
|
*/
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
// Based on goog.graphics.AffineTransform, as part of the Closure Library.
|
|
|
|
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
2011-06-22 18:56:05 -04:00
|
|
|
/**
|
|
|
|
* @name Matrix
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-22 18:56:05 -04:00
|
|
|
* @class An affine transform performs a linear mapping from 2D coordinates
|
|
|
|
* to other 2D coordinates that preserves the "straightness" and
|
|
|
|
* "parallelness" of lines.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-22 18:56:05 -04:00
|
|
|
* Such a coordinate transformation can be represented by a 3 row by 3
|
|
|
|
* column matrix with an implied last row of [ 0 0 1 ]. This matrix
|
|
|
|
* transforms source coordinates (x,y) into destination coordinates (x',y')
|
|
|
|
* by considering them to be a column vector and multiplying the coordinate
|
|
|
|
* vector by the matrix according to the following process:
|
|
|
|
* <pre>
|
2011-07-31 10:26:09 -04:00
|
|
|
* [ x ] [ a b tx ] [ x ] [ a * x + b * y + tx ]
|
|
|
|
* [ y ] = [ c d ty ] [ y ] = [ c * x + d * y + ty ]
|
|
|
|
* [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
|
2011-06-22 18:56:05 -04:00
|
|
|
* </pre>
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-22 18:56:05 -04:00
|
|
|
* This class is optimized for speed and minimizes calculations based on its
|
|
|
|
* knowledge of the underlying matrix (as opposed to say simply performing
|
|
|
|
* matrix multiplication).
|
|
|
|
*/
|
2013-05-27 15:48:58 -04:00
|
|
|
var Matrix = Base.extend(/** @lends Matrix# */{
|
2013-06-23 23:18:32 -04:00
|
|
|
_class: 'Matrix',
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
2011-05-22 18:54:27 -04:00
|
|
|
* Creates a 2D affine transform.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Number} a The scaleX coordinate of the transform
|
|
|
|
* @param {Number} c The shearY coordinate of the transform
|
|
|
|
* @param {Number} b The shearX coordinate of the transform
|
|
|
|
* @param {Number} d The scaleY coordinate of the transform
|
|
|
|
* @param {Number} tx The translateX coordinate of the transform
|
|
|
|
* @param {Number} ty The translateY coordinate of the transform
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2013-05-27 15:48:58 -04:00
|
|
|
initialize: function Matrix(arg) {
|
2011-07-25 04:15:35 -04:00
|
|
|
var count = arguments.length,
|
|
|
|
ok = true;
|
|
|
|
if (count == 6) {
|
|
|
|
this.set.apply(this, arguments);
|
|
|
|
} else if (count == 1) {
|
|
|
|
if (arg instanceof Matrix) {
|
2011-07-31 10:26:09 -04:00
|
|
|
this.set(arg._a, arg._c, arg._b, arg._d, arg._tx, arg._ty);
|
2011-07-25 04:15:35 -04:00
|
|
|
} else if (Array.isArray(arg)) {
|
|
|
|
this.set.apply(this, arg);
|
2011-02-15 18:15:28 -05:00
|
|
|
} else {
|
|
|
|
ok = false;
|
2011-06-30 06:01:51 -04:00
|
|
|
}
|
2011-07-25 04:15:35 -04:00
|
|
|
} else if (count == 0) {
|
2013-02-09 14:45:33 -05:00
|
|
|
this.reset();
|
2011-07-25 04:15:35 -04:00
|
|
|
} else {
|
|
|
|
ok = false;
|
2011-02-13 20:05:16 -05:00
|
|
|
}
|
2011-02-15 18:15:28 -05:00
|
|
|
if (!ok)
|
2011-03-03 12:29:40 -05:00
|
|
|
throw new Error('Unsupported matrix parameters');
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets this transform to the matrix specified by the 6 values.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Number} a The scaleX coordinate of the transform
|
|
|
|
* @param {Number} c The shearY coordinate of the transform
|
|
|
|
* @param {Number} b The shearX coordinate of the transform
|
|
|
|
* @param {Number} d The scaleY coordinate of the transform
|
|
|
|
* @param {Number} tx The translateX coordinate of the transform
|
|
|
|
* @param {Number} ty The translateY coordinate of the transform
|
|
|
|
* @return {Matrix} This affine transform
|
|
|
|
*/
|
|
|
|
set: function(a, c, b, d, tx, ty) {
|
|
|
|
this._a = a;
|
|
|
|
this._c = c;
|
|
|
|
this._b = b;
|
|
|
|
this._d = d;
|
|
|
|
this._tx = tx;
|
|
|
|
this._ty = ty;
|
2011-02-13 20:05:16 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2013-03-19 20:51:50 -04:00
|
|
|
_serialize: function(options) {
|
|
|
|
return Base.serialize(this.getValues(), options);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {Matrix} A copy of this transform.
|
|
|
|
*/
|
|
|
|
clone: function() {
|
2013-05-28 02:57:31 -04:00
|
|
|
return new Matrix(this._a, this._c, this._b, this._d,
|
2013-03-19 20:51:50 -04:00
|
|
|
this._tx, this._ty);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether the two matrices describe the same transformation.
|
|
|
|
*
|
|
|
|
* @param {Matrix} matrix the matrix to compare this matrix to
|
|
|
|
* @return {Boolean} {@true if the matrices are equal}
|
|
|
|
*/
|
|
|
|
equals: function(mx) {
|
2013-06-12 21:57:12 -04:00
|
|
|
return mx === this || mx && this._a == mx._a && this._b == mx._b
|
|
|
|
&& this._c == mx._c && this._d == mx._d && this._tx == mx._tx
|
|
|
|
&& this._ty == mx._ty
|
|
|
|
|| false;
|
2013-03-19 20:51:50 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {String} A string representation of this transform.
|
|
|
|
*/
|
|
|
|
toString: function() {
|
2013-04-09 20:32:19 -04:00
|
|
|
var f = Formatter.instance;
|
|
|
|
return '[[' + [f.number(this._a), f.number(this._b),
|
|
|
|
f.number(this._tx)].join(', ') + '], ['
|
|
|
|
+ [f.number(this._c), f.number(this._d),
|
|
|
|
f.number(this._ty)].join(', ') + ']]';
|
2013-03-19 20:51:50 -04:00
|
|
|
},
|
|
|
|
|
2013-02-09 14:45:33 -05:00
|
|
|
/**
|
|
|
|
* "Resets" the matrix by setting its values to the ones of the identity
|
|
|
|
* matrix that results in no transformation.
|
|
|
|
*/
|
|
|
|
reset: function() {
|
2011-12-20 17:14:30 -05:00
|
|
|
this._a = this._d = 1;
|
|
|
|
this._c = this._b = this._tx = this._ty = 0;
|
2011-12-20 17:32:53 -05:00
|
|
|
return this;
|
2011-12-20 17:14:30 -05:00
|
|
|
},
|
|
|
|
|
2011-05-29 11:31:13 -04:00
|
|
|
/**
|
|
|
|
* Concatentates this transform with a scaling transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-29 11:31:13 -04:00
|
|
|
* @name Matrix#scale
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Number} scale The scaling factor
|
|
|
|
* @param {Point} [center] The center for the scaling transformation
|
|
|
|
* @return {Matrix} This affine transform
|
2011-05-29 11:31:13 -04:00
|
|
|
*/
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
|
|
|
* Concatentates this transform with a scaling transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-16 14:26:50 -04:00
|
|
|
* @name Matrix#scale
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Number} hor The horizontal scaling factor
|
|
|
|
* @param {Number} ver The vertical scaling factor
|
|
|
|
* @param {Point} [center] The center for the scaling transformation
|
|
|
|
* @return {Matrix} This affine transform
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2013-06-24 13:15:54 -04:00
|
|
|
scale: function(/* scale, center */) {
|
2012-10-18 17:24:15 -04:00
|
|
|
// Do not modify scale, center, since that would arguments of which
|
|
|
|
// we're reading from!
|
2013-06-24 13:15:54 -04:00
|
|
|
var scale = Point.read(arguments),
|
|
|
|
center = Point.read(arguments, 0, 0, true); // readNull
|
|
|
|
if (center)
|
|
|
|
this.translate(center);
|
|
|
|
this._a *= scale.x;
|
|
|
|
this._c *= scale.x;
|
|
|
|
this._b *= scale.y;
|
|
|
|
this._d *= scale.y;
|
|
|
|
if (center)
|
|
|
|
this.translate(center.negate());
|
2011-02-13 20:05:16 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2011-05-31 10:08:25 -04:00
|
|
|
/**
|
|
|
|
* Concatentates this transform with a translate transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-31 10:08:25 -04:00
|
|
|
* @name Matrix#translate
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Point} point The vector to translate by
|
|
|
|
* @return {Matrix} This affine transform
|
2011-05-31 10:08:25 -04:00
|
|
|
*/
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
|
|
|
* Concatentates this transform with a translate transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-16 14:26:50 -04:00
|
|
|
* @name Matrix#translate
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Number} dx The distance to translate in the x direction
|
|
|
|
* @param {Number} dy The distance to translate in the y direction
|
|
|
|
* @return {Matrix} This affine transform
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
translate: function(point) {
|
2011-03-13 13:31:00 -04:00
|
|
|
point = Point.read(arguments);
|
2012-10-18 17:24:15 -04:00
|
|
|
var x = point.x,
|
|
|
|
y = point.y;
|
2011-07-31 10:26:09 -04:00
|
|
|
this._tx += x * this._a + y * this._b;
|
|
|
|
this._ty += x * this._c + y * this._d;
|
2011-02-13 20:05:16 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2011-05-31 10:08:25 -04:00
|
|
|
/**
|
|
|
|
* Concatentates this transform with a rotation transformation around an
|
|
|
|
* anchor point.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-31 10:08:25 -04:00
|
|
|
* @name Matrix#rotate
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Number} angle The angle of rotation measured in degrees
|
|
|
|
* @param {Point} center The anchor point to rotate around
|
|
|
|
* @return {Matrix} This affine transform
|
2011-05-31 10:08:25 -04:00
|
|
|
*/
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
|
|
|
* Concatentates this transform with a rotation transformation around an
|
|
|
|
* anchor point.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-16 14:26:50 -04:00
|
|
|
* @name Matrix#rotate
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Number} angle The angle of rotation measured in degrees
|
|
|
|
* @param {Number} x The x coordinate of the anchor point
|
|
|
|
* @param {Number} y The y coordinate of the anchor point
|
|
|
|
* @return {Matrix} This affine transform
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
rotate: function(angle, center) {
|
2013-02-09 14:40:49 -05:00
|
|
|
center = Point.read(arguments, 1);
|
|
|
|
angle = angle * Math.PI / 180;
|
|
|
|
// Concatenate rotation matrix into this one
|
|
|
|
var x = center.x,
|
|
|
|
y = center.y,
|
|
|
|
cos = Math.cos(angle),
|
|
|
|
sin = Math.sin(angle),
|
|
|
|
tx = x - x * cos + y * sin,
|
|
|
|
ty = y - x * sin - y * cos,
|
|
|
|
a = this._a,
|
|
|
|
b = this._b,
|
|
|
|
c = this._c,
|
|
|
|
d = this._d;
|
|
|
|
this._a = cos * a + sin * b;
|
|
|
|
this._b = -sin * a + cos * b;
|
|
|
|
this._c = cos * c + sin * d;
|
|
|
|
this._d = -sin * c + cos * d;
|
|
|
|
this._tx += tx * a + ty * b;
|
|
|
|
this._ty += tx * c + ty * d;
|
|
|
|
return this;
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
2011-05-31 10:08:25 -04:00
|
|
|
/**
|
|
|
|
* Concatentates this transform with a shear transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-31 10:08:25 -04:00
|
|
|
* @name Matrix#shear
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Point} point The shear factor in x and y direction
|
|
|
|
* @param {Point} [center] The center for the shear transformation
|
|
|
|
* @return {Matrix} This affine transform
|
2011-05-31 10:08:25 -04:00
|
|
|
*/
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
|
|
|
* Concatentates this transform with a shear transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-16 14:26:50 -04:00
|
|
|
* @name Matrix#shear
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Number} hor The horizontal shear factor
|
|
|
|
* @param {Number} ver The vertical shear factor
|
|
|
|
* @param {Point} [center] The center for the shear transformation
|
|
|
|
* @return {Matrix} This affine transform
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2013-06-24 13:15:54 -04:00
|
|
|
shear: function(/* point, center */) {
|
2012-10-18 17:24:15 -04:00
|
|
|
// Do not modify point, center, since that would arguments of which
|
|
|
|
// we're reading from!
|
2013-06-24 13:15:54 -04:00
|
|
|
var point = Point.read(arguments),
|
|
|
|
center = Point.read(arguments, 0, 0, true); // readNull
|
|
|
|
if (center)
|
|
|
|
this.translate(center);
|
2011-07-31 10:26:09 -04:00
|
|
|
var a = this._a,
|
|
|
|
c = this._c;
|
2013-06-24 13:15:54 -04:00
|
|
|
this._a += point.y * this._b;
|
|
|
|
this._c += point.y * this._d;
|
|
|
|
this._b += point.x * a;
|
|
|
|
this._d += point.x * c;
|
|
|
|
if (center)
|
|
|
|
this.translate(center.negate());
|
2011-02-13 20:05:16 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2013-02-09 15:11:19 -05:00
|
|
|
* @return {Boolean} Whether this transform is the identity transform
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2013-02-09 15:11:19 -05:00
|
|
|
isIdentity: function() {
|
|
|
|
return this._a == 1 && this._c == 0 && this._b == 0 && this._d == 1
|
|
|
|
&& this._tx == 0 && this._ty == 0;
|
|
|
|
},
|
2011-02-13 20:05:16 -05:00
|
|
|
|
|
|
|
/**
|
2013-02-09 15:11:19 -05:00
|
|
|
* Returns whether the transform is invertible. A transform is not
|
|
|
|
* invertible if the determinant is 0 or any value is non-finite or NaN.
|
2011-07-25 04:15:18 -04:00
|
|
|
*
|
2013-02-09 15:11:19 -05:00
|
|
|
* @return {Boolean} Whether the transform is invertible
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2013-02-09 15:11:19 -05:00
|
|
|
isInvertible: function() {
|
|
|
|
return !!this._getDeterminant();
|
|
|
|
},
|
2011-02-13 20:05:16 -05:00
|
|
|
|
2011-07-31 10:26:09 -04:00
|
|
|
/**
|
2013-02-09 15:11:19 -05:00
|
|
|
* Checks whether the matrix is singular or not. Singular matrices cannot be
|
|
|
|
* inverted.
|
2011-07-31 10:26:09 -04:00
|
|
|
*
|
2013-02-09 15:11:19 -05:00
|
|
|
* @return {Boolean} Whether the matrix is singular
|
2011-07-31 10:26:09 -04:00
|
|
|
*/
|
2013-02-09 15:11:19 -05:00
|
|
|
isSingular: function() {
|
|
|
|
return !this._getDeterminant();
|
2011-07-31 10:26:09 -04:00
|
|
|
},
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
|
|
|
* Concatenates an affine transform to this transform.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Matrix} mx The transform to concatenate
|
|
|
|
* @return {Matrix} This affine transform
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
concatenate: function(mx) {
|
2011-07-31 10:26:09 -04:00
|
|
|
var a = this._a,
|
|
|
|
b = this._b,
|
|
|
|
c = this._c,
|
|
|
|
d = this._d;
|
|
|
|
this._a = mx._a * a + mx._c * b;
|
|
|
|
this._b = mx._b * a + mx._d * b;
|
|
|
|
this._c = mx._a * c + mx._c * d;
|
|
|
|
this._d = mx._b * c + mx._d * d;
|
2011-12-20 05:39:10 -05:00
|
|
|
this._tx += mx._tx * a + mx._ty * b;
|
2011-07-31 10:26:09 -04:00
|
|
|
this._ty += mx._tx * c + mx._ty * d;
|
2011-02-13 20:05:16 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pre-concatenates an affine transform to this transform.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Matrix} mx The transform to preconcatenate
|
|
|
|
* @return {Matrix} This affine transform
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
preConcatenate: function(mx) {
|
2011-07-31 10:26:09 -04:00
|
|
|
var a = this._a,
|
|
|
|
b = this._b,
|
|
|
|
c = this._c,
|
|
|
|
d = this._d,
|
|
|
|
tx = this._tx,
|
|
|
|
ty = this._ty;
|
|
|
|
this._a = mx._a * a + mx._b * c;
|
|
|
|
this._b = mx._a * b + mx._b * d;
|
2011-12-20 05:39:10 -05:00
|
|
|
this._c = mx._c * a + mx._d * c;
|
2011-07-31 10:26:09 -04:00
|
|
|
this._d = mx._c * b + mx._d * d;
|
|
|
|
this._tx = mx._a * tx + mx._b * ty + mx._tx;
|
|
|
|
this._ty = mx._c * tx + mx._d * ty + mx._ty;
|
2011-02-13 20:05:16 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2011-09-23 05:17:42 -04:00
|
|
|
* Transforms a point and returns the result.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-09-23 05:17:42 -04:00
|
|
|
* @name Matrix#transform
|
|
|
|
* @function
|
2011-07-31 10:26:09 -04:00
|
|
|
* @param {Point} point The point to be transformed
|
2011-09-23 05:17:42 -04:00
|
|
|
* @return {Point} The transformed point
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* Transforms an array of coordinates by this matrix and stores the results
|
|
|
|
* into the destination array, which is also returned.
|
|
|
|
*
|
|
|
|
* @name Matrix#transform
|
|
|
|
* @function
|
2011-06-20 09:19:08 -04:00
|
|
|
* @param {Number[]} src The array containing the source points
|
2011-07-31 10:26:09 -04:00
|
|
|
* as x, y value pairs
|
|
|
|
* @param {Number} srcOff The offset to the first point to be transformed
|
2011-06-20 09:19:08 -04:00
|
|
|
* @param {Number[]} dst The array into which to store the transformed
|
2011-07-31 10:26:09 -04:00
|
|
|
* point pairs
|
2011-05-27 15:21:49 -04:00
|
|
|
* @param {Number} dstOff The offset of the location of the first
|
2011-07-31 10:26:09 -04:00
|
|
|
* transformed point in the destination array
|
|
|
|
* @param {Number} numPts The number of points to tranform
|
2011-09-23 05:17:42 -04:00
|
|
|
* @return {Number[]} The dst array, containing the transformed coordinates.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
transform: function(/* point | */ src, srcOff, dst, dstOff, numPts) {
|
2011-05-06 08:28:35 -04:00
|
|
|
return arguments.length < 5
|
2011-05-16 07:25:18 -04:00
|
|
|
// TODO: Check for rectangle and use _tranformBounds?
|
2011-05-06 08:28:35 -04:00
|
|
|
? this._transformPoint(Point.read(arguments))
|
|
|
|
: this._transformCoordinates(src, srcOff, dst, dstOff, numPts);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2011-06-30 06:01:51 -04:00
|
|
|
* A faster version of transform that only takes one point and does not
|
2011-05-06 08:28:35 -04:00
|
|
|
* attempt to convert it.
|
|
|
|
*/
|
2011-05-16 06:18:53 -04:00
|
|
|
_transformPoint: function(point, dest, dontNotify) {
|
2011-05-06 08:28:35 -04:00
|
|
|
var x = point.x,
|
|
|
|
y = point.y;
|
2011-05-16 06:07:39 -04:00
|
|
|
if (!dest)
|
2013-06-25 12:50:14 -04:00
|
|
|
dest = new Point();
|
2011-05-16 06:07:39 -04:00
|
|
|
return dest.set(
|
2011-07-31 10:26:09 -04:00
|
|
|
x * this._a + y * this._b + this._tx,
|
|
|
|
x * this._c + y * this._d + this._ty,
|
2011-05-16 06:18:53 -04:00
|
|
|
dontNotify
|
2011-05-06 08:28:35 -04:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
_transformCoordinates: function(src, srcOff, dst, dstOff, numPts) {
|
|
|
|
var i = srcOff, j = dstOff,
|
|
|
|
srcEnd = srcOff + 2 * numPts;
|
|
|
|
while (i < srcEnd) {
|
2013-02-08 21:17:51 -05:00
|
|
|
var x = src[i++],
|
|
|
|
y = src[i++];
|
2011-07-31 10:26:09 -04:00
|
|
|
dst[j++] = x * this._a + y * this._b + this._tx;
|
|
|
|
dst[j++] = x * this._c + y * this._d + this._ty;
|
2011-02-13 20:05:16 -05:00
|
|
|
}
|
2011-05-06 08:28:35 -04:00
|
|
|
return dst;
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
2011-05-16 07:29:52 -04:00
|
|
|
_transformCorners: function(rect) {
|
|
|
|
var x1 = rect.x,
|
|
|
|
y1 = rect.y,
|
|
|
|
x2 = x1 + rect.width,
|
|
|
|
y2 = y1 + rect.height,
|
|
|
|
coords = [ x1, y1, x2, y1, x2, y2, x1, y2 ];
|
|
|
|
return this._transformCoordinates(coords, 0, coords, 0, 4);
|
|
|
|
},
|
|
|
|
|
2011-05-16 07:25:18 -04:00
|
|
|
/**
|
|
|
|
* Returns the 'transformed' bounds rectangle by transforming each corner
|
|
|
|
* point and finding the new bounding box to these points. This is not
|
|
|
|
* really the transformed reactangle!
|
|
|
|
*/
|
2011-11-24 05:29:35 -05:00
|
|
|
_transformBounds: function(bounds, dest, dontNotify) {
|
2011-05-16 07:29:52 -04:00
|
|
|
var coords = this._transformCorners(bounds),
|
2011-05-16 07:25:18 -04:00
|
|
|
min = coords.slice(0, 2),
|
2013-04-06 12:39:17 -04:00
|
|
|
max = coords.slice();
|
2011-03-04 20:26:12 -05:00
|
|
|
for (var i = 2; i < 8; i++) {
|
2011-05-16 07:25:18 -04:00
|
|
|
var val = coords[i],
|
|
|
|
j = i & 1;
|
|
|
|
if (val < min[j])
|
|
|
|
min[j] = val;
|
|
|
|
else if (val > max[j])
|
|
|
|
max[j] = val;
|
2011-03-04 20:26:12 -05:00
|
|
|
}
|
2011-11-24 05:29:35 -05:00
|
|
|
if (!dest)
|
2013-06-25 12:50:14 -04:00
|
|
|
dest = new Rectangle();
|
2011-11-24 05:29:35 -05:00
|
|
|
return dest.set(min[0], min[1], max[0] - min[0], max[1] - min[1],
|
|
|
|
dontNotify);
|
2011-03-04 20:26:12 -05:00
|
|
|
},
|
|
|
|
|
2011-09-23 05:19:03 -04:00
|
|
|
/**
|
|
|
|
* Inverse transforms a point and returns the result.
|
|
|
|
*
|
|
|
|
* @param {Point} point The point to be transformed
|
|
|
|
*/
|
2013-06-24 13:15:54 -04:00
|
|
|
inverseTransform: function(/* point */) {
|
2011-09-23 05:19:03 -04:00
|
|
|
return this._inverseTransform(Point.read(arguments));
|
|
|
|
},
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
2011-09-22 15:19:41 -04:00
|
|
|
* Returns the determinant of this transform, but only if the matrix is
|
|
|
|
* reversible, null otherwise.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2011-09-22 15:19:41 -04:00
|
|
|
_getDeterminant: function() {
|
|
|
|
var det = this._a * this._d - this._b * this._c;
|
2012-11-06 00:06:13 -05:00
|
|
|
return isFinite(det) && !Numerical.isZero(det)
|
2011-09-22 15:19:41 -04:00
|
|
|
&& isFinite(this._tx) && isFinite(this._ty)
|
|
|
|
? det : null;
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
2011-09-23 05:19:03 -04:00
|
|
|
_inverseTransform: function(point, dest, dontNotify) {
|
|
|
|
var det = this._getDeterminant();
|
|
|
|
if (!det)
|
|
|
|
return null;
|
|
|
|
var x = point.x - this._tx,
|
|
|
|
y = point.y - this._ty;
|
|
|
|
if (!dest)
|
2013-06-25 12:50:14 -04:00
|
|
|
dest = new Point();
|
2011-09-23 05:19:03 -04:00
|
|
|
return dest.set(
|
|
|
|
(x * this._d - y * this._b) / det,
|
|
|
|
(y * this._a - x * this._c) / det,
|
|
|
|
dontNotify
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2013-02-09 15:09:55 -05:00
|
|
|
/**
|
|
|
|
* Attempts to decompose the affine transformation described by this matrix
|
|
|
|
* into {@code translation}, {@code scaling}, {@code rotation} and
|
|
|
|
* {@code shearing}, and returns an object with these properties if it
|
|
|
|
* succeeded, {@code null} otherwise.
|
|
|
|
*
|
|
|
|
* @return {Object} the decomposed matrix, or {@code null} if decomposition
|
|
|
|
* is not possible.
|
|
|
|
*/
|
2013-02-09 02:02:20 -05:00
|
|
|
decompose: function() {
|
|
|
|
// http://dev.w3.org/csswg/css3-2d-transforms/#matrix-decomposition
|
|
|
|
// http://stackoverflow.com/questions/4361242/
|
2013-02-09 02:25:10 -05:00
|
|
|
// https://github.com/wisec/DOMinator/blob/master/layout/style/nsStyleAnimation.cpp#L946
|
2013-02-09 02:02:20 -05:00
|
|
|
var a = this._a, b = this._b, c = this._c, d = this._d;
|
|
|
|
if (Numerical.isZero(a * d - b * c))
|
2013-02-09 15:09:55 -05:00
|
|
|
return null;
|
2013-02-09 02:02:20 -05:00
|
|
|
|
|
|
|
var scaleX = Math.sqrt(a * a + b * b);
|
|
|
|
a /= scaleX;
|
|
|
|
b /= scaleX;
|
|
|
|
|
|
|
|
var shear = a * c + b * d;
|
|
|
|
c -= a * shear;
|
|
|
|
d -= b * shear;
|
|
|
|
|
|
|
|
var scaleY = Math.sqrt(c * c + d * d);
|
|
|
|
c /= scaleY;
|
|
|
|
d /= scaleY;
|
|
|
|
shear /= scaleY;
|
|
|
|
|
|
|
|
// a * d - b * c should now be 1 or -1
|
|
|
|
if (a * d < b * c) {
|
|
|
|
a = -a;
|
|
|
|
b = -b;
|
2013-02-09 14:21:16 -05:00
|
|
|
// We don't need c & d anymore, but if we did, we'd have to do this:
|
2013-02-09 02:02:20 -05:00
|
|
|
// c = -c;
|
|
|
|
// d = -d;
|
|
|
|
shear = -shear;
|
|
|
|
scaleX = -scaleX;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2013-02-09 15:09:55 -05:00
|
|
|
translation: this.getTranslation(),
|
2013-05-28 02:57:31 -04:00
|
|
|
scaling: new Point(scaleX, scaleY),
|
2013-02-09 02:02:20 -05:00
|
|
|
rotation: -Math.atan2(b, a) * 180 / Math.PI,
|
2013-02-09 14:21:16 -05:00
|
|
|
shearing: shear
|
2013-02-09 02:02:20 -05:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2013-02-09 15:11:19 -05:00
|
|
|
/**
|
|
|
|
* The scaling factor in the x-direction ({@code a}).
|
|
|
|
*
|
|
|
|
* @name Matrix#scaleX
|
|
|
|
* @type Number
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The scaling factor in the y-direction ({@code d}).
|
|
|
|
*
|
|
|
|
* @name Matrix#scaleY
|
|
|
|
* @type Number
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {Number} The shear factor in the x-direction ({@code b}).
|
|
|
|
*
|
|
|
|
* @name Matrix#shearX
|
|
|
|
* @type Number
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {Number} The shear factor in the y-direction ({@code c}).
|
|
|
|
*
|
|
|
|
* @name Matrix#shearY
|
|
|
|
* @type Number
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The translation in the x-direction ({@code tx}).
|
|
|
|
*
|
|
|
|
* @name Matrix#translateX
|
|
|
|
* @type Number
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The translation in the y-direction ({@code ty}).
|
|
|
|
*
|
|
|
|
* @name Matrix#translateY
|
|
|
|
* @type Number
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The transform values as an array, in the same sequence as they are passed
|
|
|
|
* to {@link #initialize(a, c, b, d, tx, ty)}.
|
|
|
|
*
|
|
|
|
* @type Number[]
|
|
|
|
* @bean
|
|
|
|
*/
|
|
|
|
getValues: function() {
|
|
|
|
return [ this._a, this._c, this._b, this._d, this._tx, this._ty ];
|
|
|
|
},
|
|
|
|
|
2013-02-09 15:09:55 -05:00
|
|
|
/**
|
|
|
|
* The translation values of the matrix.
|
|
|
|
*
|
|
|
|
* @type Point
|
|
|
|
* @bean
|
|
|
|
*/
|
2011-06-20 13:25:19 -04:00
|
|
|
getTranslation: function() {
|
2013-02-09 15:09:55 -05:00
|
|
|
// No decomposition is required to extract translation, so treat this
|
2013-05-28 02:57:31 -04:00
|
|
|
return new Point(this._tx, this._ty);
|
2011-06-20 13:25:19 -04:00
|
|
|
},
|
|
|
|
|
2013-02-09 15:09:55 -05:00
|
|
|
/**
|
|
|
|
* The scaling values of the matrix, if it can be decomposed.
|
|
|
|
*
|
|
|
|
* @type Point
|
|
|
|
* @bean
|
|
|
|
* @see Matrix#decompose()
|
|
|
|
*/
|
2011-06-20 13:25:19 -04:00
|
|
|
getScaling: function() {
|
2013-02-09 15:09:55 -05:00
|
|
|
return (this.decompose() || {}).scaling;
|
2011-06-20 13:25:19 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2013-02-09 15:09:55 -05:00
|
|
|
* The rotation angle of the matrix, if it can be decomposed.
|
2011-08-16 07:53:36 -04:00
|
|
|
*
|
|
|
|
* @type Number
|
|
|
|
* @bean
|
2013-02-09 15:09:55 -05:00
|
|
|
* @see Matrix#decompose()
|
2011-06-20 13:25:19 -04:00
|
|
|
*/
|
|
|
|
getRotation: function() {
|
2013-02-09 15:09:55 -05:00
|
|
|
return (this.decompose() || {}).rotation;
|
2011-06-20 13:25:19 -04:00
|
|
|
},
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
2011-07-27 17:01:51 -04:00
|
|
|
* Inverts the transformation of the matrix. If the matrix is not invertible
|
|
|
|
* (in which case {@link #isSingular()} returns true), {@code null } is
|
|
|
|
* returned.
|
|
|
|
*
|
|
|
|
* @return {Matrix} The inverted matrix, or {@code null }, if the matrix is
|
2011-07-31 10:26:09 -04:00
|
|
|
* singular
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2013-02-09 15:18:40 -05:00
|
|
|
inverted: function() {
|
2011-09-22 15:19:41 -04:00
|
|
|
var det = this._getDeterminant();
|
2013-05-28 02:57:31 -04:00
|
|
|
return det && new Matrix(
|
2011-07-31 10:26:09 -04:00
|
|
|
this._d / det,
|
|
|
|
-this._c / det,
|
|
|
|
-this._b / det,
|
|
|
|
this._a / det,
|
|
|
|
(this._b * this._ty - this._d * this._tx) / det,
|
|
|
|
(this._c * this._tx - this._a * this._ty) / det);
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
2013-02-09 15:18:40 -05:00
|
|
|
shiftless: function() {
|
2013-05-28 02:57:31 -04:00
|
|
|
return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
|
2011-03-06 16:10:47 -05:00
|
|
|
},
|
|
|
|
|
2011-02-21 13:03:57 -05:00
|
|
|
/**
|
|
|
|
* Applies this matrix to the specified Canvas Context.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-31 08:25:24 -04:00
|
|
|
* @param {CanvasRenderingContext2D} ctx
|
2011-02-21 13:03:57 -05:00
|
|
|
*/
|
2013-03-20 00:35:41 -04:00
|
|
|
applyToContext: function(ctx) {
|
|
|
|
ctx.transform(this._a, this._c, this._b, this._d, this._tx, this._ty);
|
2011-02-13 20:05:16 -05:00
|
|
|
}
|
2011-03-07 12:38:50 -05:00
|
|
|
}, new function() {
|
|
|
|
return Base.each({
|
2011-07-31 10:26:09 -04:00
|
|
|
scaleX: '_a',
|
|
|
|
scaleY: '_d',
|
|
|
|
translateX: '_tx',
|
|
|
|
translateY: '_ty',
|
|
|
|
shearX: '_b',
|
|
|
|
shearY: '_c'
|
2011-03-07 12:38:50 -05:00
|
|
|
}, function(prop, name) {
|
|
|
|
name = Base.capitalize(name);
|
|
|
|
this['get' + name] = function() {
|
|
|
|
return this[prop];
|
|
|
|
};
|
|
|
|
this['set' + name] = function(value) {
|
|
|
|
this[prop] = value;
|
|
|
|
};
|
2011-06-17 13:42:29 -04:00
|
|
|
}, {});
|
2011-02-13 20:05:16 -05:00
|
|
|
});
|