1 /* AffineTransform.java -- transform coordinates between two 2-D spaces 2 Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package java.awt.geom; 40 41 import java.awt.Shape; 42 import java.io.IOException; 43 import java.io.ObjectInputStream; 44 import java.io.Serializable; 45 46 /** 47 * This class represents an affine transformation between two coordinate 48 * spaces in 2 dimensions. Such a transform preserves the "straightness" 49 * and "parallelness" of lines. The transform is built from a sequence of 50 * translations, scales, flips, rotations, and shears. 51 * 52 * <p>The transformation can be represented using matrix math on a 3x3 array. 53 * Given (x,y), the transformation (x',y') can be found by: 54 * <pre> 55 * [ x'] [ m00 m01 m02 ] [ x ] [ m00*x + m01*y + m02 ] 56 * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10*x + m11*y + m12 ] 57 * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] 58 * </pre> 59 * The bottom row of the matrix is constant, so a transform can be uniquely 60 * represented (as in {@link #toString()}) by 61 * "[[m00, m01, m02], [m10, m11, m12]]". 62 * 63 * @author Tom Tromey (tromey@cygnus.com) 64 * @author Eric Blake (ebb9@email.byu.edu) 65 * @since 1.2 66 * @status partially updated to 1.4, still has some problems 67 */ 68 public class AffineTransform implements Cloneable, Serializable 69 { 70 /** 71 * Compatible with JDK 1.2+. 72 */ 73 private static final long serialVersionUID = 1330973210523860834L; 74 75 /** 76 * The transformation is the identity (x' = x, y' = y). All other transforms 77 * have either a combination of the appropriate transform flag bits for 78 * their type, or the type GENERAL_TRANSFORM. 79 * 80 * @see #TYPE_TRANSLATION 81 * @see #TYPE_UNIFORM_SCALE 82 * @see #TYPE_GENERAL_SCALE 83 * @see #TYPE_FLIP 84 * @see #TYPE_QUADRANT_ROTATION 85 * @see #TYPE_GENERAL_ROTATION 86 * @see #TYPE_GENERAL_TRANSFORM 87 * @see #getType() 88 */ 89 public static final int TYPE_IDENTITY = 0; 90 91 /** 92 * The transformation includes a translation - shifting in the x or y 93 * direction without changing length or angles. 94 * 95 * @see #TYPE_IDENTITY 96 * @see #TYPE_UNIFORM_SCALE 97 * @see #TYPE_GENERAL_SCALE 98 * @see #TYPE_FLIP 99 * @see #TYPE_QUADRANT_ROTATION 100 * @see #TYPE_GENERAL_ROTATION 101 * @see #TYPE_GENERAL_TRANSFORM 102 * @see #getType() 103 */ 104 public static final int TYPE_TRANSLATION = 1; 105 106 /** 107 * The transformation includes a uniform scale - length is scaled in both 108 * the x and y directions by the same amount, without affecting angles. 109 * This is mutually exclusive with TYPE_GENERAL_SCALE. 110 * 111 * @see #TYPE_IDENTITY 112 * @see #TYPE_TRANSLATION 113 * @see #TYPE_GENERAL_SCALE 114 * @see #TYPE_FLIP 115 * @see #TYPE_QUADRANT_ROTATION 116 * @see #TYPE_GENERAL_ROTATION 117 * @see #TYPE_GENERAL_TRANSFORM 118 * @see #TYPE_MASK_SCALE 119 * @see #getType() 120 */ 121 public static final int TYPE_UNIFORM_SCALE = 2; 122 123 /** 124 * The transformation includes a general scale - length is scaled in either 125 * or both the x and y directions, but by different amounts; without 126 * affecting angles. This is mutually exclusive with TYPE_UNIFORM_SCALE. 127 * 128 * @see #TYPE_IDENTITY 129 * @see #TYPE_TRANSLATION 130 * @see #TYPE_UNIFORM_SCALE 131 * @see #TYPE_FLIP 132 * @see #TYPE_QUADRANT_ROTATION 133 * @see #TYPE_GENERAL_ROTATION 134 * @see #TYPE_GENERAL_TRANSFORM 135 * @see #TYPE_MASK_SCALE 136 * @see #getType() 137 */ 138 public static final int TYPE_GENERAL_SCALE = 4; 139 140 /** 141 * This constant checks if either variety of scale transform is performed. 142 * 143 * @see #TYPE_UNIFORM_SCALE 144 * @see #TYPE_GENERAL_SCALE 145 */ 146 public static final int TYPE_MASK_SCALE = 6; 147 148 /** 149 * The transformation includes a flip about an axis, swapping between 150 * right-handed and left-handed coordinate systems. In a right-handed 151 * system, the positive x-axis rotates counter-clockwise to the positive 152 * y-axis; in a left-handed system it rotates clockwise. 153 * 154 * @see #TYPE_IDENTITY 155 * @see #TYPE_TRANSLATION 156 * @see #TYPE_UNIFORM_SCALE 157 * @see #TYPE_GENERAL_SCALE 158 * @see #TYPE_QUADRANT_ROTATION 159 * @see #TYPE_GENERAL_ROTATION 160 * @see #TYPE_GENERAL_TRANSFORM 161 * @see #getType() 162 */ 163 public static final int TYPE_FLIP = 64; 164 165 /** 166 * The transformation includes a rotation of a multiple of 90 degrees (PI/2 167 * radians). Angles are rotated, but length is preserved. This is mutually 168 * exclusive with TYPE_GENERAL_ROTATION. 169 * 170 * @see #TYPE_IDENTITY 171 * @see #TYPE_TRANSLATION 172 * @see #TYPE_UNIFORM_SCALE 173 * @see #TYPE_GENERAL_SCALE 174 * @see #TYPE_FLIP 175 * @see #TYPE_GENERAL_ROTATION 176 * @see #TYPE_GENERAL_TRANSFORM 177 * @see #TYPE_MASK_ROTATION 178 * @see #getType() 179 */ 180 public static final int TYPE_QUADRANT_ROTATION = 8; 181 182 /** 183 * The transformation includes a rotation by an arbitrary angle. Angles are 184 * rotated, but length is preserved. This is mutually exclusive with 185 * TYPE_QUADRANT_ROTATION. 186 * 187 * @see #TYPE_IDENTITY 188 * @see #TYPE_TRANSLATION 189 * @see #TYPE_UNIFORM_SCALE 190 * @see #TYPE_GENERAL_SCALE 191 * @see #TYPE_FLIP 192 * @see #TYPE_QUADRANT_ROTATION 193 * @see #TYPE_GENERAL_TRANSFORM 194 * @see #TYPE_MASK_ROTATION 195 * @see #getType() 196 */ 197 public static final int TYPE_GENERAL_ROTATION = 16; 198 199 /** 200 * This constant checks if either variety of rotation is performed. 201 * 202 * @see #TYPE_QUADRANT_ROTATION 203 * @see #TYPE_GENERAL_ROTATION 204 */ 205 public static final int TYPE_MASK_ROTATION = 24; 206 207 /** 208 * The transformation is an arbitrary conversion of coordinates which 209 * could not be decomposed into the other TYPEs. 210 * 211 * @see #TYPE_IDENTITY 212 * @see #TYPE_TRANSLATION 213 * @see #TYPE_UNIFORM_SCALE 214 * @see #TYPE_GENERAL_SCALE 215 * @see #TYPE_FLIP 216 * @see #TYPE_QUADRANT_ROTATION 217 * @see #TYPE_GENERAL_ROTATION 218 * @see #getType() 219 */ 220 public static final int TYPE_GENERAL_TRANSFORM = 32; 221 222 /** 223 * The X coordinate scaling element of the transform matrix. 224 * 225 * @serial matrix[0,0] 226 */ 227 private double m00; 228 229 /** 230 * The Y coordinate shearing element of the transform matrix. 231 * 232 * @serial matrix[1,0] 233 */ 234 private double m10; 235 236 /** 237 * The X coordinate shearing element of the transform matrix. 238 * 239 * @serial matrix[0,1] 240 */ 241 private double m01; 242 243 /** 244 * The Y coordinate scaling element of the transform matrix. 245 * 246 * @serial matrix[1,1] 247 */ 248 private double m11; 249 250 /** 251 * The X coordinate translation element of the transform matrix. 252 * 253 * @serial matrix[0,2] 254 */ 255 private double m02; 256 257 /** 258 * The Y coordinate translation element of the transform matrix. 259 * 260 * @serial matrix[1,2] 261 */ 262 private double m12; 263 264 /** The type of this transform. */ 265 private transient int type; 266 267 /** 268 * Construct a new identity transform: 269 * <pre> 270 * [ 1 0 0 ] 271 * [ 0 1 0 ] 272 * [ 0 0 1 ] 273 * </pre> 274 */ AffineTransform()275 public AffineTransform() 276 { 277 m00 = m11 = 1; 278 } 279 280 /** 281 * Create a new transform which copies the given one. 282 * 283 * @param tx the transform to copy 284 * @throws NullPointerException if tx is null 285 */ AffineTransform(AffineTransform tx)286 public AffineTransform(AffineTransform tx) 287 { 288 setTransform(tx); 289 } 290 291 /** 292 * Construct a transform with the given matrix entries: 293 * <pre> 294 * [ m00 m01 m02 ] 295 * [ m10 m11 m12 ] 296 * [ 0 0 1 ] 297 * </pre> 298 * 299 * @param m00 the x scaling component 300 * @param m10 the y shearing component 301 * @param m01 the x shearing component 302 * @param m11 the y scaling component 303 * @param m02 the x translation component 304 * @param m12 the y translation component 305 */ AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12)306 public AffineTransform(float m00, float m10, 307 float m01, float m11, 308 float m02, float m12) 309 { 310 this.m00 = m00; 311 this.m10 = m10; 312 this.m01 = m01; 313 this.m11 = m11; 314 this.m02 = m02; 315 this.m12 = m12; 316 updateType(); 317 } 318 319 /** 320 * Construct a transform from a sequence of float entries. The array must 321 * have at least 4 entries, which has a translation factor of 0; or 6 322 * entries, for specifying all parameters: 323 * <pre> 324 * [ f[0] f[2] (f[4]) ] 325 * [ f[1] f[3] (f[5]) ] 326 * [ 0 0 1 ] 327 * </pre> 328 * 329 * @param f the matrix to copy from, with at least 4 (6) entries 330 * @throws NullPointerException if f is null 331 * @throws ArrayIndexOutOfBoundsException if f is too small 332 */ AffineTransform(float[] f)333 public AffineTransform(float[] f) 334 { 335 m00 = f[0]; 336 m10 = f[1]; 337 m01 = f[2]; 338 m11 = f[3]; 339 if (f.length >= 6) 340 { 341 m02 = f[4]; 342 m12 = f[5]; 343 } 344 updateType(); 345 } 346 347 /** 348 * Construct a transform with the given matrix entries: 349 * <pre> 350 * [ m00 m01 m02 ] 351 * [ m10 m11 m12 ] 352 * [ 0 0 1 ] 353 * </pre> 354 * 355 * @param m00 the x scaling component 356 * @param m10 the y shearing component 357 * @param m01 the x shearing component 358 * @param m11 the y scaling component 359 * @param m02 the x translation component 360 * @param m12 the y translation component 361 */ AffineTransform(double m00, double m10, double m01, double m11, double m02, double m12)362 public AffineTransform(double m00, double m10, double m01, 363 double m11, double m02, double m12) 364 { 365 this.m00 = m00; 366 this.m10 = m10; 367 this.m01 = m01; 368 this.m11 = m11; 369 this.m02 = m02; 370 this.m12 = m12; 371 updateType(); 372 } 373 374 /** 375 * Construct a transform from a sequence of double entries. The array must 376 * have at least 4 entries, which has a translation factor of 0; or 6 377 * entries, for specifying all parameters: 378 * <pre> 379 * [ d[0] d[2] (d[4]) ] 380 * [ d[1] d[3] (d[5]) ] 381 * [ 0 0 1 ] 382 * </pre> 383 * 384 * @param d the matrix to copy from, with at least 4 (6) entries 385 * @throws NullPointerException if d is null 386 * @throws ArrayIndexOutOfBoundsException if d is too small 387 */ AffineTransform(double[] d)388 public AffineTransform(double[] d) 389 { 390 m00 = d[0]; 391 m10 = d[1]; 392 m01 = d[2]; 393 m11 = d[3]; 394 if (d.length >= 6) 395 { 396 m02 = d[4]; 397 m12 = d[5]; 398 } 399 updateType(); 400 } 401 402 /** 403 * Returns a translation transform: 404 * <pre> 405 * [ 1 0 tx ] 406 * [ 0 1 ty ] 407 * [ 0 0 1 ] 408 * </pre> 409 * 410 * @param tx the x translation distance 411 * @param ty the y translation distance 412 * @return the translating transform 413 */ getTranslateInstance(double tx, double ty)414 public static AffineTransform getTranslateInstance(double tx, double ty) 415 { 416 AffineTransform t = new AffineTransform(); 417 t.m02 = tx; 418 t.m12 = ty; 419 t.type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; 420 return t; 421 } 422 423 /** 424 * Returns a rotation transform. A positive angle (in radians) rotates 425 * the positive x-axis to the positive y-axis: 426 * <pre> 427 * [ cos(theta) -sin(theta) 0 ] 428 * [ sin(theta) cos(theta) 0 ] 429 * [ 0 0 1 ] 430 * </pre> 431 * 432 * @param theta the rotation angle 433 * @return the rotating transform 434 */ getRotateInstance(double theta)435 public static AffineTransform getRotateInstance(double theta) 436 { 437 AffineTransform t = new AffineTransform(); 438 t.setToRotation(theta); 439 return t; 440 } 441 442 /** 443 * Returns a rotation transform about a point. A positive angle (in radians) 444 * rotates the positive x-axis to the positive y-axis. This is the same 445 * as calling: 446 * <pre> 447 * AffineTransform tx = new AffineTransform(); 448 * tx.setToTranslation(x, y); 449 * tx.rotate(theta); 450 * tx.translate(-x, -y); 451 * </pre> 452 * 453 * <p>The resulting matrix is: 454 * <pre> 455 * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 456 * [ sin(theta) cos(theta) y-x*sin-y*cos ] 457 * [ 0 0 1 ] 458 * </pre> 459 * 460 * @param theta the rotation angle 461 * @param x the x coordinate of the pivot point 462 * @param y the y coordinate of the pivot point 463 * @return the rotating transform 464 */ getRotateInstance(double theta, double x, double y)465 public static AffineTransform getRotateInstance(double theta, 466 double x, double y) 467 { 468 AffineTransform t = new AffineTransform(); 469 t.setToTranslation(x, y); 470 t.rotate(theta); 471 t.translate(-x, -y); 472 return t; 473 } 474 475 /** 476 * Returns a scaling transform: 477 * <pre> 478 * [ sx 0 0 ] 479 * [ 0 sy 0 ] 480 * [ 0 0 1 ] 481 * </pre> 482 * 483 * @param sx the x scaling factor 484 * @param sy the y scaling factor 485 * @return the scaling transform 486 */ getScaleInstance(double sx, double sy)487 public static AffineTransform getScaleInstance(double sx, double sy) 488 { 489 AffineTransform t = new AffineTransform(); 490 t.setToScale(sx, sy); 491 return t; 492 } 493 494 /** 495 * Returns a shearing transform (points are shifted in the x direction based 496 * on a factor of their y coordinate, and in the y direction as a factor of 497 * their x coordinate): 498 * <pre> 499 * [ 1 shx 0 ] 500 * [ shy 1 0 ] 501 * [ 0 0 1 ] 502 * </pre> 503 * 504 * @param shx the x shearing factor 505 * @param shy the y shearing factor 506 * @return the shearing transform 507 */ getShearInstance(double shx, double shy)508 public static AffineTransform getShearInstance(double shx, double shy) 509 { 510 AffineTransform t = new AffineTransform(); 511 t.setToShear(shx, shy); 512 return t; 513 } 514 515 /** 516 * Returns the type of this transform. The result is always valid, although 517 * it may not be the simplest interpretation (in other words, there are 518 * sequences of transforms which reduce to something simpler, which this 519 * does not always detect). The result is either TYPE_GENERAL_TRANSFORM, 520 * or a bit-wise combination of TYPE_TRANSLATION, the mutually exclusive 521 * TYPE_*_ROTATIONs, and the mutually exclusive TYPE_*_SCALEs. 522 * 523 * @return The type. 524 * 525 * @see #TYPE_IDENTITY 526 * @see #TYPE_TRANSLATION 527 * @see #TYPE_UNIFORM_SCALE 528 * @see #TYPE_GENERAL_SCALE 529 * @see #TYPE_QUADRANT_ROTATION 530 * @see #TYPE_GENERAL_ROTATION 531 * @see #TYPE_GENERAL_TRANSFORM 532 */ getType()533 public int getType() 534 { 535 return type; 536 } 537 538 /** 539 * Return the determinant of this transform matrix. If the determinant is 540 * non-zero, the transform is invertible; otherwise operations which require 541 * an inverse throw a NoninvertibleTransformException. A result very near 542 * zero, due to rounding errors, may indicate that inversion results do not 543 * carry enough precision to be meaningful. 544 * 545 * <p>If this is a uniform scale transformation, the determinant also 546 * represents the squared value of the scale. Otherwise, it carries little 547 * additional meaning. The determinant is calculated as: 548 * <pre> 549 * | m00 m01 m02 | 550 * | m10 m11 m12 | = m00 * m11 - m01 * m10 551 * | 0 0 1 | 552 * </pre> 553 * 554 * @return the determinant 555 * @see #createInverse() 556 */ getDeterminant()557 public double getDeterminant() 558 { 559 return m00 * m11 - m01 * m10; 560 } 561 562 /** 563 * Return the matrix of values used in this transform. If the matrix has 564 * fewer than 6 entries, only the scale and shear factors are returned; 565 * otherwise the translation factors are copied as well. The resulting 566 * values are: 567 * <pre> 568 * [ d[0] d[2] (d[4]) ] 569 * [ d[1] d[3] (d[5]) ] 570 * [ 0 0 1 ] 571 * </pre> 572 * 573 * @param d the matrix to store the results into; with 4 (6) entries 574 * @throws NullPointerException if d is null 575 * @throws ArrayIndexOutOfBoundsException if d is too small 576 */ getMatrix(double[] d)577 public void getMatrix(double[] d) 578 { 579 d[0] = m00; 580 d[1] = m10; 581 d[2] = m01; 582 d[3] = m11; 583 if (d.length >= 6) 584 { 585 d[4] = m02; 586 d[5] = m12; 587 } 588 } 589 590 /** 591 * Returns the X coordinate scaling factor of the matrix. 592 * 593 * @return m00 594 * @see #getMatrix(double[]) 595 */ getScaleX()596 public double getScaleX() 597 { 598 return m00; 599 } 600 601 /** 602 * Returns the Y coordinate scaling factor of the matrix. 603 * 604 * @return m11 605 * @see #getMatrix(double[]) 606 */ getScaleY()607 public double getScaleY() 608 { 609 return m11; 610 } 611 612 /** 613 * Returns the X coordinate shearing factor of the matrix. 614 * 615 * @return m01 616 * @see #getMatrix(double[]) 617 */ getShearX()618 public double getShearX() 619 { 620 return m01; 621 } 622 623 /** 624 * Returns the Y coordinate shearing factor of the matrix. 625 * 626 * @return m10 627 * @see #getMatrix(double[]) 628 */ getShearY()629 public double getShearY() 630 { 631 return m10; 632 } 633 634 /** 635 * Returns the X coordinate translation factor of the matrix. 636 * 637 * @return m02 638 * @see #getMatrix(double[]) 639 */ getTranslateX()640 public double getTranslateX() 641 { 642 return m02; 643 } 644 645 /** 646 * Returns the Y coordinate translation factor of the matrix. 647 * 648 * @return m12 649 * @see #getMatrix(double[]) 650 */ getTranslateY()651 public double getTranslateY() 652 { 653 return m12; 654 } 655 656 /** 657 * Concatenate a translation onto this transform. This is equivalent, but 658 * more efficient than 659 * <code>concatenate(AffineTransform.getTranslateInstance(tx, ty))</code>. 660 * 661 * @param tx the x translation distance 662 * @param ty the y translation distance 663 * @see #getTranslateInstance(double, double) 664 * @see #concatenate(AffineTransform) 665 */ translate(double tx, double ty)666 public void translate(double tx, double ty) 667 { 668 m02 += tx * m00 + ty * m01; 669 m12 += tx * m10 + ty * m11; 670 updateType(); 671 } 672 673 /** 674 * Concatenate a rotation onto this transform. This is equivalent, but 675 * more efficient than 676 * <code>concatenate(AffineTransform.getRotateInstance(theta))</code>. 677 * 678 * @param theta the rotation angle 679 * @see #getRotateInstance(double) 680 * @see #concatenate(AffineTransform) 681 */ rotate(double theta)682 public void rotate(double theta) 683 { 684 double c = Math.cos(theta); 685 double s = Math.sin(theta); 686 double n00 = m00 * c + m01 * s; 687 double n01 = m00 * -s + m01 * c; 688 double n10 = m10 * c + m11 * s; 689 double n11 = m10 * -s + m11 * c; 690 m00 = n00; 691 m01 = n01; 692 m10 = n10; 693 m11 = n11; 694 updateType(); 695 } 696 697 /** 698 * Concatenate a rotation about a point onto this transform. This is 699 * equivalent, but more efficient than 700 * <code>concatenate(AffineTransform.getRotateInstance(theta, x, y))</code>. 701 * 702 * @param theta the rotation angle 703 * @param x the x coordinate of the pivot point 704 * @param y the y coordinate of the pivot point 705 * @see #getRotateInstance(double, double, double) 706 * @see #concatenate(AffineTransform) 707 */ rotate(double theta, double x, double y)708 public void rotate(double theta, double x, double y) 709 { 710 translate(x, y); 711 rotate(theta); 712 translate(-x, -y); 713 } 714 715 /** 716 * Concatenate a scale onto this transform. This is equivalent, but more 717 * efficient than 718 * <code>concatenate(AffineTransform.getScaleInstance(sx, sy))</code>. 719 * 720 * @param sx the x scaling factor 721 * @param sy the y scaling factor 722 * @see #getScaleInstance(double, double) 723 * @see #concatenate(AffineTransform) 724 */ scale(double sx, double sy)725 public void scale(double sx, double sy) 726 { 727 m00 *= sx; 728 m01 *= sy; 729 m10 *= sx; 730 m11 *= sy; 731 updateType(); 732 } 733 734 /** 735 * Concatenate a shearing onto this transform. This is equivalent, but more 736 * efficient than 737 * <code>concatenate(AffineTransform.getShearInstance(sx, sy))</code>. 738 * 739 * @param shx the x shearing factor 740 * @param shy the y shearing factor 741 * @see #getShearInstance(double, double) 742 * @see #concatenate(AffineTransform) 743 */ shear(double shx, double shy)744 public void shear(double shx, double shy) 745 { 746 double n00 = m00 + (shy * m01); 747 double n01 = m01 + (shx * m00); 748 double n10 = m10 + (shy * m11); 749 double n11 = m11 + (shx * m10); 750 m00 = n00; 751 m01 = n01; 752 m10 = n10; 753 m11 = n11; 754 updateType(); 755 } 756 757 /** 758 * Reset this transform to the identity (no transformation): 759 * <pre> 760 * [ 1 0 0 ] 761 * [ 0 1 0 ] 762 * [ 0 0 1 ] 763 * </pre> 764 */ setToIdentity()765 public void setToIdentity() 766 { 767 m00 = m11 = 1; 768 m01 = m02 = m10 = m12 = 0; 769 type = TYPE_IDENTITY; 770 } 771 772 /** 773 * Set this transform to a translation: 774 * <pre> 775 * [ 1 0 tx ] 776 * [ 0 1 ty ] 777 * [ 0 0 1 ] 778 * </pre> 779 * 780 * @param tx the x translation distance 781 * @param ty the y translation distance 782 */ setToTranslation(double tx, double ty)783 public void setToTranslation(double tx, double ty) 784 { 785 m00 = m11 = 1; 786 m01 = m10 = 0; 787 m02 = tx; 788 m12 = ty; 789 type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; 790 } 791 792 /** 793 * Set this transform to a rotation. A positive angle (in radians) rotates 794 * the positive x-axis to the positive y-axis: 795 * <pre> 796 * [ cos(theta) -sin(theta) 0 ] 797 * [ sin(theta) cos(theta) 0 ] 798 * [ 0 0 1 ] 799 * </pre> 800 * 801 * @param theta the rotation angle 802 */ setToRotation(double theta)803 public void setToRotation(double theta) 804 { 805 double c = Math.cos(theta); 806 double s = Math.sin(theta); 807 m00 = c; 808 m01 = -s; 809 m02 = 0; 810 m10 = s; 811 m11 = c; 812 m12 = 0; 813 type = (c == 1 ? TYPE_IDENTITY 814 : c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION 815 : TYPE_GENERAL_ROTATION); 816 } 817 818 /** 819 * Set this transform to a rotation about a point. A positive angle (in 820 * radians) rotates the positive x-axis to the positive y-axis. This is the 821 * same as calling: 822 * <pre> 823 * tx.setToTranslation(x, y); 824 * tx.rotate(theta); 825 * tx.translate(-x, -y); 826 * </pre> 827 * 828 * <p>The resulting matrix is: 829 * <pre> 830 * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 831 * [ sin(theta) cos(theta) y-x*sin-y*cos ] 832 * [ 0 0 1 ] 833 * </pre> 834 * 835 * @param theta the rotation angle 836 * @param x the x coordinate of the pivot point 837 * @param y the y coordinate of the pivot point 838 */ setToRotation(double theta, double x, double y)839 public void setToRotation(double theta, double x, double y) 840 { 841 double c = Math.cos(theta); 842 double s = Math.sin(theta); 843 m00 = c; 844 m01 = -s; 845 m02 = x - x * c + y * s; 846 m10 = s; 847 m11 = c; 848 m12 = y - x * s - y * c; 849 updateType(); 850 } 851 852 /** 853 * Set this transform to a scale: 854 * <pre> 855 * [ sx 0 0 ] 856 * [ 0 sy 0 ] 857 * [ 0 0 1 ] 858 * </pre> 859 * 860 * @param sx the x scaling factor 861 * @param sy the y scaling factor 862 */ setToScale(double sx, double sy)863 public void setToScale(double sx, double sy) 864 { 865 m00 = sx; 866 m01 = m02 = m10 = m12 = 0; 867 m11 = sy; 868 type = (sx != sy ? TYPE_GENERAL_SCALE 869 : sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE); 870 } 871 872 /** 873 * Set this transform to a shear (points are shifted in the x direction based 874 * on a factor of their y coordinate, and in the y direction as a factor of 875 * their x coordinate): 876 * <pre> 877 * [ 1 shx 0 ] 878 * [ shy 1 0 ] 879 * [ 0 0 1 ] 880 * </pre> 881 * 882 * @param shx the x shearing factor 883 * @param shy the y shearing factor 884 */ setToShear(double shx, double shy)885 public void setToShear(double shx, double shy) 886 { 887 m00 = m11 = 1; 888 m01 = shx; 889 m10 = shy; 890 m02 = m12 = 0; 891 updateType(); 892 } 893 894 /** 895 * Set this transform to a copy of the given one. 896 * 897 * @param tx the transform to copy 898 * @throws NullPointerException if tx is null 899 */ setTransform(AffineTransform tx)900 public void setTransform(AffineTransform tx) 901 { 902 m00 = tx.m00; 903 m01 = tx.m01; 904 m02 = tx.m02; 905 m10 = tx.m10; 906 m11 = tx.m11; 907 m12 = tx.m12; 908 type = tx.type; 909 } 910 911 /** 912 * Set this transform to the given values: 913 * <pre> 914 * [ m00 m01 m02 ] 915 * [ m10 m11 m12 ] 916 * [ 0 0 1 ] 917 * </pre> 918 * 919 * @param m00 the x scaling component 920 * @param m10 the y shearing component 921 * @param m01 the x shearing component 922 * @param m11 the y scaling component 923 * @param m02 the x translation component 924 * @param m12 the y translation component 925 */ setTransform(double m00, double m10, double m01, double m11, double m02, double m12)926 public void setTransform(double m00, double m10, double m01, 927 double m11, double m02, double m12) 928 { 929 this.m00 = m00; 930 this.m10 = m10; 931 this.m01 = m01; 932 this.m11 = m11; 933 this.m02 = m02; 934 this.m12 = m12; 935 updateType(); 936 } 937 938 /** 939 * Set this transform to the result of performing the original version of 940 * this followed by tx. This is commonly used when chaining transformations 941 * from one space to another. In matrix form: 942 * <pre> 943 * [ this ] = [ this ] x [ tx ] 944 * </pre> 945 * 946 * @param tx the transform to concatenate 947 * @throws NullPointerException if tx is null 948 * @see #preConcatenate(AffineTransform) 949 */ concatenate(AffineTransform tx)950 public void concatenate(AffineTransform tx) 951 { 952 double n00 = m00 * tx.m00 + m01 * tx.m10; 953 double n01 = m00 * tx.m01 + m01 * tx.m11; 954 double n02 = m00 * tx.m02 + m01 * tx.m12 + m02; 955 double n10 = m10 * tx.m00 + m11 * tx.m10; 956 double n11 = m10 * tx.m01 + m11 * tx.m11; 957 double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; 958 m00 = n00; 959 m01 = n01; 960 m02 = n02; 961 m10 = n10; 962 m11 = n11; 963 m12 = n12; 964 updateType(); 965 } 966 967 /** 968 * Set this transform to the result of performing tx followed by the 969 * original version of this. This is less common than normal concatenation, 970 * but can still be used to chain transformations from one space to another. 971 * In matrix form: 972 * <pre> 973 * [ this ] = [ tx ] x [ this ] 974 * </pre> 975 * 976 * @param tx the transform to concatenate 977 * @throws NullPointerException if tx is null 978 * @see #concatenate(AffineTransform) 979 */ preConcatenate(AffineTransform tx)980 public void preConcatenate(AffineTransform tx) 981 { 982 double n00 = tx.m00 * m00 + tx.m01 * m10; 983 double n01 = tx.m00 * m01 + tx.m01 * m11; 984 double n02 = tx.m00 * m02 + tx.m01 * m12 + tx.m02; 985 double n10 = tx.m10 * m00 + tx.m11 * m10; 986 double n11 = tx.m10 * m01 + tx.m11 * m11; 987 double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; 988 m00 = n00; 989 m01 = n01; 990 m02 = n02; 991 m10 = n10; 992 m11 = n11; 993 m12 = n12; 994 updateType(); 995 } 996 997 /** 998 * Returns a transform, which if concatenated to this one, will result in 999 * the identity transform. This is useful for undoing transformations, but 1000 * is only possible if the original transform has an inverse (ie. does not 1001 * map multiple points to the same line or point). A transform exists only 1002 * if getDeterminant() has a non-zero value. 1003 * 1004 * The inverse is calculated as: 1005 * 1006 * <pre> 1007 * 1008 * Let A be the matrix for which we want to find the inverse: 1009 * 1010 * A = [ m00 m01 m02 ] 1011 * [ m10 m11 m12 ] 1012 * [ 0 0 1 ] 1013 * 1014 * 1015 * 1 1016 * inverse (A) = --- x adjoint(A) 1017 * det 1018 * 1019 * 1020 * 1021 * = 1 [ m11 -m01 m01*m12-m02*m11 ] 1022 * --- x [ -m10 m00 -m00*m12+m10*m02 ] 1023 * det [ 0 0 m00*m11-m10*m01 ] 1024 * 1025 * 1026 * 1027 * = [ m11/det -m01/det m01*m12-m02*m11/det ] 1028 * [ -m10/det m00/det -m00*m12+m10*m02/det ] 1029 * [ 0 0 1 ] 1030 * 1031 * 1032 * </pre> 1033 * 1034 * 1035 * 1036 * @return a new inverse transform 1037 * @throws NoninvertibleTransformException if inversion is not possible 1038 * @see #getDeterminant() 1039 */ createInverse()1040 public AffineTransform createInverse() 1041 throws NoninvertibleTransformException 1042 { 1043 double det = getDeterminant(); 1044 if (det == 0) 1045 throw new NoninvertibleTransformException("can't invert transform"); 1046 1047 double im00 = m11 / det; 1048 double im10 = -m10 / det; 1049 double im01 = -m01 / det; 1050 double im11 = m00 / det; 1051 double im02 = (m01 * m12 - m02 * m11) / det; 1052 double im12 = (-m00 * m12 + m10 * m02) / det; 1053 1054 return new AffineTransform (im00, im10, im01, im11, im02, im12); 1055 } 1056 1057 /** 1058 * Perform this transformation on the given source point, and store the 1059 * result in the destination (creating it if necessary). It is safe for 1060 * src and dst to be the same. 1061 * 1062 * @param src the source point 1063 * @param dst the destination, or null 1064 * @return the transformation of src, in dst if it was non-null 1065 * @throws NullPointerException if src is null 1066 */ transform(Point2D src, Point2D dst)1067 public Point2D transform(Point2D src, Point2D dst) 1068 { 1069 if (dst == null) 1070 dst = new Point2D.Double(); 1071 double x = src.getX(); 1072 double y = src.getY(); 1073 double nx = m00 * x + m01 * y + m02; 1074 double ny = m10 * x + m11 * y + m12; 1075 dst.setLocation(nx, ny); 1076 return dst; 1077 } 1078 1079 /** 1080 * Perform this transformation on an array of points, storing the results 1081 * in another (possibly same) array. This will not create a destination 1082 * array, but will create points for the null entries of the destination. 1083 * The transformation is done sequentially. While having a single source 1084 * and destination point be the same is safe, you should be aware that 1085 * duplicate references to the same point in the source, and having the 1086 * source overlap the destination, may result in your source points changing 1087 * from a previous transform before it is their turn to be evaluated. 1088 * 1089 * @param src the array of source points 1090 * @param srcOff the starting offset into src 1091 * @param dst the array of destination points (may have null entries) 1092 * @param dstOff the starting offset into dst 1093 * @param num the number of points to transform 1094 * @throws NullPointerException if src or dst is null, or src has null 1095 * entries 1096 * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1097 * @throws ArrayStoreException if new points are incompatible with dst 1098 */ transform(Point2D[] src, int srcOff, Point2D[] dst, int dstOff, int num)1099 public void transform(Point2D[] src, int srcOff, 1100 Point2D[] dst, int dstOff, int num) 1101 { 1102 while (--num >= 0) 1103 dst[dstOff] = transform(src[srcOff++], dst[dstOff++]); 1104 } 1105 1106 /** 1107 * Perform this transformation on an array of points, in (x,y) pairs, 1108 * storing the results in another (possibly same) array. This will not 1109 * create a destination array. All sources are copied before the 1110 * transformation, so that no result will overwrite a point that has not yet 1111 * been evaluated. 1112 * 1113 * @param srcPts the array of source points 1114 * @param srcOff the starting offset into src 1115 * @param dstPts the array of destination points 1116 * @param dstOff the starting offset into dst 1117 * @param num the number of points to transform 1118 * @throws NullPointerException if src or dst is null 1119 * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1120 */ transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int num)1121 public void transform(float[] srcPts, int srcOff, 1122 float[] dstPts, int dstOff, int num) 1123 { 1124 if (srcPts == dstPts && dstOff > srcOff 1125 && num > 1 && srcOff + 2 * num > dstOff) 1126 { 1127 float[] f = new float[2 * num]; 1128 System.arraycopy(srcPts, srcOff, f, 0, 2 * num); 1129 srcPts = f; 1130 } 1131 while (--num >= 0) 1132 { 1133 float x = srcPts[srcOff++]; 1134 float y = srcPts[srcOff++]; 1135 dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); 1136 dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); 1137 } 1138 } 1139 1140 /** 1141 * Perform this transformation on an array of points, in (x,y) pairs, 1142 * storing the results in another (possibly same) array. This will not 1143 * create a destination array. All sources are copied before the 1144 * transformation, so that no result will overwrite a point that has not yet 1145 * been evaluated. 1146 * 1147 * @param srcPts the array of source points 1148 * @param srcOff the starting offset into src 1149 * @param dstPts the array of destination points 1150 * @param dstOff the starting offset into dst 1151 * @param num the number of points to transform 1152 * @throws NullPointerException if src or dst is null 1153 * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1154 */ transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int num)1155 public void transform(double[] srcPts, int srcOff, 1156 double[] dstPts, int dstOff, int num) 1157 { 1158 if (srcPts == dstPts && dstOff > srcOff 1159 && num > 1 && srcOff + 2 * num > dstOff) 1160 { 1161 double[] d = new double[2 * num]; 1162 System.arraycopy(srcPts, srcOff, d, 0, 2 * num); 1163 srcPts = d; 1164 } 1165 while (--num >= 0) 1166 { 1167 double x = srcPts[srcOff++]; 1168 double y = srcPts[srcOff++]; 1169 dstPts[dstOff++] = m00 * x + m01 * y + m02; 1170 dstPts[dstOff++] = m10 * x + m11 * y + m12; 1171 } 1172 } 1173 1174 /** 1175 * Perform this transformation on an array of points, in (x,y) pairs, 1176 * storing the results in another array. This will not create a destination 1177 * array. 1178 * 1179 * @param srcPts the array of source points 1180 * @param srcOff the starting offset into src 1181 * @param dstPts the array of destination points 1182 * @param dstOff the starting offset into dst 1183 * @param num the number of points to transform 1184 * @throws NullPointerException if src or dst is null 1185 * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1186 */ transform(float[] srcPts, int srcOff, double[] dstPts, int dstOff, int num)1187 public void transform(float[] srcPts, int srcOff, 1188 double[] dstPts, int dstOff, int num) 1189 { 1190 while (--num >= 0) 1191 { 1192 float x = srcPts[srcOff++]; 1193 float y = srcPts[srcOff++]; 1194 dstPts[dstOff++] = m00 * x + m01 * y + m02; 1195 dstPts[dstOff++] = m10 * x + m11 * y + m12; 1196 } 1197 } 1198 1199 /** 1200 * Perform this transformation on an array of points, in (x,y) pairs, 1201 * storing the results in another array. This will not create a destination 1202 * array. 1203 * 1204 * @param srcPts the array of source points 1205 * @param srcOff the starting offset into src 1206 * @param dstPts the array of destination points 1207 * @param dstOff the starting offset into dst 1208 * @param num the number of points to transform 1209 * @throws NullPointerException if src or dst is null 1210 * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1211 */ transform(double[] srcPts, int srcOff, float[] dstPts, int dstOff, int num)1212 public void transform(double[] srcPts, int srcOff, 1213 float[] dstPts, int dstOff, int num) 1214 { 1215 while (--num >= 0) 1216 { 1217 double x = srcPts[srcOff++]; 1218 double y = srcPts[srcOff++]; 1219 dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); 1220 dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); 1221 } 1222 } 1223 1224 /** 1225 * Perform the inverse of this transformation on the given source point, 1226 * and store the result in the destination (creating it if necessary). It 1227 * is safe for src and dst to be the same. 1228 * 1229 * @param src the source point 1230 * @param dst the destination, or null 1231 * @return the inverse transformation of src, in dst if it was non-null 1232 * @throws NullPointerException if src is null 1233 * @throws NoninvertibleTransformException if the inverse does not exist 1234 * @see #getDeterminant() 1235 */ inverseTransform(Point2D src, Point2D dst)1236 public Point2D inverseTransform(Point2D src, Point2D dst) 1237 throws NoninvertibleTransformException 1238 { 1239 return createInverse().transform(src, dst); 1240 } 1241 1242 /** 1243 * Perform the inverse of this transformation on an array of points, in 1244 * (x,y) pairs, storing the results in another (possibly same) array. This 1245 * will not create a destination array. All sources are copied before the 1246 * transformation, so that no result will overwrite a point that has not yet 1247 * been evaluated. 1248 * 1249 * @param srcPts the array of source points 1250 * @param srcOff the starting offset into src 1251 * @param dstPts the array of destination points 1252 * @param dstOff the starting offset into dst 1253 * @param num the number of points to transform 1254 * @throws NullPointerException if src or dst is null 1255 * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1256 * @throws NoninvertibleTransformException if the inverse does not exist 1257 * @see #getDeterminant() 1258 */ inverseTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int num)1259 public void inverseTransform(double[] srcPts, int srcOff, 1260 double[] dstPts, int dstOff, int num) 1261 throws NoninvertibleTransformException 1262 { 1263 createInverse().transform(srcPts, srcOff, dstPts, dstOff, num); 1264 } 1265 1266 /** 1267 * Perform this transformation, less any translation, on the given source 1268 * point, and store the result in the destination (creating it if 1269 * necessary). It is safe for src and dst to be the same. The reduced 1270 * transform is equivalent to: 1271 * <pre> 1272 * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] 1273 * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] 1274 * </pre> 1275 * 1276 * @param src the source point 1277 * @param dst the destination, or null 1278 * @return the delta transformation of src, in dst if it was non-null 1279 * @throws NullPointerException if src is null 1280 */ deltaTransform(Point2D src, Point2D dst)1281 public Point2D deltaTransform(Point2D src, Point2D dst) 1282 { 1283 if (dst == null) 1284 dst = new Point2D.Double(); 1285 double x = src.getX(); 1286 double y = src.getY(); 1287 double nx = m00 * x + m01 * y; 1288 double ny = m10 * x + m11 * y; 1289 dst.setLocation(nx, ny); 1290 return dst; 1291 } 1292 1293 /** 1294 * Perform this transformation, less any translation, on an array of points, 1295 * in (x,y) pairs, storing the results in another (possibly same) array. 1296 * This will not create a destination array. All sources are copied before 1297 * the transformation, so that no result will overwrite a point that has 1298 * not yet been evaluated. The reduced transform is equivalent to: 1299 * <pre> 1300 * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] 1301 * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] 1302 * </pre> 1303 * 1304 * @param srcPts the array of source points 1305 * @param srcOff the starting offset into src 1306 * @param dstPts the array of destination points 1307 * @param dstOff the starting offset into dst 1308 * @param num the number of points to transform 1309 * @throws NullPointerException if src or dst is null 1310 * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1311 */ deltaTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int num)1312 public void deltaTransform(double[] srcPts, int srcOff, 1313 double[] dstPts, int dstOff, 1314 int num) 1315 { 1316 if (srcPts == dstPts && dstOff > srcOff 1317 && num > 1 && srcOff + 2 * num > dstOff) 1318 { 1319 double[] d = new double[2 * num]; 1320 System.arraycopy(srcPts, srcOff, d, 0, 2 * num); 1321 srcPts = d; 1322 } 1323 while (--num >= 0) 1324 { 1325 double x = srcPts[srcOff++]; 1326 double y = srcPts[srcOff++]; 1327 dstPts[dstOff++] = m00 * x + m01 * y; 1328 dstPts[dstOff++] = m10 * x + m11 * y; 1329 } 1330 } 1331 1332 /** 1333 * Return a new Shape, based on the given one, where the path of the shape 1334 * has been transformed by this transform. Notice that this uses GeneralPath, 1335 * which only stores points in float precision. 1336 * 1337 * @param src the shape source to transform 1338 * @return the shape, transformed by this, <code>null</code> if src is 1339 * <code>null</code>. 1340 * @see GeneralPath#transform(AffineTransform) 1341 */ createTransformedShape(Shape src)1342 public Shape createTransformedShape(Shape src) 1343 { 1344 if(src == null) 1345 return null; 1346 GeneralPath p = new GeneralPath(src); 1347 p.transform(this); 1348 return p; 1349 } 1350 1351 /** 1352 * Returns a string representation of the transform, in the format: 1353 * <code>"AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" 1354 * + m10 + ", " + m11 + ", " + m12 + "]]"</code>. 1355 * 1356 * @return the string representation 1357 */ toString()1358 public String toString() 1359 { 1360 return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" 1361 + m10 + ", " + m11 + ", " + m12 + "]]"; 1362 } 1363 1364 /** 1365 * Tests if this transformation is the identity: 1366 * <pre> 1367 * [ 1 0 0 ] 1368 * [ 0 1 0 ] 1369 * [ 0 0 1 ] 1370 * </pre> 1371 * 1372 * @return true if this is the identity transform 1373 */ isIdentity()1374 public boolean isIdentity() 1375 { 1376 // Rather than rely on type, check explicitly. 1377 return (m00 == 1 && m01 == 0 && m02 == 0 1378 && m10 == 0 && m11 == 1 && m12 == 0); 1379 } 1380 1381 /** 1382 * Create a new transform of the same run-time type, with the same 1383 * transforming properties as this one. 1384 * 1385 * @return the clone 1386 */ clone()1387 public Object clone() 1388 { 1389 try 1390 { 1391 return super.clone(); 1392 } 1393 catch (CloneNotSupportedException e) 1394 { 1395 throw (Error) new InternalError().initCause(e); // Impossible 1396 } 1397 } 1398 1399 /** 1400 * Return the hashcode for this transformation. The formula is not 1401 * documented, but appears to be the same as: 1402 * <pre> 1403 * long l = Double.doubleToLongBits(getScaleX()); 1404 * l = l * 31 + Double.doubleToLongBits(getShearX()); 1405 * l = l * 31 + Double.doubleToLongBits(getTranslateX()); 1406 * l = l * 31 + Double.doubleToLongBits(getShearY()); 1407 * l = l * 31 + Double.doubleToLongBits(getScaleY()); 1408 * l = l * 31 + Double.doubleToLongBits(getTranslateY()); 1409 * return (int) ((l >> 32) ^ l); 1410 * </pre> 1411 * 1412 * @return the hashcode 1413 */ hashCode()1414 public int hashCode() 1415 { 1416 long l = Double.doubleToLongBits(m00); 1417 l = l * 31 + Double.doubleToLongBits(m01); 1418 l = l * 31 + Double.doubleToLongBits(m02); 1419 l = l * 31 + Double.doubleToLongBits(m10); 1420 l = l * 31 + Double.doubleToLongBits(m11); 1421 l = l * 31 + Double.doubleToLongBits(m12); 1422 return (int) ((l >> 32) ^ l); 1423 } 1424 1425 /** 1426 * Compares two transforms for equality. This returns true if they have the 1427 * same matrix values. 1428 * 1429 * @param obj the transform to compare 1430 * @return true if it is equal 1431 */ equals(Object obj)1432 public boolean equals(Object obj) 1433 { 1434 if (! (obj instanceof AffineTransform)) 1435 return false; 1436 AffineTransform t = (AffineTransform) obj; 1437 return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02 1438 && m10 == t.m10 && m11 == t.m11 && m12 == t.m12); 1439 } 1440 1441 /** 1442 * Helper to decode the type from the matrix. This is not guaranteed 1443 * to find the optimal type, but at least it will be valid. 1444 */ updateType()1445 private void updateType() 1446 { 1447 double det = getDeterminant(); 1448 if (det == 0) 1449 { 1450 type = TYPE_GENERAL_TRANSFORM; 1451 return; 1452 } 1453 // Scale (includes rotation by PI) or translation. 1454 if (m01 == 0 && m10 == 0) 1455 { 1456 if (m00 == m11) 1457 type = m00 == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE; 1458 else 1459 type = TYPE_GENERAL_SCALE; 1460 if (m02 != 0 || m12 != 0) 1461 type |= TYPE_TRANSLATION; 1462 } 1463 // Rotation. 1464 else if (m00 == m11 && m01 == -m10) 1465 { 1466 type = m00 == 0 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION; 1467 if (det != 1) 1468 type |= TYPE_UNIFORM_SCALE; 1469 if (m02 != 0 || m12 != 0) 1470 type |= TYPE_TRANSLATION; 1471 } 1472 else 1473 type = TYPE_GENERAL_TRANSFORM; 1474 } 1475 1476 /** 1477 * Reads a transform from an object stream. 1478 * 1479 * @param s the stream to read from 1480 * @throws ClassNotFoundException if there is a problem deserializing 1481 * @throws IOException if there is a problem deserializing 1482 */ readObject(ObjectInputStream s)1483 private void readObject(ObjectInputStream s) 1484 throws ClassNotFoundException, IOException 1485 { 1486 s.defaultReadObject(); 1487 updateType(); 1488 } 1489 } // class AffineTransform 1490