// Copyright 2008 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Provides an object representation of an AffineTransform and * methods for working with it. */ goog.provide('goog.graphics.AffineTransform'); goog.require('goog.math'); /** * Creates a 2D affine transform. An affine transform performs a linear * mapping from 2D coordinates to other 2D coordinates that preserves the * "straightness" and "parallelness" of lines. * * 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: *
* [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ] * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ] * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] ** * 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). * * @param {number=} opt_m00 The m00 coordinate of the transform. * @param {number=} opt_m10 The m10 coordinate of the transform. * @param {number=} opt_m01 The m01 coordinate of the transform. * @param {number=} opt_m11 The m11 coordinate of the transform. * @param {number=} opt_m02 The m02 coordinate of the transform. * @param {number=} opt_m12 The m12 coordinate of the transform. * @constructor */ goog.graphics.AffineTransform = function(opt_m00, opt_m10, opt_m01, opt_m11, opt_m02, opt_m12) { if (arguments.length == 6) { this.setTransform(/** @type {number} */ (opt_m00), /** @type {number} */ (opt_m10), /** @type {number} */ (opt_m01), /** @type {number} */ (opt_m11), /** @type {number} */ (opt_m02), /** @type {number} */ (opt_m12)); } else if (arguments.length != 0) { throw Error('Insufficient matrix parameters'); } else { this.m00_ = this.m11_ = 1; this.m10_ = this.m01_ = this.m02_ = this.m12_ = 0; } }; /** * @return {boolean} Whether this transform is the identity transform. */ goog.graphics.AffineTransform.prototype.isIdentity = function() { return this.m00_ == 1 && this.m10_ == 0 && this.m01_ == 0 && this.m11_ == 1 && this.m02_ == 0 && this.m12_ == 0; }; /** * @return {!goog.graphics.AffineTransform} A copy of this transform. */ goog.graphics.AffineTransform.prototype.clone = function() { return new goog.graphics.AffineTransform(this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_); }; /** * Sets this transform to the matrix specified by the 6 values. * * @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. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.setTransform = function(m00, m10, m01, m11, m02, m12) { if (!goog.isNumber(m00) || !goog.isNumber(m10) || !goog.isNumber(m01) || !goog.isNumber(m11) || !goog.isNumber(m02) || !goog.isNumber(m12)) { throw Error('Invalid transform parameters'); } this.m00_ = m00; this.m10_ = m10; this.m01_ = m01; this.m11_ = m11; this.m02_ = m02; this.m12_ = m12; return this; }; /** * Sets this transform to be identical to the given transform. * * @param {!goog.graphics.AffineTransform} tx The transform to copy. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.copyFrom = function(tx) { this.m00_ = tx.m00_; this.m10_ = tx.m10_; this.m01_ = tx.m01_; this.m11_ = tx.m11_; this.m02_ = tx.m02_; this.m12_ = tx.m12_; return this; }; /** * Concatenates this transform with a scaling transformation. * * @param {number} sx The x-axis scaling factor. * @param {number} sy The y-axis scaling factor. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.scale = function(sx, sy) { this.m00_ *= sx; this.m10_ *= sx; this.m01_ *= sy; this.m11_ *= sy; return this; }; /** * Pre-concatenates this transform with a scaling transformation, * i.e. calculates the following matrix product: * *
* [sx 0 0] [m00 m01 m02] * [ 0 sy 0] [m10 m11 m12] * [ 0 0 1] [ 0 0 1] ** * @param {number} sx The x-axis scaling factor. * @param {number} sy The y-axis scaling factor. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.preScale = function(sx, sy) { this.m00_ *= sx; this.m01_ *= sx; this.m02_ *= sx; this.m10_ *= sy; this.m11_ *= sy; this.m12_ *= sy; return this; }; /** * Concatenates this transform with a translate transformation. * * @param {number} dx The distance to translate in the x direction. * @param {number} dy The distance to translate in the y direction. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.translate = function(dx, dy) { this.m02_ += dx * this.m00_ + dy * this.m01_; this.m12_ += dx * this.m10_ + dy * this.m11_; return this; }; /** * Pre-concatenates this transform with a translate transformation, * i.e. calculates the following matrix product: * *
* [1 0 dx] [m00 m01 m02] * [0 1 dy] [m10 m11 m12] * [0 0 1] [ 0 0 1] ** * @param {number} dx The distance to translate in the x direction. * @param {number} dy The distance to translate in the y direction. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.preTranslate = function(dx, dy) { this.m02_ += dx; this.m12_ += dy; return this; }; /** * Concatenates this transform with a rotation transformation around an anchor * point. * * @param {number} theta The angle of rotation measured in radians. * @param {number} x The x coordinate of the anchor point. * @param {number} y The y coordinate of the anchor point. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.rotate = function(theta, x, y) { return this.concatenate( goog.graphics.AffineTransform.getRotateInstance(theta, x, y)); }; /** * Pre-concatenates this transform with a rotation transformation around an * anchor point. * * @param {number} theta The angle of rotation measured in radians. * @param {number} x The x coordinate of the anchor point. * @param {number} y The y coordinate of the anchor point. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.preRotate = function(theta, x, y) { return this.preConcatenate( goog.graphics.AffineTransform.getRotateInstance(theta, x, y)); }; /** * Concatenates this transform with a shear transformation. * * @param {number} shx The x shear factor. * @param {number} shy The y shear factor. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.shear = function(shx, shy) { var m00 = this.m00_; var m10 = this.m10_; this.m00_ += shy * this.m01_; this.m10_ += shy * this.m11_; this.m01_ += shx * m00; this.m11_ += shx * m10; return this; }; /** * Pre-concatenates this transform with a shear transformation. * i.e. calculates the following matrix product: * *
* [ 1 shx 0] [m00 m01 m02] * [shy 1 0] [m10 m11 m12] * [ 0 0 1] [ 0 0 1] ** * @param {number} shx The x shear factor. * @param {number} shy The y shear factor. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.preShear = function(shx, shy) { var m00 = this.m00_; var m01 = this.m01_; var m02 = this.m02_; this.m00_ += shx * this.m10_; this.m01_ += shx * this.m11_; this.m02_ += shx * this.m12_; this.m10_ += shy * m00; this.m11_ += shy * m01; this.m12_ += shy * m02; return this; }; /** * @return {string} A string representation of this transform. The format of * of the string is compatible with SVG matrix notation, i.e. * "matrix(a,b,c,d,e,f)". */ goog.graphics.AffineTransform.prototype.toString = function() { return 'matrix(' + [this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_].join( ',') + ')'; }; /** * @return {number} The scaling factor in the x-direction (m00). */ goog.graphics.AffineTransform.prototype.getScaleX = function() { return this.m00_; }; /** * @return {number} The scaling factor in the y-direction (m11). */ goog.graphics.AffineTransform.prototype.getScaleY = function() { return this.m11_; }; /** * @return {number} The translation in the x-direction (m02). */ goog.graphics.AffineTransform.prototype.getTranslateX = function() { return this.m02_; }; /** * @return {number} The translation in the y-direction (m12). */ goog.graphics.AffineTransform.prototype.getTranslateY = function() { return this.m12_; }; /** * @return {number} The shear factor in the x-direction (m01). */ goog.graphics.AffineTransform.prototype.getShearX = function() { return this.m01_; }; /** * @return {number} The shear factor in the y-direction (m10). */ goog.graphics.AffineTransform.prototype.getShearY = function() { return this.m10_; }; /** * Concatenates an affine transform to this transform. * * @param {!goog.graphics.AffineTransform} tx The transform to concatenate. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.concatenate = function(tx) { var m0 = this.m00_; var m1 = this.m01_; this.m00_ = tx.m00_ * m0 + tx.m10_ * m1; this.m01_ = tx.m01_ * m0 + tx.m11_ * m1; this.m02_ += tx.m02_ * m0 + tx.m12_ * m1; m0 = this.m10_; m1 = this.m11_; this.m10_ = tx.m00_ * m0 + tx.m10_ * m1; this.m11_ = tx.m01_ * m0 + tx.m11_ * m1; this.m12_ += tx.m02_ * m0 + tx.m12_ * m1; return this; }; /** * Pre-concatenates an affine transform to this transform. * * @param {!goog.graphics.AffineTransform} tx The transform to preconcatenate. * @return {!goog.graphics.AffineTransform} This affine transform. */ goog.graphics.AffineTransform.prototype.preConcatenate = function(tx) { var m0 = this.m00_; var m1 = this.m10_; this.m00_ = tx.m00_ * m0 + tx.m01_ * m1; this.m10_ = tx.m10_ * m0 + tx.m11_ * m1; m0 = this.m01_; m1 = this.m11_; this.m01_ = tx.m00_ * m0 + tx.m01_ * m1; this.m11_ = tx.m10_ * m0 + tx.m11_ * m1; m0 = this.m02_; m1 = this.m12_; this.m02_ = tx.m00_ * m0 + tx.m01_ * m1 + tx.m02_; this.m12_ = tx.m10_ * m0 + tx.m11_ * m1 + tx.m12_; return this; }; /** * Transforms an array of coordinates by this transform and stores the result * into a destination array. * * @param {!Array.