Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/graphics/affinetransform.js
2868 views
1
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
16
/**
17
* @fileoverview Provides an object representation of an AffineTransform and
18
* methods for working with it.
19
*/
20
21
22
goog.provide('goog.graphics.AffineTransform');
23
24
25
26
/**
27
* Creates a 2D affine transform. An affine transform performs a linear
28
* mapping from 2D coordinates to other 2D coordinates that preserves the
29
* "straightness" and "parallelness" of lines.
30
*
31
* Such a coordinate transformation can be represented by a 3 row by 3 column
32
* matrix with an implied last row of [ 0 0 1 ]. This matrix transforms source
33
* coordinates (x,y) into destination coordinates (x',y') by considering them
34
* to be a column vector and multiplying the coordinate vector by the matrix
35
* according to the following process:
36
* <pre>
37
* [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
38
* [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]
39
* [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
40
* </pre>
41
*
42
* This class is optimized for speed and minimizes calculations based on its
43
* knowledge of the underlying matrix (as opposed to say simply performing
44
* matrix multiplication).
45
*
46
* @param {number=} opt_m00 The m00 coordinate of the transform.
47
* @param {number=} opt_m10 The m10 coordinate of the transform.
48
* @param {number=} opt_m01 The m01 coordinate of the transform.
49
* @param {number=} opt_m11 The m11 coordinate of the transform.
50
* @param {number=} opt_m02 The m02 coordinate of the transform.
51
* @param {number=} opt_m12 The m12 coordinate of the transform.
52
* @constructor
53
* @final
54
*/
55
goog.graphics.AffineTransform = function(
56
opt_m00, opt_m10, opt_m01, opt_m11, opt_m02, opt_m12) {
57
if (arguments.length == 6) {
58
this.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) {
66
throw Error('Insufficient matrix parameters');
67
} else {
68
this.m00_ = this.m11_ = 1;
69
this.m10_ = this.m01_ = this.m02_ = this.m12_ = 0;
70
}
71
};
72
73
74
/**
75
* @return {boolean} Whether this transform is the identity transform.
76
*/
77
goog.graphics.AffineTransform.prototype.isIdentity = function() {
78
return this.m00_ == 1 && this.m10_ == 0 && this.m01_ == 0 && this.m11_ == 1 &&
79
this.m02_ == 0 && this.m12_ == 0;
80
};
81
82
83
/**
84
* @return {!goog.graphics.AffineTransform} A copy of this transform.
85
*/
86
goog.graphics.AffineTransform.prototype.clone = function() {
87
return new goog.graphics.AffineTransform(
88
this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_);
89
};
90
91
92
/**
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.graphics.AffineTransform} This affine transform.
102
*/
103
goog.graphics.AffineTransform.prototype.setTransform = function(
104
m00, m10, m01, m11, m02, m12) {
105
if (!goog.isNumber(m00) || !goog.isNumber(m10) || !goog.isNumber(m01) ||
106
!goog.isNumber(m11) || !goog.isNumber(m02) || !goog.isNumber(m12)) {
107
throw Error('Invalid transform parameters');
108
}
109
this.m00_ = m00;
110
this.m10_ = m10;
111
this.m01_ = m01;
112
this.m11_ = m11;
113
this.m02_ = m02;
114
this.m12_ = m12;
115
return this;
116
};
117
118
119
/**
120
* Sets this transform to be identical to the given transform.
121
*
122
* @param {!goog.graphics.AffineTransform} tx The transform to copy.
123
* @return {!goog.graphics.AffineTransform} This affine transform.
124
*/
125
goog.graphics.AffineTransform.prototype.copyFrom = function(tx) {
126
this.m00_ = tx.m00_;
127
this.m10_ = tx.m10_;
128
this.m01_ = tx.m01_;
129
this.m11_ = tx.m11_;
130
this.m02_ = tx.m02_;
131
this.m12_ = tx.m12_;
132
return this;
133
};
134
135
136
/**
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.graphics.AffineTransform} This affine transform.
142
*/
143
goog.graphics.AffineTransform.prototype.scale = function(sx, sy) {
144
this.m00_ *= sx;
145
this.m10_ *= sx;
146
this.m01_ *= sy;
147
this.m11_ *= sy;
148
return this;
149
};
150
151
152
/**
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.graphics.AffineTransform} This affine transform.
165
*/
166
goog.graphics.AffineTransform.prototype.preScale = function(sx, sy) {
167
this.m00_ *= sx;
168
this.m01_ *= sx;
169
this.m02_ *= sx;
170
this.m10_ *= sy;
171
this.m11_ *= sy;
172
this.m12_ *= sy;
173
return this;
174
};
175
176
177
/**
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.graphics.AffineTransform} This affine transform.
183
*/
184
goog.graphics.AffineTransform.prototype.translate = function(dx, dy) {
185
this.m02_ += dx * this.m00_ + dy * this.m01_;
186
this.m12_ += dx * this.m10_ + dy * this.m11_;
187
return this;
188
};
189
190
191
/**
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.graphics.AffineTransform} This affine transform.
204
*/
205
goog.graphics.AffineTransform.prototype.preTranslate = function(dx, dy) {
206
this.m02_ += dx;
207
this.m12_ += dy;
208
return this;
209
};
210
211
212
/**
213
* Concatenates this transform with a rotation transformation around an anchor
214
* 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.graphics.AffineTransform} This affine transform.
220
*/
221
goog.graphics.AffineTransform.prototype.rotate = function(theta, x, y) {
222
return this.concatenate(
223
goog.graphics.AffineTransform.getRotateInstance(theta, x, y));
224
};
225
226
227
/**
228
* Pre-concatenates this transform with a rotation transformation around an
229
* 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.graphics.AffineTransform} This affine transform.
235
*/
236
goog.graphics.AffineTransform.prototype.preRotate = function(theta, x, y) {
237
return this.preConcatenate(
238
goog.graphics.AffineTransform.getRotateInstance(theta, x, y));
239
};
240
241
242
/**
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.graphics.AffineTransform} This affine transform.
248
*/
249
goog.graphics.AffineTransform.prototype.shear = function(shx, shy) {
250
var m00 = this.m00_;
251
var m10 = this.m10_;
252
this.m00_ += shy * this.m01_;
253
this.m10_ += shy * this.m11_;
254
this.m01_ += shx * m00;
255
this.m11_ += shx * m10;
256
return this;
257
};
258
259
260
/**
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.graphics.AffineTransform} This affine transform.
273
*/
274
goog.graphics.AffineTransform.prototype.preShear = function(shx, shy) {
275
var m00 = this.m00_;
276
var m01 = this.m01_;
277
var m02 = this.m02_;
278
this.m00_ += shx * this.m10_;
279
this.m01_ += shx * this.m11_;
280
this.m02_ += shx * this.m12_;
281
this.m10_ += shy * m00;
282
this.m11_ += shy * m01;
283
this.m12_ += shy * m02;
284
return this;
285
};
286
287
288
/**
289
* @return {string} A string representation of this transform. The format of
290
* of the string is compatible with SVG matrix notation, i.e.
291
* "matrix(a,b,c,d,e,f)".
292
* @override
293
*/
294
goog.graphics.AffineTransform.prototype.toString = function() {
295
return 'matrix(' +
296
[this.m00_, this.m10_, this.m01_, this.m11_, this.m02_, this.m12_].join(
297
',') +
298
')';
299
};
300
301
302
/**
303
* @return {number} The scaling factor in the x-direction (m00).
304
*/
305
goog.graphics.AffineTransform.prototype.getScaleX = function() {
306
return this.m00_;
307
};
308
309
310
/**
311
* @return {number} The scaling factor in the y-direction (m11).
312
*/
313
goog.graphics.AffineTransform.prototype.getScaleY = function() {
314
return this.m11_;
315
};
316
317
318
/**
319
* @return {number} The translation in the x-direction (m02).
320
*/
321
goog.graphics.AffineTransform.prototype.getTranslateX = function() {
322
return this.m02_;
323
};
324
325
326
/**
327
* @return {number} The translation in the y-direction (m12).
328
*/
329
goog.graphics.AffineTransform.prototype.getTranslateY = function() {
330
return this.m12_;
331
};
332
333
334
/**
335
* @return {number} The shear factor in the x-direction (m01).
336
*/
337
goog.graphics.AffineTransform.prototype.getShearX = function() {
338
return this.m01_;
339
};
340
341
342
/**
343
* @return {number} The shear factor in the y-direction (m10).
344
*/
345
goog.graphics.AffineTransform.prototype.getShearY = function() {
346
return this.m10_;
347
};
348
349
350
/**
351
* Concatenates an affine transform to this transform.
352
*
353
* @param {!goog.graphics.AffineTransform} tx The transform to concatenate.
354
* @return {!goog.graphics.AffineTransform} This affine transform.
355
*/
356
goog.graphics.AffineTransform.prototype.concatenate = function(tx) {
357
var m0 = this.m00_;
358
var m1 = this.m01_;
359
this.m00_ = tx.m00_ * m0 + tx.m10_ * m1;
360
this.m01_ = tx.m01_ * m0 + tx.m11_ * m1;
361
this.m02_ += tx.m02_ * m0 + tx.m12_ * m1;
362
363
m0 = this.m10_;
364
m1 = this.m11_;
365
this.m10_ = tx.m00_ * m0 + tx.m10_ * m1;
366
this.m11_ = tx.m01_ * m0 + tx.m11_ * m1;
367
this.m12_ += tx.m02_ * m0 + tx.m12_ * m1;
368
return this;
369
};
370
371
372
/**
373
* Pre-concatenates an affine transform to this transform.
374
*
375
* @param {!goog.graphics.AffineTransform} tx The transform to preconcatenate.
376
* @return {!goog.graphics.AffineTransform} This affine transform.
377
*/
378
goog.graphics.AffineTransform.prototype.preConcatenate = function(tx) {
379
var m0 = this.m00_;
380
var m1 = this.m10_;
381
this.m00_ = tx.m00_ * m0 + tx.m01_ * m1;
382
this.m10_ = tx.m10_ * m0 + tx.m11_ * m1;
383
384
m0 = this.m01_;
385
m1 = this.m11_;
386
this.m01_ = tx.m00_ * m0 + tx.m01_ * m1;
387
this.m11_ = tx.m10_ * m0 + tx.m11_ * m1;
388
389
m0 = this.m02_;
390
m1 = this.m12_;
391
this.m02_ = tx.m00_ * m0 + tx.m01_ * m1 + tx.m02_;
392
this.m12_ = tx.m10_ * m0 + tx.m11_ * m1 + tx.m12_;
393
return this;
394
};
395
396
397
/**
398
* Transforms an array of coordinates by this transform and stores the result
399
* into a destination array.
400
*
401
* @param {!Array<number>} src The array containing the source points
402
* 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 transformed
405
* point pairs.
406
* @param {number} dstOff The offset of the location of the first transformed
407
* point in the destination array.
408
* @param {number} numPts The number of points to transform.
409
*/
410
goog.graphics.AffineTransform.prototype.transform = function(
411
src, srcOff, dst, dstOff, numPts) {
412
var i = srcOff;
413
var j = dstOff;
414
var srcEnd = srcOff + 2 * numPts;
415
while (i < srcEnd) {
416
var x = src[i++];
417
var y = src[i++];
418
dst[j++] = x * this.m00_ + y * this.m01_ + this.m02_;
419
dst[j++] = x * this.m10_ + y * this.m11_ + this.m12_;
420
}
421
};
422
423
424
/**
425
* @return {number} The determinant of this transform.
426
*/
427
goog.graphics.AffineTransform.prototype.getDeterminant = function() {
428
return this.m00_ * this.m11_ - this.m01_ * this.m10_;
429
};
430
431
432
/**
433
* Returns whether the transform is invertible. A transform is not invertible
434
* if the determinant is 0 or any value is non-finite or NaN.
435
*
436
* @return {boolean} Whether the transform is invertible.
437
*/
438
goog.graphics.AffineTransform.prototype.isInvertible = function() {
439
var det = this.getDeterminant();
440
return isFinite(det) && isFinite(this.m02_) && isFinite(this.m12_) &&
441
det != 0;
442
};
443
444
445
/**
446
* @return {!goog.graphics.AffineTransform} An AffineTransform object
447
* representing the inverse transformation.
448
*/
449
goog.graphics.AffineTransform.prototype.createInverse = function() {
450
var det = this.getDeterminant();
451
return new goog.graphics.AffineTransform(
452
this.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
};
456
457
458
/**
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.graphics.AffineTransform} A transform representing a scaling
464
* transformation.
465
*/
466
goog.graphics.AffineTransform.getScaleInstance = function(sx, sy) {
467
return new goog.graphics.AffineTransform().setToScale(sx, sy);
468
};
469
470
471
/**
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.graphics.AffineTransform} A transform representing a
477
* translation transformation.
478
*/
479
goog.graphics.AffineTransform.getTranslateInstance = function(dx, dy) {
480
return new goog.graphics.AffineTransform().setToTranslation(dx, dy);
481
};
482
483
484
/**
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.graphics.AffineTransform} A transform representing a shearing
490
* transformation.
491
*/
492
goog.graphics.AffineTransform.getShearInstance = function(shx, shy) {
493
return new goog.graphics.AffineTransform().setToShear(shx, shy);
494
};
495
496
497
/**
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.graphics.AffineTransform} A transform representing a rotation
504
* transformation.
505
*/
506
goog.graphics.AffineTransform.getRotateInstance = function(theta, x, y) {
507
return new goog.graphics.AffineTransform().setToRotation(theta, x, y);
508
};
509
510
511
/**
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.graphics.AffineTransform} This affine transform.
517
*/
518
goog.graphics.AffineTransform.prototype.setToScale = function(sx, sy) {
519
return this.setTransform(sx, 0, 0, sy, 0, 0);
520
};
521
522
523
/**
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.graphics.AffineTransform} This affine transform.
529
*/
530
goog.graphics.AffineTransform.prototype.setToTranslation = function(dx, dy) {
531
return this.setTransform(1, 0, 0, 1, dx, dy);
532
};
533
534
535
/**
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.graphics.AffineTransform} This affine transform.
541
*/
542
goog.graphics.AffineTransform.prototype.setToShear = function(shx, shy) {
543
return this.setTransform(1, shy, shx, 1, 0, 0);
544
};
545
546
547
/**
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.graphics.AffineTransform} This affine transform.
554
*/
555
goog.graphics.AffineTransform.prototype.setToRotation = function(theta, x, y) {
556
var cos = Math.cos(theta);
557
var sin = Math.sin(theta);
558
return this.setTransform(
559
cos, sin, -sin, cos, x - x * cos + y * sin, y - x * sin - y * cos);
560
};
561
562
563
/**
564
* Compares two affine transforms for equality.
565
*
566
* @param {goog.graphics.AffineTransform} tx The other affine transform.
567
* @return {boolean} whether the two transforms are equal.
568
*/
569
goog.graphics.AffineTransform.prototype.equals = function(tx) {
570
if (this == tx) {
571
return true;
572
}
573
if (!tx) {
574
return false;
575
}
576
return this.m00_ == tx.m00_ && this.m01_ == tx.m01_ && this.m02_ == tx.m02_ &&
577
this.m10_ == tx.m10_ && this.m11_ == tx.m11_ && this.m12_ == tx.m12_;
578
};
579
580