2011-03-06 19:50:44 -05:00
|
|
|
/*
|
|
|
|
* Paper.js
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-03-06 19:50:44 -05:00
|
|
|
* This file is part of Paper.js, a JavaScript Vector Graphics Library,
|
|
|
|
* based on Scriptographer.org and designed to be largely API compatible.
|
2011-03-07 20:41:50 -05:00
|
|
|
* http://paperjs.org/
|
2011-03-06 19:50:44 -05:00
|
|
|
* http://scriptographer.org/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-03-06 19:50:44 -05:00
|
|
|
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
|
|
|
|
* 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-25 04:16:41 -04:00
|
|
|
* [ x ] [ m00 m01 m02 ] [ x ] [ m00 * x + m01 * y + m02 ]
|
|
|
|
* [ y ] = [ m10 m11 m12 ] [ y ] = [ m10 * x + m11 * y + m12 ]
|
|
|
|
* [ 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).
|
|
|
|
*/
|
|
|
|
var Matrix = this.Matrix = Base.extend(/** @lends 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-05-27 15:21:49 -04:00
|
|
|
* @param {Number} m00 The m00 coordinate of the transform.
|
|
|
|
* @param {Number} m10 The m10 coordinate of the transform.
|
|
|
|
* @param {Number} m01 The m01 coordinate of the transform.
|
|
|
|
* @param {Number} m11 The m11 coordinate of the transform.
|
|
|
|
* @param {Number} m02 The m02 coordinate of the transform.
|
|
|
|
* @param {Number} m12 The m12 coordinate of the transform.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2011-07-25 04:15:35 -04:00
|
|
|
initialize: function(arg) {
|
|
|
|
var count = arguments.length,
|
|
|
|
ok = true;
|
|
|
|
if (count == 6) {
|
|
|
|
this.set.apply(this, arguments);
|
|
|
|
} else if (count == 1) {
|
|
|
|
if (arg instanceof Matrix) {
|
|
|
|
this.set(arg._m00, arg._m10, arg._m01,
|
|
|
|
arg._m11, arg._m02, arg._m12);
|
|
|
|
} 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) {
|
2011-02-13 20:05:16 -05:00
|
|
|
this._m00 = this._m11 = 1;
|
|
|
|
this._m10 = this._m01 = this._m02 = this._m12 = 0;
|
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
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {Matrix} A copy of this transform.
|
|
|
|
*/
|
|
|
|
clone: function() {
|
2011-03-06 16:05:24 -05:00
|
|
|
return Matrix.create(this._m00, this._m10, this._m01,
|
2011-02-13 20:05:16 -05:00
|
|
|
this._m11, this._m02, this._m12);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets this transform to the matrix specified by the 6 values.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-27 15:21:49 -04:00
|
|
|
* @param {Number} m00 The m00 coordinate of the transform.
|
|
|
|
* @param {Number} m10 The m10 coordinate of the transform.
|
|
|
|
* @param {Number} m01 The m01 coordinate of the transform.
|
|
|
|
* @param {Number} m11 The m11 coordinate of the transform.
|
|
|
|
* @param {Number} m02 The m02 coordinate of the transform.
|
|
|
|
* @param {Number} m12 The m12 coordinate of the transform.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
|
|
|
set: function(m00, m10, m01, m11, m02, m12) {
|
|
|
|
this._m00 = m00;
|
|
|
|
this._m10 = m10;
|
|
|
|
this._m01 = m01;
|
|
|
|
this._m11 = m11;
|
|
|
|
this._m02 = m02;
|
|
|
|
this._m12 = m12;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
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
|
|
|
|
* @param {Number} scale The scaling factor.
|
2011-06-19 16:52:52 -04:00
|
|
|
* @param {Point} [center] The center for the scaling
|
2011-05-29 11:31:13 -04:00
|
|
|
* transformation.
|
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
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-01 12:00:16 -04:00
|
|
|
* @param {Number} hor The horizontal scaling factor.
|
|
|
|
* @param {Number} ver The vertical scaling factor.
|
2011-06-19 16:52:52 -04:00
|
|
|
* @param {Point} [center] The center for the scaling
|
2011-05-29 11:31:13 -04:00
|
|
|
* transformation.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
2011-07-01 12:00:16 -04:00
|
|
|
scale: function(hor, ver /* | scale */, center) {
|
|
|
|
if (arguments.length < 2 || typeof ver === 'object') {
|
|
|
|
// hor is the single scale parameter, representing both hor and ver
|
|
|
|
// Read center first from argument 1, then set ver = hor (thus
|
2011-02-15 18:23:40 -05:00
|
|
|
// modifing the content of argument 1!)
|
|
|
|
center = Point.read(arguments, 1);
|
2011-07-01 12:00:16 -04:00
|
|
|
ver = hor;
|
2011-02-15 18:23:40 -05:00
|
|
|
} else {
|
|
|
|
center = Point.read(arguments, 2);
|
|
|
|
}
|
2011-02-13 20:05:16 -05:00
|
|
|
if (center)
|
2011-02-15 17:53:05 -05:00
|
|
|
this.translate(center);
|
2011-07-01 12:00:16 -04:00
|
|
|
this._m00 *= hor;
|
|
|
|
this._m10 *= hor;
|
|
|
|
this._m01 *= ver;
|
|
|
|
this._m11 *= ver;
|
2011-02-13 20:05:16 -05:00
|
|
|
if (center)
|
2011-02-15 17:53:05 -05:00
|
|
|
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
|
|
|
|
* @param {Point} point The vector to translate by.
|
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
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-05-27 15:21:49 -04:00
|
|
|
* @param {Number} dx The distance to translate in the x direction.
|
|
|
|
* @param {Number} dy The distance to translate in the y direction.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
|
|
|
translate: function(point) {
|
2011-03-13 13:31:00 -04:00
|
|
|
point = Point.read(arguments);
|
|
|
|
var x = point.x, y = point.y;
|
|
|
|
this._m02 += x * this._m00 + y * this._m01;
|
|
|
|
this._m12 += x * this._m10 + y * this._m11;
|
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
|
|
|
|
* @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-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-05-27 15:21:49 -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.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
|
|
|
rotate: function(angle, center) {
|
|
|
|
return this.concatenate(
|
|
|
|
Matrix.getRotateInstance.apply(Matrix, arguments));
|
|
|
|
},
|
|
|
|
|
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
|
|
|
|
* @param {Point} point The shear factor in x and y direction.
|
2011-06-19 16:52:52 -04:00
|
|
|
* @param {Point} [center] The center for the shear transformation.
|
2011-05-31 10:08:25 -04:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
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-01 12:00:16 -04:00
|
|
|
* @param {Number} hor The horizontal shear factor.
|
|
|
|
* @param {Number} ver The vertical shear factor.
|
2011-06-19 16:52:52 -04:00
|
|
|
* @param {Point} [center] The center for the shear transformation.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
2011-07-01 12:00:16 -04:00
|
|
|
shear: function(hor, ver, center) {
|
2011-02-15 18:23:40 -05:00
|
|
|
// See #scale() for explanation of this:
|
2011-07-01 12:00:16 -04:00
|
|
|
if (arguments.length < 2 || typeof ver === 'object') {
|
2011-02-15 18:23:40 -05:00
|
|
|
center = Point.read(arguments, 1);
|
2011-07-01 12:00:16 -04:00
|
|
|
ver = hor;
|
2011-02-15 18:23:40 -05:00
|
|
|
} else {
|
|
|
|
center = Point.read(arguments, 2);
|
|
|
|
}
|
2011-02-13 20:05:16 -05:00
|
|
|
if (center)
|
2011-02-15 17:53:05 -05:00
|
|
|
this.translate(center);
|
2011-02-13 20:05:16 -05:00
|
|
|
var m00 = this._m00;
|
|
|
|
var m10 = this._m10;
|
2011-07-01 12:00:16 -04:00
|
|
|
this._m00 += ver * this._m01;
|
|
|
|
this._m10 += ver * this._m11;
|
|
|
|
this._m01 += hor * m00;
|
|
|
|
this._m11 += hor * m10;
|
2011-02-13 20:05:16 -05:00
|
|
|
if (center)
|
2011-02-15 17:53:05 -05:00
|
|
|
this.translate(center.negate());
|
2011-02-13 20:05:16 -05:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2011-05-27 15:21:49 -04:00
|
|
|
* @return {String} A string representation of this transform.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
toString: function() {
|
2011-05-04 14:42:50 -04:00
|
|
|
var format = Base.formatNumber;
|
|
|
|
return '[[' + [format(this._m00), format(this._m01),
|
|
|
|
format(this._m02)].join(', ') + '], ['
|
|
|
|
+ [format(this._m10), format(this._m11),
|
|
|
|
format(this._m12)].join(', ') + ']]';
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
2011-06-30 06:01:51 -04:00
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
2011-07-25 04:15:18 -04:00
|
|
|
* The scaling factor in the x-direction (m00).
|
|
|
|
*
|
|
|
|
* @name Matrix#scaleX
|
|
|
|
* @type Number
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2011-07-25 04:15:18 -04:00
|
|
|
* The scaling factor in the y-direction (m11).
|
|
|
|
*
|
|
|
|
* @name Matrix#scaleY
|
|
|
|
* @type Number
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2011-07-25 04:15:18 -04:00
|
|
|
* The translation in the x-direction (m02).
|
|
|
|
*
|
|
|
|
* @name Matrix#translateX
|
|
|
|
* @type Number
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2011-07-25 04:15:18 -04:00
|
|
|
* The translation in the y-direction (m12).
|
|
|
|
*
|
|
|
|
* @name Matrix#translateY
|
|
|
|
* @type Number
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2011-05-27 15:21:49 -04:00
|
|
|
* @return {Number} The shear factor in the x-direction (m01).
|
2011-07-25 04:15:18 -04:00
|
|
|
*
|
|
|
|
* @name Matrix#shearX
|
|
|
|
* @type Number
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2011-05-27 15:21:49 -04:00
|
|
|
* @return {Number} The shear factor in the y-direction (m10).
|
2011-07-25 04:15:18 -04:00
|
|
|
*
|
|
|
|
* @name Matrix#shearY
|
|
|
|
* @type Number
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Concatenates an affine transform to this transform.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-02-13 20:05:16 -05:00
|
|
|
* @param {Matrix} mx The transform to concatenate.
|
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
|
|
|
concatenate: function(mx) {
|
2011-03-06 16:06:46 -05:00
|
|
|
var m0 = this._m00,
|
|
|
|
m1 = this._m01;
|
2011-02-13 20:05:16 -05:00
|
|
|
this._m00 = mx._m00 * m0 + mx._m10 * m1;
|
|
|
|
this._m01 = mx._m01 * m0 + mx._m11 * m1;
|
|
|
|
this._m02 += mx._m02 * m0 + mx._m12 * m1;
|
|
|
|
|
|
|
|
m0 = this._m10;
|
|
|
|
m1 = this._m11;
|
|
|
|
this._m10 = mx._m00 * m0 + mx._m10 * m1;
|
|
|
|
this._m11 = mx._m01 * m0 + mx._m11 * m1;
|
|
|
|
this._m12 += mx._m02 * m0 + mx._m12 * m1;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pre-concatenates an affine transform to this transform.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-02-13 20:05:16 -05:00
|
|
|
* @param {Matrix} mx The transform to preconcatenate.
|
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
|
|
|
preConcatenate: function(mx) {
|
2011-03-06 16:06:46 -05:00
|
|
|
var m0 = this._m00,
|
|
|
|
m1 = this._m10;
|
2011-02-13 20:05:16 -05:00
|
|
|
this._m00 = mx._m00 * m0 + mx._m01 * m1;
|
|
|
|
this._m10 = mx._m10 * m0 + mx._m11 * m1;
|
|
|
|
|
|
|
|
m0 = this._m01;
|
|
|
|
m1 = this._m11;
|
|
|
|
this._m01 = mx._m00 * m0 + mx._m01 * m1;
|
|
|
|
this._m11 = mx._m10 * m0 + mx._m11 * m1;
|
|
|
|
|
|
|
|
m0 = this._m02;
|
|
|
|
m1 = this._m12;
|
|
|
|
this._m02 = mx._m00 * m0 + mx._m01 * m1 + mx._m02;
|
|
|
|
this._m12 = mx._m10 * m0 + mx._m11 * m1 + mx._m12;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Transforms a point or an array of coordinates by this matrix and returns
|
|
|
|
* the result. If an array is transformed, the the result is stored into a
|
|
|
|
* destination array.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-02-13 20:05:16 -05:00
|
|
|
* @param {Point} point The point to be transformed.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-20 09:19:08 -04:00
|
|
|
* @param {Number[]} src The array containing the source points
|
2011-02-15 17:51:02 -05:00
|
|
|
* as x, y value pairs.
|
2011-05-27 15:21:49 -04:00
|
|
|
* @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-02-15 17:51:02 -05:00
|
|
|
* point pairs.
|
2011-05-27 15:21:49 -04:00
|
|
|
* @param {Number} dstOff The offset of the location of the first
|
2011-02-15 17:51:02 -05:00
|
|
|
* transformed point in the destination array.
|
2011-05-27 15:21:49 -04:00
|
|
|
* @param {Number} numPts The number of points to tranform.
|
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)
|
|
|
|
dest = new Point(Point.dont);
|
|
|
|
return dest.set(
|
2011-05-06 08:28:35 -04:00
|
|
|
x * this._m00 + y * this._m01 + this._m02,
|
2011-05-16 06:18:53 -04:00
|
|
|
x * this._m10 + y * this._m11 + this._m12,
|
|
|
|
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) {
|
|
|
|
var x = src[i++];
|
|
|
|
var y = src[i++];
|
|
|
|
dst[j++] = x * this._m00 + y * this._m01 + this._m02;
|
|
|
|
dst[j++] = x * this._m10 + y * this._m11 + this._m12;
|
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!
|
|
|
|
*/
|
|
|
|
_transformBounds: function(bounds) {
|
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),
|
|
|
|
max = coords.slice(0);
|
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-05-16 07:25:18 -04:00
|
|
|
return Rectangle.create(min[0], min[1],
|
|
|
|
max[0] - min[0], max[1] - min[1]);
|
2011-03-04 20:26:12 -05:00
|
|
|
},
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
2011-05-27 15:21:49 -04:00
|
|
|
* @return {Number} The determinant of this transform.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
getDeterminant: function() {
|
|
|
|
return this._m00 * this._m11 - this._m01 * this._m10;
|
|
|
|
},
|
|
|
|
|
2011-06-20 13:25:19 -04:00
|
|
|
getTranslation: function() {
|
|
|
|
return new Point(this._m02, this._m12);
|
|
|
|
},
|
|
|
|
|
|
|
|
getScaling: function() {
|
2011-07-01 12:00:16 -04:00
|
|
|
var hor = Math.sqrt(this._m00 * this._m00 + this._m10 * this._m10),
|
|
|
|
ver = Math.sqrt(this._m01 * this._m01 + this._m11 * this._m11);
|
|
|
|
return new Point(this._m00 < 0 ? -hor : hor, this._m01 < 0 ? -ver : ver);
|
2011-06-20 13:25:19 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {Number} The rotation angle of the matrix. If a non-uniform
|
|
|
|
* rotation is applied as a result of a shear() or scale() command,
|
|
|
|
* undefined is returned, as the resulting transformation cannot be
|
|
|
|
* expressed in one rotation angle.
|
|
|
|
*/
|
|
|
|
getRotation: function() {
|
|
|
|
var angle1 = -Math.atan2(this._m01, this._m11),
|
|
|
|
angle2 = Math.atan2(this._m10, this._m00);
|
|
|
|
return Math.abs(angle1 - angle2) < Numerical.TOLERANCE
|
|
|
|
? angle1 * 180 / Math.PI : undefined;
|
|
|
|
},
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
2011-05-30 13:42:17 -04:00
|
|
|
* @return {Boolean} Whether this transform is the identity transform.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
isIdentity: function() {
|
|
|
|
return this._m00 == 1 && this._m10 == 0 && this._m01 == 0 &&
|
|
|
|
this._m11 == 1 && this._m02 == 0 && this._m12 == 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2011-02-15 17:51:02 -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-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-30 13:42:17 -04:00
|
|
|
* @return {Boolean} Whether the transform is invertible.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
isInvertible: function() {
|
|
|
|
var det = this.getDeterminant();
|
|
|
|
return isFinite(det) && det != 0 && isFinite(this._m02)
|
|
|
|
&& isFinite(this._m12);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether the matrix is singular or not. Singular matrices cannot be
|
|
|
|
* inverted.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-30 13:42:17 -04:00
|
|
|
* @return {Boolean} Whether the matrix is singular.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
isSingular: function() {
|
|
|
|
return !this.isInvertible();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
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
|
|
|
|
* singular.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
createInverse: function() {
|
|
|
|
var det = this.getDeterminant();
|
|
|
|
if (isFinite(det) && det != 0 && isFinite(this._m02)
|
|
|
|
&& isFinite(this._m12)) {
|
2011-03-06 16:05:24 -05:00
|
|
|
return Matrix.create(
|
2011-02-13 20:05:16 -05:00
|
|
|
this._m11 / det,
|
|
|
|
-this._m10 / det,
|
|
|
|
-this._m01 / det,
|
|
|
|
this._m00 / det,
|
|
|
|
(this._m01 * this._m12 - this._m11 * this._m02) / det,
|
|
|
|
(this._m10 * this._m02 - this._m00 * this._m12) / det);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2011-03-06 16:10:47 -05:00
|
|
|
createShiftless: function() {
|
|
|
|
return Matrix.create(this._m00, this._m10, this._m01, this._m11, 0, 0);
|
|
|
|
},
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
|
|
|
* Sets this transform to a scaling transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-01 12:00:16 -04:00
|
|
|
* @param {Number} hor The horizontal scaling factor.
|
|
|
|
* @param {Number} ver The vertical scaling factor.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
2011-07-01 12:00:16 -04:00
|
|
|
setToScale: function(hor, ver) {
|
|
|
|
return this.set(hor, 0, 0, ver, 0, 0);
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets this transform to a translation transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-27 15:21:49 -04:00
|
|
|
* @param {Number} dx The distance to translate in the x direction.
|
|
|
|
* @param {Number} dy The distance to translate in the y direction.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
|
|
|
setToTranslation: function(delta) {
|
2011-03-13 13:31:00 -04:00
|
|
|
delta = Point.read(arguments);
|
|
|
|
return this.set(1, 0, 0, 1, delta.x, delta.y);
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets this transform to a shearing transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-01 12:00:16 -04:00
|
|
|
* @param {Number} hor The horizontal shear factor.
|
|
|
|
* @param {Number} ver The vertical shear factor.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
2011-07-01 12:00:16 -04:00
|
|
|
setToShear: function(hor, ver) {
|
|
|
|
return this.set(1, ver, hor, 1, 0, 0);
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets this transform to a rotation transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-27 15:21:49 -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.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} This affine transform.
|
|
|
|
*/
|
|
|
|
setToRotation: function(angle, center) {
|
2011-03-13 13:31:00 -04:00
|
|
|
center = Point.read(arguments, 1);
|
2011-05-31 10:19:43 -04:00
|
|
|
angle = angle * Math.PI / 180;
|
2011-03-13 13:31:00 -04:00
|
|
|
var x = center.x,
|
|
|
|
y = center.y,
|
|
|
|
cos = Math.cos(angle),
|
|
|
|
sin = Math.sin(angle);
|
|
|
|
return this.set(cos, sin, -sin, cos,
|
|
|
|
x - x * cos + y * sin,
|
|
|
|
y - x * sin - y * cos);
|
2011-02-13 20:05:16 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -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
|
|
|
|
* @param {Boolean} [reset=false]
|
2011-02-21 13:03:57 -05:00
|
|
|
*/
|
2011-04-26 10:39:16 -04:00
|
|
|
applyToContext: function(ctx, reset) {
|
|
|
|
ctx[reset ? 'setTransform' : 'transform'](
|
2011-03-06 16:09:52 -05:00
|
|
|
this._m00, this._m10, this._m01,
|
|
|
|
this._m11, this._m02, this._m12
|
2011-03-02 11:06:06 -05:00
|
|
|
);
|
2011-03-05 16:04:01 -05:00
|
|
|
return this;
|
2011-02-21 13:03:57 -05:00
|
|
|
},
|
2011-02-13 20:05:16 -05:00
|
|
|
|
2011-06-22 18:58:50 -04:00
|
|
|
statics: /** @lends Matrix */{
|
2011-03-06 16:05:24 -05:00
|
|
|
// See Point.create()
|
|
|
|
create: function(m00, m10, m01, m11, m02, m12) {
|
|
|
|
return new Matrix(Matrix.dont).set(m00, m10, m01, m11, m02, m12);
|
|
|
|
},
|
|
|
|
|
2011-02-13 20:05:16 -05:00
|
|
|
/**
|
|
|
|
* Creates a transform representing a scaling transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-01 12:00:16 -04:00
|
|
|
* @param {Number} hor The horizontal scaling factor.
|
|
|
|
* @param {Number} ver The vertical scaling factor.
|
2011-02-13 20:05:16 -05:00
|
|
|
* @return {Matrix} A transform representing a scaling
|
2011-02-15 17:51:02 -05:00
|
|
|
* transformation.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2011-07-01 12:00:16 -04:00
|
|
|
getScaleInstance: function(hor, ver) {
|
2011-02-13 20:05:16 -05:00
|
|
|
var mx = new Matrix();
|
|
|
|
return mx.setToScale.apply(mx, arguments);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a transform representing a translation transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-27 15:21:49 -04:00
|
|
|
* @param {Number} dx The distance to translate in the x direction.
|
|
|
|
* @param {Number} dy The distance to translate in the y direction.
|
2011-02-15 17:51:02 -05:00
|
|
|
* @return {Matrix} A transform representing a translation
|
|
|
|
* transformation.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
getTranslateInstance: function(delta) {
|
|
|
|
var mx = new Matrix();
|
|
|
|
return mx.setToTranslation.apply(mx, arguments);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a transform representing a shearing transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-01 12:00:16 -04:00
|
|
|
* @param {Number} hor The horizontal shear factor.
|
|
|
|
* @param {Number} ver The vertical shear factor.
|
2011-02-15 17:51:02 -05:00
|
|
|
* @return {Matrix} A transform representing a shearing transformation.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
2011-07-01 12:00:16 -04:00
|
|
|
getShearInstance: function(hor, ver, center) {
|
2011-02-13 20:05:16 -05:00
|
|
|
var mx = new Matrix();
|
|
|
|
return mx.setToShear.apply(mx, arguments);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a transform representing a rotation transformation.
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-05-27 15:21:49 -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.
|
2011-02-15 17:51:02 -05:00
|
|
|
* @return {Matrix} A transform representing a rotation transformation.
|
2011-02-13 20:05:16 -05:00
|
|
|
*/
|
|
|
|
getRotateInstance: function(angle, center) {
|
|
|
|
var mx = new Matrix();
|
|
|
|
return mx.setToRotation.apply(mx, arguments);
|
|
|
|
}
|
|
|
|
}
|
2011-03-07 12:38:50 -05:00
|
|
|
}, new function() {
|
|
|
|
return Base.each({
|
|
|
|
scaleX: '_m00',
|
|
|
|
scaleY: '_m11',
|
|
|
|
translateX: '_m02',
|
|
|
|
translateY: '_m12',
|
|
|
|
shearX: '_m01',
|
|
|
|
shearY: '_m10'
|
|
|
|
}, 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
|
|
|
});
|