Path: blob/trunk/third_party/closure/goog/graphics/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.graphics.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* @constructor52* @final53*/54goog.graphics.AffineTransform = function(55opt_m00, opt_m10, opt_m01, opt_m11, opt_m02, opt_m12) {56if (arguments.length == 6) {57this.setTransform(58/** @type {number} */ (opt_m00),59/** @type {number} */ (opt_m10),60/** @type {number} */ (opt_m01),61/** @type {number} */ (opt_m11),62/** @type {number} */ (opt_m02),63/** @type {number} */ (opt_m12));64} else if (arguments.length != 0) {65throw Error('Insufficient matrix parameters');66} else {67this.m00_ = this.m11_ = 1;68this.m10_ = this.m01_ = this.m02_ = this.m12_ = 0;69}70};717273/**74* @return {boolean} Whether this transform is the identity transform.75*/76goog.graphics.AffineTransform.prototype.isIdentity = function() {77return this.m00_ == 1 && this.m10_ == 0 && this.m01_ == 0 && this.m11_ == 1 &&78this.m02_ == 0 && this.m12_ == 0;79};808182/**83* @return {!goog.graphics.AffineTransform} A copy of this transform.84*/85goog.graphics.AffineTransform.prototype.clone = function() {86return new goog.graphics.AffineTransform(87this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_);88};899091/**92* Sets this transform to the matrix specified by the 6 values.93*94* @param {number} m00 The m00 coordinate of the transform.95* @param {number} m10 The m10 coordinate of the transform.96* @param {number} m01 The m01 coordinate of the transform.97* @param {number} m11 The m11 coordinate of the transform.98* @param {number} m02 The m02 coordinate of the transform.99* @param {number} m12 The m12 coordinate of the transform.100* @return {!goog.graphics.AffineTransform} This affine transform.101*/102goog.graphics.AffineTransform.prototype.setTransform = function(103m00, m10, m01, m11, m02, m12) {104if (!goog.isNumber(m00) || !goog.isNumber(m10) || !goog.isNumber(m01) ||105!goog.isNumber(m11) || !goog.isNumber(m02) || !goog.isNumber(m12)) {106throw Error('Invalid transform parameters');107}108this.m00_ = m00;109this.m10_ = m10;110this.m01_ = m01;111this.m11_ = m11;112this.m02_ = m02;113this.m12_ = m12;114return this;115};116117118/**119* Sets this transform to be identical to the given transform.120*121* @param {!goog.graphics.AffineTransform} tx The transform to copy.122* @return {!goog.graphics.AffineTransform} This affine transform.123*/124goog.graphics.AffineTransform.prototype.copyFrom = function(tx) {125this.m00_ = tx.m00_;126this.m10_ = tx.m10_;127this.m01_ = tx.m01_;128this.m11_ = tx.m11_;129this.m02_ = tx.m02_;130this.m12_ = tx.m12_;131return this;132};133134135/**136* Concatenates this transform with a scaling transformation.137*138* @param {number} sx The x-axis scaling factor.139* @param {number} sy The y-axis scaling factor.140* @return {!goog.graphics.AffineTransform} This affine transform.141*/142goog.graphics.AffineTransform.prototype.scale = function(sx, sy) {143this.m00_ *= sx;144this.m10_ *= sx;145this.m01_ *= sy;146this.m11_ *= sy;147return this;148};149150151/**152* Pre-concatenates this transform with a scaling transformation,153* i.e. calculates the following matrix product:154*155* <pre>156* [sx 0 0] [m00 m01 m02]157* [ 0 sy 0] [m10 m11 m12]158* [ 0 0 1] [ 0 0 1]159* </pre>160*161* @param {number} sx The x-axis scaling factor.162* @param {number} sy The y-axis scaling factor.163* @return {!goog.graphics.AffineTransform} This affine transform.164*/165goog.graphics.AffineTransform.prototype.preScale = function(sx, sy) {166this.m00_ *= sx;167this.m01_ *= sx;168this.m02_ *= sx;169this.m10_ *= sy;170this.m11_ *= sy;171this.m12_ *= sy;172return this;173};174175176/**177* Concatenates this transform with a translate transformation.178*179* @param {number} dx The distance to translate in the x direction.180* @param {number} dy The distance to translate in the y direction.181* @return {!goog.graphics.AffineTransform} This affine transform.182*/183goog.graphics.AffineTransform.prototype.translate = function(dx, dy) {184this.m02_ += dx * this.m00_ + dy * this.m01_;185this.m12_ += dx * this.m10_ + dy * this.m11_;186return this;187};188189190/**191* Pre-concatenates this transform with a translate transformation,192* i.e. calculates the following matrix product:193*194* <pre>195* [1 0 dx] [m00 m01 m02]196* [0 1 dy] [m10 m11 m12]197* [0 0 1] [ 0 0 1]198* </pre>199*200* @param {number} dx The distance to translate in the x direction.201* @param {number} dy The distance to translate in the y direction.202* @return {!goog.graphics.AffineTransform} This affine transform.203*/204goog.graphics.AffineTransform.prototype.preTranslate = function(dx, dy) {205this.m02_ += dx;206this.m12_ += dy;207return this;208};209210211/**212* Concatenates this transform with a rotation transformation around an anchor213* point.214*215* @param {number} theta The angle of rotation measured in radians.216* @param {number} x The x coordinate of the anchor point.217* @param {number} y The y coordinate of the anchor point.218* @return {!goog.graphics.AffineTransform} This affine transform.219*/220goog.graphics.AffineTransform.prototype.rotate = function(theta, x, y) {221return this.concatenate(222goog.graphics.AffineTransform.getRotateInstance(theta, x, y));223};224225226/**227* Pre-concatenates this transform with a rotation transformation around an228* anchor point.229*230* @param {number} theta The angle of rotation measured in radians.231* @param {number} x The x coordinate of the anchor point.232* @param {number} y The y coordinate of the anchor point.233* @return {!goog.graphics.AffineTransform} This affine transform.234*/235goog.graphics.AffineTransform.prototype.preRotate = function(theta, x, y) {236return this.preConcatenate(237goog.graphics.AffineTransform.getRotateInstance(theta, x, y));238};239240241/**242* Concatenates this transform with a shear transformation.243*244* @param {number} shx The x shear factor.245* @param {number} shy The y shear factor.246* @return {!goog.graphics.AffineTransform} This affine transform.247*/248goog.graphics.AffineTransform.prototype.shear = function(shx, shy) {249var m00 = this.m00_;250var m10 = this.m10_;251this.m00_ += shy * this.m01_;252this.m10_ += shy * this.m11_;253this.m01_ += shx * m00;254this.m11_ += shx * m10;255return this;256};257258259/**260* Pre-concatenates this transform with a shear transformation.261* i.e. calculates the following matrix product:262*263* <pre>264* [ 1 shx 0] [m00 m01 m02]265* [shy 1 0] [m10 m11 m12]266* [ 0 0 1] [ 0 0 1]267* </pre>268*269* @param {number} shx The x shear factor.270* @param {number} shy The y shear factor.271* @return {!goog.graphics.AffineTransform} This affine transform.272*/273goog.graphics.AffineTransform.prototype.preShear = function(shx, shy) {274var m00 = this.m00_;275var m01 = this.m01_;276var m02 = this.m02_;277this.m00_ += shx * this.m10_;278this.m01_ += shx * this.m11_;279this.m02_ += shx * this.m12_;280this.m10_ += shy * m00;281this.m11_ += shy * m01;282this.m12_ += shy * m02;283return this;284};285286287/**288* @return {string} A string representation of this transform. The format of289* of the string is compatible with SVG matrix notation, i.e.290* "matrix(a,b,c,d,e,f)".291* @override292*/293goog.graphics.AffineTransform.prototype.toString = function() {294return 'matrix(' +295[this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_].join(296',') +297')';298};299300301/**302* @return {number} The scaling factor in the x-direction (m00).303*/304goog.graphics.AffineTransform.prototype.getScaleX = function() {305return this.m00_;306};307308309/**310* @return {number} The scaling factor in the y-direction (m11).311*/312goog.graphics.AffineTransform.prototype.getScaleY = function() {313return this.m11_;314};315316317/**318* @return {number} The translation in the x-direction (m02).319*/320goog.graphics.AffineTransform.prototype.getTranslateX = function() {321return this.m02_;322};323324325/**326* @return {number} The translation in the y-direction (m12).327*/328goog.graphics.AffineTransform.prototype.getTranslateY = function() {329return this.m12_;330};331332333/**334* @return {number} The shear factor in the x-direction (m01).335*/336goog.graphics.AffineTransform.prototype.getShearX = function() {337return this.m01_;338};339340341/**342* @return {number} The shear factor in the y-direction (m10).343*/344goog.graphics.AffineTransform.prototype.getShearY = function() {345return this.m10_;346};347348349/**350* Concatenates an affine transform to this transform.351*352* @param {!goog.graphics.AffineTransform} tx The transform to concatenate.353* @return {!goog.graphics.AffineTransform} This affine transform.354*/355goog.graphics.AffineTransform.prototype.concatenate = function(tx) {356var m0 = this.m00_;357var m1 = this.m01_;358this.m00_ = tx.m00_ * m0 + tx.m10_ * m1;359this.m01_ = tx.m01_ * m0 + tx.m11_ * m1;360this.m02_ += tx.m02_ * m0 + tx.m12_ * m1;361362m0 = this.m10_;363m1 = this.m11_;364this.m10_ = tx.m00_ * m0 + tx.m10_ * m1;365this.m11_ = tx.m01_ * m0 + tx.m11_ * m1;366this.m12_ += tx.m02_ * m0 + tx.m12_ * m1;367return this;368};369370371/**372* Pre-concatenates an affine transform to this transform.373*374* @param {!goog.graphics.AffineTransform} tx The transform to preconcatenate.375* @return {!goog.graphics.AffineTransform} This affine transform.376*/377goog.graphics.AffineTransform.prototype.preConcatenate = function(tx) {378var m0 = this.m00_;379var m1 = this.m10_;380this.m00_ = tx.m00_ * m0 + tx.m01_ * m1;381this.m10_ = tx.m10_ * m0 + tx.m11_ * m1;382383m0 = this.m01_;384m1 = this.m11_;385this.m01_ = tx.m00_ * m0 + tx.m01_ * m1;386this.m11_ = tx.m10_ * m0 + tx.m11_ * m1;387388m0 = this.m02_;389m1 = this.m12_;390this.m02_ = tx.m00_ * m0 + tx.m01_ * m1 + tx.m02_;391this.m12_ = tx.m10_ * m0 + tx.m11_ * m1 + tx.m12_;392return this;393};394395396/**397* Transforms an array of coordinates by this transform and stores the result398* into a destination array.399*400* @param {!Array<number>} src The array containing the source points401* as x, y value pairs.402* @param {number} srcOff The offset to the first point to be transformed.403* @param {!Array<number>} dst The array into which to store the transformed404* point pairs.405* @param {number} dstOff The offset of the location of the first transformed406* point in the destination array.407* @param {number} numPts The number of points to transform.408*/409goog.graphics.AffineTransform.prototype.transform = function(410src, srcOff, dst, dstOff, numPts) {411var i = srcOff;412var j = dstOff;413var srcEnd = srcOff + 2 * numPts;414while (i < srcEnd) {415var x = src[i++];416var y = src[i++];417dst[j++] = x * this.m00_ + y * this.m01_ + this.m02_;418dst[j++] = x * this.m10_ + y * this.m11_ + this.m12_;419}420};421422423/**424* @return {number} The determinant of this transform.425*/426goog.graphics.AffineTransform.prototype.getDeterminant = function() {427return this.m00_ * this.m11_ - this.m01_ * this.m10_;428};429430431/**432* Returns whether the transform is invertible. A transform is not invertible433* if the determinant is 0 or any value is non-finite or NaN.434*435* @return {boolean} Whether the transform is invertible.436*/437goog.graphics.AffineTransform.prototype.isInvertible = function() {438var det = this.getDeterminant();439return isFinite(det) && isFinite(this.m02_) && isFinite(this.m12_) &&440det != 0;441};442443444/**445* @return {!goog.graphics.AffineTransform} An AffineTransform object446* representing the inverse transformation.447*/448goog.graphics.AffineTransform.prototype.createInverse = function() {449var det = this.getDeterminant();450return new goog.graphics.AffineTransform(451this.m11_ / det, -this.m10_ / det, -this.m01_ / det, this.m00_ / det,452(this.m01_ * this.m12_ - this.m11_ * this.m02_) / det,453(this.m10_ * this.m02_ - this.m00_ * this.m12_) / det);454};455456457/**458* Creates a transform representing a scaling transformation.459*460* @param {number} sx The x-axis scaling factor.461* @param {number} sy The y-axis scaling factor.462* @return {!goog.graphics.AffineTransform} A transform representing a scaling463* transformation.464*/465goog.graphics.AffineTransform.getScaleInstance = function(sx, sy) {466return new goog.graphics.AffineTransform().setToScale(sx, sy);467};468469470/**471* Creates a transform representing a translation transformation.472*473* @param {number} dx The distance to translate in the x direction.474* @param {number} dy The distance to translate in the y direction.475* @return {!goog.graphics.AffineTransform} A transform representing a476* translation transformation.477*/478goog.graphics.AffineTransform.getTranslateInstance = function(dx, dy) {479return new goog.graphics.AffineTransform().setToTranslation(dx, dy);480};481482483/**484* Creates a transform representing a shearing transformation.485*486* @param {number} shx The x-axis shear factor.487* @param {number} shy The y-axis shear factor.488* @return {!goog.graphics.AffineTransform} A transform representing a shearing489* transformation.490*/491goog.graphics.AffineTransform.getShearInstance = function(shx, shy) {492return new goog.graphics.AffineTransform().setToShear(shx, shy);493};494495496/**497* Creates a transform representing a rotation transformation.498*499* @param {number} theta The angle of rotation measured in radians.500* @param {number} x The x coordinate of the anchor point.501* @param {number} y The y coordinate of the anchor point.502* @return {!goog.graphics.AffineTransform} A transform representing a rotation503* transformation.504*/505goog.graphics.AffineTransform.getRotateInstance = function(theta, x, y) {506return new goog.graphics.AffineTransform().setToRotation(theta, x, y);507};508509510/**511* Sets this transform to a scaling transformation.512*513* @param {number} sx The x-axis scaling factor.514* @param {number} sy The y-axis scaling factor.515* @return {!goog.graphics.AffineTransform} This affine transform.516*/517goog.graphics.AffineTransform.prototype.setToScale = function(sx, sy) {518return this.setTransform(sx, 0, 0, sy, 0, 0);519};520521522/**523* Sets this transform to a translation transformation.524*525* @param {number} dx The distance to translate in the x direction.526* @param {number} dy The distance to translate in the y direction.527* @return {!goog.graphics.AffineTransform} This affine transform.528*/529goog.graphics.AffineTransform.prototype.setToTranslation = function(dx, dy) {530return this.setTransform(1, 0, 0, 1, dx, dy);531};532533534/**535* Sets this transform to a shearing transformation.536*537* @param {number} shx The x-axis shear factor.538* @param {number} shy The y-axis shear factor.539* @return {!goog.graphics.AffineTransform} This affine transform.540*/541goog.graphics.AffineTransform.prototype.setToShear = function(shx, shy) {542return this.setTransform(1, shy, shx, 1, 0, 0);543};544545546/**547* Sets this transform to a rotation transformation.548*549* @param {number} theta The angle of rotation measured in radians.550* @param {number} x The x coordinate of the anchor point.551* @param {number} y The y coordinate of the anchor point.552* @return {!goog.graphics.AffineTransform} This affine transform.553*/554goog.graphics.AffineTransform.prototype.setToRotation = function(theta, x, y) {555var cos = Math.cos(theta);556var sin = Math.sin(theta);557return this.setTransform(558cos, sin, -sin, cos, x - x * cos + y * sin, y - x * sin - y * cos);559};560561562/**563* Compares two affine transforms for equality.564*565* @param {goog.graphics.AffineTransform} tx The other affine transform.566* @return {boolean} whether the two transforms are equal.567*/568goog.graphics.AffineTransform.prototype.equals = function(tx) {569if (this == tx) {570return true;571}572if (!tx) {573return false;574}575return this.m00_ == tx.m00_ && this.m01_ == tx.m01_ && this.m02_ == tx.m02_ &&576this.m10_ == tx.m10_ && this.m11_ == tx.m11_ && this.m12_ == tx.m12_;577};578579580