Path: blob/trunk/third_party/closure/goog/math/affinetransform.js
2868 views
// Copyright 2008 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.131415/**16* @fileoverview Provides an object representation of an AffineTransform and17* methods for working with it.18*/192021goog.provide('goog.math.AffineTransform');22232425/**26* Creates a 2D affine transform. An affine transform performs a linear27* mapping from 2D coordinates to other 2D coordinates that preserves the28* "straightness" and "parallelness" of lines.29*30* Such a coordinate transformation can be represented by a 3 row by 3 column31* matrix with an implied last row of [ 0 0 1 ]. This matrix transforms source32* coordinates (x,y) into destination coordinates (x',y') by considering them33* to be a column vector and multiplying the coordinate vector by the matrix34* according to the following process:35* <pre>36* [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]37* [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]38* [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]39* </pre>40*41* This class is optimized for speed and minimizes calculations based on its42* knowledge of the underlying matrix (as opposed to say simply performing43* matrix multiplication).44*45* @param {number=} opt_m00 The m00 coordinate of the transform.46* @param {number=} opt_m10 The m10 coordinate of the transform.47* @param {number=} opt_m01 The m01 coordinate of the transform.48* @param {number=} opt_m11 The m11 coordinate of the transform.49* @param {number=} opt_m02 The m02 coordinate of the transform.50* @param {number=} opt_m12 The m12 coordinate of the transform.51* @struct52* @constructor53* @final54*/55goog.math.AffineTransform = function(56opt_m00, opt_m10, opt_m01, opt_m11, opt_m02, opt_m12) {57if (arguments.length == 6) {58this.setTransform(59/** @type {number} */ (opt_m00),60/** @type {number} */ (opt_m10),61/** @type {number} */ (opt_m01),62/** @type {number} */ (opt_m11),63/** @type {number} */ (opt_m02),64/** @type {number} */ (opt_m12));65} else if (arguments.length != 0) {66throw Error('Insufficient matrix parameters');67} else {68this.m00_ = this.m11_ = 1;69this.m10_ = this.m01_ = this.m02_ = this.m12_ = 0;70}71};727374/**75* @return {boolean} Whether this transform is the identity transform.76*/77goog.math.AffineTransform.prototype.isIdentity = function() {78return this.m00_ == 1 && this.m10_ == 0 && this.m01_ == 0 && this.m11_ == 1 &&79this.m02_ == 0 && this.m12_ == 0;80};818283/**84* @return {!goog.math.AffineTransform} A copy of this transform.85*/86goog.math.AffineTransform.prototype.clone = function() {87return new goog.math.AffineTransform(88this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_);89};909192/**93* Sets this transform to the matrix specified by the 6 values.94*95* @param {number} m00 The m00 coordinate of the transform.96* @param {number} m10 The m10 coordinate of the transform.97* @param {number} m01 The m01 coordinate of the transform.98* @param {number} m11 The m11 coordinate of the transform.99* @param {number} m02 The m02 coordinate of the transform.100* @param {number} m12 The m12 coordinate of the transform.101* @return {!goog.math.AffineTransform} This affine transform.102*/103goog.math.AffineTransform.prototype.setTransform = function(104m00, m10, m01, m11, m02, m12) {105if (!goog.isNumber(m00) || !goog.isNumber(m10) || !goog.isNumber(m01) ||106!goog.isNumber(m11) || !goog.isNumber(m02) || !goog.isNumber(m12)) {107throw Error('Invalid transform parameters');108}109this.m00_ = m00;110this.m10_ = m10;111this.m01_ = m01;112this.m11_ = m11;113this.m02_ = m02;114this.m12_ = m12;115return this;116};117118119/**120* Sets this transform to be identical to the given transform.121*122* @param {!goog.math.AffineTransform} tx The transform to copy.123* @return {!goog.math.AffineTransform} This affine transform.124*/125goog.math.AffineTransform.prototype.copyFrom = function(tx) {126this.m00_ = tx.m00_;127this.m10_ = tx.m10_;128this.m01_ = tx.m01_;129this.m11_ = tx.m11_;130this.m02_ = tx.m02_;131this.m12_ = tx.m12_;132return this;133};134135136/**137* Concatenates this transform with a scaling transformation.138*139* @param {number} sx The x-axis scaling factor.140* @param {number} sy The y-axis scaling factor.141* @return {!goog.math.AffineTransform} This affine transform.142*/143goog.math.AffineTransform.prototype.scale = function(sx, sy) {144this.m00_ *= sx;145this.m10_ *= sx;146this.m01_ *= sy;147this.m11_ *= sy;148return this;149};150151152/**153* Pre-concatenates this transform with a scaling transformation,154* i.e. calculates the following matrix product:155*156* <pre>157* [sx 0 0] [m00 m01 m02]158* [ 0 sy 0] [m10 m11 m12]159* [ 0 0 1] [ 0 0 1]160* </pre>161*162* @param {number} sx The x-axis scaling factor.163* @param {number} sy The y-axis scaling factor.164* @return {!goog.math.AffineTransform} This affine transform.165*/166goog.math.AffineTransform.prototype.preScale = function(sx, sy) {167this.m00_ *= sx;168this.m01_ *= sx;169this.m02_ *= sx;170this.m10_ *= sy;171this.m11_ *= sy;172this.m12_ *= sy;173return this;174};175176177/**178* Concatenates this transform with a translate transformation.179*180* @param {number} dx The distance to translate in the x direction.181* @param {number} dy The distance to translate in the y direction.182* @return {!goog.math.AffineTransform} This affine transform.183*/184goog.math.AffineTransform.prototype.translate = function(dx, dy) {185this.m02_ += dx * this.m00_ + dy * this.m01_;186this.m12_ += dx * this.m10_ + dy * this.m11_;187return this;188};189190191/**192* Pre-concatenates this transform with a translate transformation,193* i.e. calculates the following matrix product:194*195* <pre>196* [1 0 dx] [m00 m01 m02]197* [0 1 dy] [m10 m11 m12]198* [0 0 1] [ 0 0 1]199* </pre>200*201* @param {number} dx The distance to translate in the x direction.202* @param {number} dy The distance to translate in the y direction.203* @return {!goog.math.AffineTransform} This affine transform.204*/205goog.math.AffineTransform.prototype.preTranslate = function(dx, dy) {206this.m02_ += dx;207this.m12_ += dy;208return this;209};210211212/**213* Concatenates this transform with a rotation transformation around an anchor214* point.215*216* @param {number} theta The angle of rotation measured in radians.217* @param {number} x The x coordinate of the anchor point.218* @param {number} y The y coordinate of the anchor point.219* @return {!goog.math.AffineTransform} This affine transform.220*/221goog.math.AffineTransform.prototype.rotate = function(theta, x, y) {222return this.concatenate(223goog.math.AffineTransform.getRotateInstance(theta, x, y));224};225226227/**228* Pre-concatenates this transform with a rotation transformation around an229* anchor point.230*231* @param {number} theta The angle of rotation measured in radians.232* @param {number} x The x coordinate of the anchor point.233* @param {number} y The y coordinate of the anchor point.234* @return {!goog.math.AffineTransform} This affine transform.235*/236goog.math.AffineTransform.prototype.preRotate = function(theta, x, y) {237return this.preConcatenate(238goog.math.AffineTransform.getRotateInstance(theta, x, y));239};240241242/**243* Concatenates this transform with a shear transformation.244*245* @param {number} shx The x shear factor.246* @param {number} shy The y shear factor.247* @return {!goog.math.AffineTransform} This affine transform.248*/249goog.math.AffineTransform.prototype.shear = function(shx, shy) {250var m00 = this.m00_;251var m10 = this.m10_;252this.m00_ += shy * this.m01_;253this.m10_ += shy * this.m11_;254this.m01_ += shx * m00;255this.m11_ += shx * m10;256return this;257};258259260/**261* Pre-concatenates this transform with a shear transformation.262* i.e. calculates the following matrix product:263*264* <pre>265* [ 1 shx 0] [m00 m01 m02]266* [shy 1 0] [m10 m11 m12]267* [ 0 0 1] [ 0 0 1]268* </pre>269*270* @param {number} shx The x shear factor.271* @param {number} shy The y shear factor.272* @return {!goog.math.AffineTransform} This affine transform.273*/274goog.math.AffineTransform.prototype.preShear = function(shx, shy) {275var m00 = this.m00_;276var m01 = this.m01_;277var m02 = this.m02_;278this.m00_ += shx * this.m10_;279this.m01_ += shx * this.m11_;280this.m02_ += shx * this.m12_;281this.m10_ += shy * m00;282this.m11_ += shy * m01;283this.m12_ += shy * m02;284return this;285};286287288/**289* @return {string} A string representation of this transform. The format of290* of the string is compatible with SVG matrix notation, i.e.291* "matrix(a,b,c,d,e,f)".292* @override293*/294goog.math.AffineTransform.prototype.toString = function() {295return 'matrix(' +296[this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_].join(297',') +298')';299};300301302/**303* @return {number} The scaling factor in the x-direction (m00).304*/305goog.math.AffineTransform.prototype.getScaleX = function() {306return this.m00_;307};308309310/**311* @return {number} The scaling factor in the y-direction (m11).312*/313goog.math.AffineTransform.prototype.getScaleY = function() {314return this.m11_;315};316317318/**319* @return {number} The translation in the x-direction (m02).320*/321goog.math.AffineTransform.prototype.getTranslateX = function() {322return this.m02_;323};324325326/**327* @return {number} The translation in the y-direction (m12).328*/329goog.math.AffineTransform.prototype.getTranslateY = function() {330return this.m12_;331};332333334/**335* @return {number} The shear factor in the x-direction (m01).336*/337goog.math.AffineTransform.prototype.getShearX = function() {338return this.m01_;339};340341342/**343* @return {number} The shear factor in the y-direction (m10).344*/345goog.math.AffineTransform.prototype.getShearY = function() {346return this.m10_;347};348349350/**351* Concatenates an affine transform to this transform.352*353* @param {!goog.math.AffineTransform} tx The transform to concatenate.354* @return {!goog.math.AffineTransform} This affine transform.355*/356goog.math.AffineTransform.prototype.concatenate = function(tx) {357var m0 = this.m00_;358var m1 = this.m01_;359this.m00_ = tx.m00_ * m0 + tx.m10_ * m1;360this.m01_ = tx.m01_ * m0 + tx.m11_ * m1;361this.m02_ += tx.m02_ * m0 + tx.m12_ * m1;362363m0 = this.m10_;364m1 = this.m11_;365this.m10_ = tx.m00_ * m0 + tx.m10_ * m1;366this.m11_ = tx.m01_ * m0 + tx.m11_ * m1;367this.m12_ += tx.m02_ * m0 + tx.m12_ * m1;368return this;369};370371372/**373* Pre-concatenates an affine transform to this transform.374*375* @param {!goog.math.AffineTransform} tx The transform to preconcatenate.376* @return {!goog.math.AffineTransform} This affine transform.377*/378goog.math.AffineTransform.prototype.preConcatenate = function(tx) {379var m0 = this.m00_;380var m1 = this.m10_;381this.m00_ = tx.m00_ * m0 + tx.m01_ * m1;382this.m10_ = tx.m10_ * m0 + tx.m11_ * m1;383384m0 = this.m01_;385m1 = this.m11_;386this.m01_ = tx.m00_ * m0 + tx.m01_ * m1;387this.m11_ = tx.m10_ * m0 + tx.m11_ * m1;388389m0 = this.m02_;390m1 = this.m12_;391this.m02_ = tx.m00_ * m0 + tx.m01_ * m1 + tx.m02_;392this.m12_ = tx.m10_ * m0 + tx.m11_ * m1 + tx.m12_;393return this;394};395396397/**398* Transforms an array of coordinates by this transform and stores the result399* into a destination array.400*401* @param {!Array<number>} src The array containing the source points402* as x, y value pairs.403* @param {number} srcOff The offset to the first point to be transformed.404* @param {!Array<number>} dst The array into which to store the transformed405* point pairs.406* @param {number} dstOff The offset of the location of the first transformed407* point in the destination array.408* @param {number} numPts The number of points to transform.409*/410goog.math.AffineTransform.prototype.transform = function(411src, srcOff, dst, dstOff, numPts) {412var i = srcOff;413var j = dstOff;414var srcEnd = srcOff + 2 * numPts;415while (i < srcEnd) {416var x = src[i++];417var y = src[i++];418dst[j++] = x * this.m00_ + y * this.m01_ + this.m02_;419dst[j++] = x * this.m10_ + y * this.m11_ + this.m12_;420}421};422423424/**425* @return {number} The determinant of this transform.426*/427goog.math.AffineTransform.prototype.getDeterminant = function() {428return this.m00_ * this.m11_ - this.m01_ * this.m10_;429};430431432/**433* Returns whether the transform is invertible. A transform is not invertible434* if the determinant is 0 or any value is non-finite or NaN.435*436* @return {boolean} Whether the transform is invertible.437*/438goog.math.AffineTransform.prototype.isInvertible = function() {439var det = this.getDeterminant();440return isFinite(det) && isFinite(this.m02_) && isFinite(this.m12_) &&441det != 0;442};443444445/**446* @return {!goog.math.AffineTransform} An AffineTransform object447* representing the inverse transformation.448*/449goog.math.AffineTransform.prototype.createInverse = function() {450var det = this.getDeterminant();451return new goog.math.AffineTransform(452this.m11_ / det, -this.m10_ / det, -this.m01_ / det, this.m00_ / det,453(this.m01_ * this.m12_ - this.m11_ * this.m02_) / det,454(this.m10_ * this.m02_ - this.m00_ * this.m12_) / det);455};456457458/**459* Creates a transform representing a scaling transformation.460*461* @param {number} sx The x-axis scaling factor.462* @param {number} sy The y-axis scaling factor.463* @return {!goog.math.AffineTransform} A transform representing a scaling464* transformation.465*/466goog.math.AffineTransform.getScaleInstance = function(sx, sy) {467return new goog.math.AffineTransform().setToScale(sx, sy);468};469470471/**472* Creates a transform representing a translation transformation.473*474* @param {number} dx The distance to translate in the x direction.475* @param {number} dy The distance to translate in the y direction.476* @return {!goog.math.AffineTransform} A transform representing a477* translation transformation.478*/479goog.math.AffineTransform.getTranslateInstance = function(dx, dy) {480return new goog.math.AffineTransform().setToTranslation(dx, dy);481};482483484/**485* Creates a transform representing a shearing transformation.486*487* @param {number} shx The x-axis shear factor.488* @param {number} shy The y-axis shear factor.489* @return {!goog.math.AffineTransform} A transform representing a shearing490* transformation.491*/492goog.math.AffineTransform.getShearInstance = function(shx, shy) {493return new goog.math.AffineTransform().setToShear(shx, shy);494};495496497/**498* Creates a transform representing a rotation transformation.499*500* @param {number} theta The angle of rotation measured in radians.501* @param {number} x The x coordinate of the anchor point.502* @param {number} y The y coordinate of the anchor point.503* @return {!goog.math.AffineTransform} A transform representing a rotation504* transformation.505*/506goog.math.AffineTransform.getRotateInstance = function(theta, x, y) {507return new goog.math.AffineTransform().setToRotation(theta, x, y);508};509510511/**512* Sets this transform to a scaling transformation.513*514* @param {number} sx The x-axis scaling factor.515* @param {number} sy The y-axis scaling factor.516* @return {!goog.math.AffineTransform} This affine transform.517*/518goog.math.AffineTransform.prototype.setToScale = function(sx, sy) {519return this.setTransform(sx, 0, 0, sy, 0, 0);520};521522523/**524* Sets this transform to a translation transformation.525*526* @param {number} dx The distance to translate in the x direction.527* @param {number} dy The distance to translate in the y direction.528* @return {!goog.math.AffineTransform} This affine transform.529*/530goog.math.AffineTransform.prototype.setToTranslation = function(dx, dy) {531return this.setTransform(1, 0, 0, 1, dx, dy);532};533534535/**536* Sets this transform to a shearing transformation.537*538* @param {number} shx The x-axis shear factor.539* @param {number} shy The y-axis shear factor.540* @return {!goog.math.AffineTransform} This affine transform.541*/542goog.math.AffineTransform.prototype.setToShear = function(shx, shy) {543return this.setTransform(1, shy, shx, 1, 0, 0);544};545546547/**548* Sets this transform to a rotation transformation.549*550* @param {number} theta The angle of rotation measured in radians.551* @param {number} x The x coordinate of the anchor point.552* @param {number} y The y coordinate of the anchor point.553* @return {!goog.math.AffineTransform} This affine transform.554*/555goog.math.AffineTransform.prototype.setToRotation = function(theta, x, y) {556var cos = Math.cos(theta);557var sin = Math.sin(theta);558return this.setTransform(559cos, sin, -sin, cos, x - x * cos + y * sin, y - x * sin - y * cos);560};561562563/**564* Compares two affine transforms for equality.565*566* @param {goog.math.AffineTransform} tx The other affine transform.567* @return {boolean} whether the two transforms are equal.568*/569goog.math.AffineTransform.prototype.equals = function(tx) {570if (this == tx) {571return true;572}573if (!tx) {574return false;575}576return this.m00_ == tx.m00_ && this.m01_ == tx.m01_ && this.m02_ == tx.m02_ &&577this.m10_ == tx.m10_ && this.m11_ == tx.m11_ && this.m12_ == tx.m12_;578};579580581