1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 /** 18 * @author Denis M. Kishenko 19 * @version $Revision$ 20 */ 21 22 package java.awt.geom; 23 24 import java.awt.Shape; 25 import java.io.IOException; 26 import java.io.Serializable; 27 28 import org.apache.harmony.awt.internal.nls.Messages; 29 import org.apache.harmony.misc.HashCode; 30 31 /** 32 * The Class AffineTransform represents a linear transformation (rotation, 33 * scaling, or shear) followed by a translation that acts on a coordinate space. 34 * It preserves collinearity of points and ratios of distances between collinear 35 * points: so if A, B, and C are on a line, then after the space has been 36 * transformed via the affine transform, the images of the three points will 37 * still be on a line, and the ratio of the distance from A to B with the 38 * distance from B to C will be the same as the corresponding ratio in the image 39 * space. 40 * 41 * @since Android 1.0 42 */ 43 public class AffineTransform implements Cloneable, Serializable { 44 45 /** 46 * The Constant serialVersionUID. 47 */ 48 private static final long serialVersionUID = 1330973210523860834L; 49 50 /** 51 * The Constant TYPE_IDENTITY. 52 */ 53 public static final int TYPE_IDENTITY = 0; 54 55 /** 56 * The Constant TYPE_TRANSLATION. 57 */ 58 public static final int TYPE_TRANSLATION = 1; 59 60 /** 61 * The Constant TYPE_UNIFORM_SCALE. 62 */ 63 public static final int TYPE_UNIFORM_SCALE = 2; 64 65 /** 66 * The Constant TYPE_GENERAL_SCALE. 67 */ 68 public static final int TYPE_GENERAL_SCALE = 4; 69 70 /** 71 * The Constant TYPE_QUADRANT_ROTATION. 72 */ 73 public static final int TYPE_QUADRANT_ROTATION = 8; 74 75 /** 76 * The Constant TYPE_GENERAL_ROTATION. 77 */ 78 public static final int TYPE_GENERAL_ROTATION = 16; 79 80 /** 81 * The Constant TYPE_GENERAL_TRANSFORM. 82 */ 83 public static final int TYPE_GENERAL_TRANSFORM = 32; 84 85 /** 86 * The Constant TYPE_FLIP. 87 */ 88 public static final int TYPE_FLIP = 64; 89 90 /** 91 * The Constant TYPE_MASK_SCALE. 92 */ 93 public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE; 94 95 /** 96 * The Constant TYPE_MASK_ROTATION. 97 */ 98 public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION; 99 100 /** 101 * The <code>TYPE_UNKNOWN</code> is an initial type value. 102 */ 103 static final int TYPE_UNKNOWN = -1; 104 105 /** 106 * The min value equivalent to zero. If absolute value less then ZERO it 107 * considered as zero. 108 */ 109 static final double ZERO = 1E-10; 110 111 /** 112 * The values of transformation matrix. 113 */ 114 double m00; 115 116 /** 117 * The m10. 118 */ 119 double m10; 120 121 /** 122 * The m01. 123 */ 124 double m01; 125 126 /** 127 * The m11. 128 */ 129 double m11; 130 131 /** 132 * The m02. 133 */ 134 double m02; 135 136 /** 137 * The m12. 138 */ 139 double m12; 140 141 /** 142 * The transformation <code>type</code>. 143 */ 144 transient int type; 145 146 /** 147 * Instantiates a new affine transform of type <code>TYPE_IDENTITY</code> 148 * (which leaves coordinates unchanged). 149 */ AffineTransform()150 public AffineTransform() { 151 type = TYPE_IDENTITY; 152 m00 = m11 = 1.0; 153 m10 = m01 = m02 = m12 = 0.0; 154 } 155 156 /** 157 * Instantiates a new affine transform that has the same data as the given 158 * AffineTransform. 159 * 160 * @param t 161 * the transform to copy. 162 */ AffineTransform(AffineTransform t)163 public AffineTransform(AffineTransform t) { 164 this.type = t.type; 165 this.m00 = t.m00; 166 this.m10 = t.m10; 167 this.m01 = t.m01; 168 this.m11 = t.m11; 169 this.m02 = t.m02; 170 this.m12 = t.m12; 171 } 172 173 /** 174 * Instantiates a new affine transform by specifying the values of the 2x3 175 * transformation matrix as floats. The type is set to the default type: 176 * <code>TYPE_UNKNOWN</code> 177 * 178 * @param m00 179 * the m00 entry in the transformation matrix. 180 * @param m10 181 * the m10 entry in the transformation matrix. 182 * @param m01 183 * the m01 entry in the transformation matrix. 184 * @param m11 185 * the m11 entry in the transformation matrix. 186 * @param m02 187 * the m02 entry in the transformation matrix. 188 * @param m12 189 * the m12 entry in the transformation matrix. 190 */ AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12)191 public AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12) { 192 this.type = TYPE_UNKNOWN; 193 this.m00 = m00; 194 this.m10 = m10; 195 this.m01 = m01; 196 this.m11 = m11; 197 this.m02 = m02; 198 this.m12 = m12; 199 } 200 201 /** 202 * Instantiates a new affine transform by specifying the values of the 2x3 203 * transformation matrix as doubles. The type is set to the default type: 204 * <code>TYPE_UNKNOWN</code> 205 * 206 * @param m00 207 * the m00 entry in the transformation matrix. 208 * @param m10 209 * the m10 entry in the transformation matrix. 210 * @param m01 211 * the m01 entry in the transformation matrix. 212 * @param m11 213 * the m11 entry in the transformation matrix. 214 * @param m02 215 * the m02 entry in the transformation matrix. 216 * @param m12 217 * the m12 entry in the transformation matrix. 218 */ AffineTransform(double m00, double m10, double m01, double m11, double m02, double m12)219 public AffineTransform(double m00, double m10, double m01, double m11, double m02, double m12) { 220 this.type = TYPE_UNKNOWN; 221 this.m00 = m00; 222 this.m10 = m10; 223 this.m01 = m01; 224 this.m11 = m11; 225 this.m02 = m02; 226 this.m12 = m12; 227 } 228 229 /** 230 * Instantiates a new affine transform by reading the values of the 231 * transformation matrix from an array of floats. The mapping from the array 232 * to the matrix starts with <code>matrix[0]</code> giving the top-left 233 * entry of the matrix and proceeds with the usual left-to-right and 234 * top-down ordering. 235 * <p> 236 * If the array has only four entries, then the two entries of the last row 237 * of the transformation matrix default to zero. 238 * 239 * @param matrix 240 * the array of four or six floats giving the values of the 241 * matrix. 242 * @throws ArrayIndexOutOfBoundsException 243 * if the size of the array is 0, 1, 2, 3, or 5. 244 */ AffineTransform(float[] matrix)245 public AffineTransform(float[] matrix) { 246 this.type = TYPE_UNKNOWN; 247 m00 = matrix[0]; 248 m10 = matrix[1]; 249 m01 = matrix[2]; 250 m11 = matrix[3]; 251 if (matrix.length > 4) { 252 m02 = matrix[4]; 253 m12 = matrix[5]; 254 } 255 } 256 257 /** 258 * Instantiates a new affine transform by reading the values of the 259 * transformation matrix from an array of doubles. The mapping from the 260 * array to the matrix starts with <code>matrix[0]</code> giving the 261 * top-left entry of the matrix and proceeds with the usual left-to-right 262 * and top-down ordering. 263 * <p> 264 * If the array has only four entries, then the two entries of the last row 265 * of the transformation matrix default to zero. 266 * 267 * @param matrix 268 * the array of four or six doubles giving the values of the 269 * matrix. 270 * @throws ArrayIndexOutOfBoundsException 271 * if the size of the array is 0, 1, 2, 3, or 5. 272 */ AffineTransform(double[] matrix)273 public AffineTransform(double[] matrix) { 274 this.type = TYPE_UNKNOWN; 275 m00 = matrix[0]; 276 m10 = matrix[1]; 277 m01 = matrix[2]; 278 m11 = matrix[3]; 279 if (matrix.length > 4) { 280 m02 = matrix[4]; 281 m12 = matrix[5]; 282 } 283 } 284 285 /** 286 * Returns type of the affine transformation. 287 * <p> 288 * The type is computed as follows: Label the entries of the transformation 289 * matrix as three rows (m00, m01), (m10, m11), and (m02, m12). Then if the 290 * original basis vectors are (1, 0) and (0, 1), the new basis vectors after 291 * transformation are given by (m00, m01) and (m10, m11), and the 292 * translation vector is (m02, m12). 293 * <p> 294 * The types are classified as follows: <br/> TYPE_IDENTITY - no change<br/> 295 * TYPE_TRANSLATION - The translation vector isn't zero<br/> 296 * TYPE_UNIFORM_SCALE - The new basis vectors have equal length<br/> 297 * TYPE_GENERAL_SCALE - The new basis vectors dont' have equal length<br/> 298 * TYPE_FLIP - The new basis vector orientation differs from the original 299 * one<br/> TYPE_QUADRANT_ROTATION - The new basis is a rotation of the 300 * original by 90, 180, 270, or 360 degrees<br/> TYPE_GENERAL_ROTATION - The 301 * new basis is a rotation of the original by an arbitrary angle<br/> 302 * TYPE_GENERAL_TRANSFORM - The transformation can't be inverted.<br/> 303 * <p> 304 * Note that multiple types are possible, thus the types can be combined 305 * using bitwise combinations. 306 * 307 * @return the type of the Affine Transform. 308 */ getType()309 public int getType() { 310 if (type != TYPE_UNKNOWN) { 311 return type; 312 } 313 314 int type = 0; 315 316 if (m00 * m01 + m10 * m11 != 0.0) { 317 type |= TYPE_GENERAL_TRANSFORM; 318 return type; 319 } 320 321 if (m02 != 0.0 || m12 != 0.0) { 322 type |= TYPE_TRANSLATION; 323 } else if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) { 324 type = TYPE_IDENTITY; 325 return type; 326 } 327 328 if (m00 * m11 - m01 * m10 < 0.0) { 329 type |= TYPE_FLIP; 330 } 331 332 double dx = m00 * m00 + m10 * m10; 333 double dy = m01 * m01 + m11 * m11; 334 if (dx != dy) { 335 type |= TYPE_GENERAL_SCALE; 336 } else if (dx != 1.0) { 337 type |= TYPE_UNIFORM_SCALE; 338 } 339 340 if ((m00 == 0.0 && m11 == 0.0) || (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0))) { 341 type |= TYPE_QUADRANT_ROTATION; 342 } else if (m01 != 0.0 || m10 != 0.0) { 343 type |= TYPE_GENERAL_ROTATION; 344 } 345 346 return type; 347 } 348 349 /** 350 * Gets the scale x entry of the transformation matrix (the upper left 351 * matrix entry). 352 * 353 * @return the scale x value. 354 */ getScaleX()355 public double getScaleX() { 356 return m00; 357 } 358 359 /** 360 * Gets the scale y entry of the transformation matrix (the lower right 361 * entry of the linear transformation). 362 * 363 * @return the scale y value. 364 */ getScaleY()365 public double getScaleY() { 366 return m11; 367 } 368 369 /** 370 * Gets the shear x entry of the transformation matrix (the upper right 371 * entry of the linear transformation). 372 * 373 * @return the shear x value. 374 */ getShearX()375 public double getShearX() { 376 return m01; 377 } 378 379 /** 380 * Gets the shear y entry of the transformation matrix (the lower left entry 381 * of the linear transformation). 382 * 383 * @return the shear y value. 384 */ getShearY()385 public double getShearY() { 386 return m10; 387 } 388 389 /** 390 * Gets the x coordinate of the translation vector. 391 * 392 * @return the x coordinate of the translation vector. 393 */ getTranslateX()394 public double getTranslateX() { 395 return m02; 396 } 397 398 /** 399 * Gets the y coordinate of the translation vector. 400 * 401 * @return the y coordinate of the translation vector. 402 */ getTranslateY()403 public double getTranslateY() { 404 return m12; 405 } 406 407 /** 408 * Checks if the AffineTransformation is the identity. 409 * 410 * @return true, if the AffineTransformation is the identity. 411 */ isIdentity()412 public boolean isIdentity() { 413 return getType() == TYPE_IDENTITY; 414 } 415 416 /** 417 * Writes the values of the transformation matrix into the given array of 418 * doubles. If the array has length 4, only the linear transformation part 419 * will be written into it. If it has length greater than 4, the translation 420 * vector will be included as well. 421 * 422 * @param matrix 423 * the array to fill with the values of the matrix. 424 * @throws ArrayIndexOutOfBoundsException 425 * if the size of the array is 0, 1, 2, 3, or 5. 426 */ getMatrix(double[] matrix)427 public void getMatrix(double[] matrix) { 428 matrix[0] = m00; 429 matrix[1] = m10; 430 matrix[2] = m01; 431 matrix[3] = m11; 432 if (matrix.length > 4) { 433 matrix[4] = m02; 434 matrix[5] = m12; 435 } 436 } 437 438 /** 439 * Gets the determinant of the linear transformation matrix. 440 * 441 * @return the determinant of the linear transformation matrix. 442 */ getDeterminant()443 public double getDeterminant() { 444 return m00 * m11 - m01 * m10; 445 } 446 447 /** 448 * Sets the transform in terms of a list of double values. 449 * 450 * @param m00 451 * the m00 coordinate of the transformation matrix. 452 * @param m10 453 * the m10 coordinate of the transformation matrix. 454 * @param m01 455 * the m01 coordinate of the transformation matrix. 456 * @param m11 457 * the m11 coordinate of the transformation matrix. 458 * @param m02 459 * the m02 coordinate of the transformation matrix. 460 * @param m12 461 * the m12 coordinate of the transformation matrix. 462 */ setTransform(double m00, double m10, double m01, double m11, double m02, double m12)463 public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12) { 464 this.type = TYPE_UNKNOWN; 465 this.m00 = m00; 466 this.m10 = m10; 467 this.m01 = m01; 468 this.m11 = m11; 469 this.m02 = m02; 470 this.m12 = m12; 471 } 472 473 /** 474 * Sets the transform's data to match the data of the transform sent as a 475 * parameter. 476 * 477 * @param t 478 * the transform that gives the new values. 479 */ setTransform(AffineTransform t)480 public void setTransform(AffineTransform t) { 481 type = t.type; 482 setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12); 483 } 484 485 /** 486 * Sets the transform to the identity transform. 487 */ setToIdentity()488 public void setToIdentity() { 489 type = TYPE_IDENTITY; 490 m00 = m11 = 1.0; 491 m10 = m01 = m02 = m12 = 0.0; 492 } 493 494 /** 495 * Sets the transformation to a translation alone. Sets the linear part of 496 * the transformation to identity and the translation vector to the values 497 * sent as parameters. Sets the type to <code>TYPE_IDENTITY</code> if the 498 * resulting AffineTransformation is the identity transformation, otherwise 499 * sets it to <code>TYPE_TRANSLATION</code>. 500 * 501 * @param mx 502 * the distance to translate in the x direction. 503 * @param my 504 * the distance to translate in the y direction. 505 */ setToTranslation(double mx, double my)506 public void setToTranslation(double mx, double my) { 507 m00 = m11 = 1.0; 508 m01 = m10 = 0.0; 509 m02 = mx; 510 m12 = my; 511 if (mx == 0.0 && my == 0.0) { 512 type = TYPE_IDENTITY; 513 } else { 514 type = TYPE_TRANSLATION; 515 } 516 } 517 518 /** 519 * Sets the transformation to being a scale alone, eliminating rotation, 520 * shear, and translation elements. Sets the type to 521 * <code>TYPE_IDENTITY</code> if the resulting AffineTransformation is the 522 * identity transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>. 523 * 524 * @param scx 525 * the scaling factor in the x direction. 526 * @param scy 527 * the scaling factor in the y direction. 528 */ setToScale(double scx, double scy)529 public void setToScale(double scx, double scy) { 530 m00 = scx; 531 m11 = scy; 532 m10 = m01 = m02 = m12 = 0.0; 533 if (scx != 1.0 || scy != 1.0) { 534 type = TYPE_UNKNOWN; 535 } else { 536 type = TYPE_IDENTITY; 537 } 538 } 539 540 /** 541 * Sets the transformation to being a shear alone, eliminating rotation, 542 * scaling, and translation elements. Sets the type to 543 * <code>TYPE_IDENTITY</code> if the resulting AffineTransformation is the 544 * identity transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>. 545 * 546 * @param shx 547 * the shearing factor in the x direction. 548 * @param shy 549 * the shearing factor in the y direction. 550 */ setToShear(double shx, double shy)551 public void setToShear(double shx, double shy) { 552 m00 = m11 = 1.0; 553 m02 = m12 = 0.0; 554 m01 = shx; 555 m10 = shy; 556 if (shx != 0.0 || shy != 0.0) { 557 type = TYPE_UNKNOWN; 558 } else { 559 type = TYPE_IDENTITY; 560 } 561 } 562 563 /** 564 * Sets the transformation to being a rotation alone, eliminating shearing, 565 * scaling, and translation elements. Sets the type to 566 * <code>TYPE_IDENTITY</code> if the resulting AffineTransformation is the 567 * identity transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>. 568 * 569 * @param angle 570 * the angle of rotation in radians. 571 */ setToRotation(double angle)572 public void setToRotation(double angle) { 573 double sin = Math.sin(angle); 574 double cos = Math.cos(angle); 575 if (Math.abs(cos) < ZERO) { 576 cos = 0.0; 577 sin = sin > 0.0 ? 1.0 : -1.0; 578 } else if (Math.abs(sin) < ZERO) { 579 sin = 0.0; 580 cos = cos > 0.0 ? 1.0 : -1.0; 581 } 582 m00 = m11 = cos; 583 m01 = -sin; 584 m10 = sin; 585 m02 = m12 = 0.0; 586 type = TYPE_UNKNOWN; 587 } 588 589 /** 590 * Sets the transformation to being a rotation followed by a translation. 591 * Sets the type to <code>TYPE_UNKNOWN</code>. 592 * 593 * @param angle 594 * the angle of rotation in radians. 595 * @param px 596 * the distance to translate in the x direction. 597 * @param py 598 * the distance to translate in the y direction. 599 */ setToRotation(double angle, double px, double py)600 public void setToRotation(double angle, double px, double py) { 601 setToRotation(angle); 602 m02 = px * (1.0 - m00) + py * m10; 603 m12 = py * (1.0 - m00) - px * m10; 604 type = TYPE_UNKNOWN; 605 } 606 607 /** 608 * Creates a new AffineTransformation that is a translation alone with the 609 * translation vector given by the values sent as parameters. The new 610 * transformation's type is <code>TYPE_IDENTITY</code> if the 611 * AffineTransformation is the identity transformation, otherwise it's 612 * <code>TYPE_TRANSLATION</code>. 613 * 614 * @param mx 615 * the distance to translate in the x direction. 616 * @param my 617 * the distance to translate in the y direction. 618 * @return the new AffineTransformation. 619 */ getTranslateInstance(double mx, double my)620 public static AffineTransform getTranslateInstance(double mx, double my) { 621 AffineTransform t = new AffineTransform(); 622 t.setToTranslation(mx, my); 623 return t; 624 } 625 626 /** 627 * Creates a new AffineTransformation that is a scale alone. The new 628 * transformation's type is <code>TYPE_IDENTITY</code> if the 629 * AffineTransformation is the identity transformation, otherwise it's 630 * <code>TYPE_UNKNOWN</code>. 631 * 632 * @param scx 633 * the scaling factor in the x direction. 634 * @param scY 635 * the scaling factor in the y direction. 636 * @return the new AffineTransformation. 637 */ getScaleInstance(double scx, double scY)638 public static AffineTransform getScaleInstance(double scx, double scY) { 639 AffineTransform t = new AffineTransform(); 640 t.setToScale(scx, scY); 641 return t; 642 } 643 644 /** 645 * Creates a new AffineTransformation that is a shear alone. The new 646 * transformation's type is <code>TYPE_IDENTITY</code> if the 647 * AffineTransformation is the identity transformation, otherwise it's 648 * <code>TYPE_UNKNOWN</code>. 649 * 650 * @param shx 651 * the shearing factor in the x direction. 652 * @param shy 653 * the shearing factor in the y direction. 654 * @return the new AffineTransformation. 655 */ getShearInstance(double shx, double shy)656 public static AffineTransform getShearInstance(double shx, double shy) { 657 AffineTransform m = new AffineTransform(); 658 m.setToShear(shx, shy); 659 return m; 660 } 661 662 /** 663 * Creates a new AffineTransformation that is a rotation alone. The new 664 * transformation's type is <code>TYPE_IDENTITY</code> if the 665 * AffineTransformation is the identity transformation, otherwise it's 666 * <code>TYPE_UNKNOWN</code>. 667 * 668 * @param angle 669 * the angle of rotation in radians. 670 * @return the new AffineTransformation. 671 */ getRotateInstance(double angle)672 public static AffineTransform getRotateInstance(double angle) { 673 AffineTransform t = new AffineTransform(); 674 t.setToRotation(angle); 675 return t; 676 } 677 678 /** 679 * Creates a new AffineTransformation that is a rotation followed by a 680 * translation. Sets the type to <code>TYPE_UNKNOWN</code>. 681 * 682 * @param angle 683 * the angle of rotation in radians. 684 * @param x 685 * the distance to translate in the x direction. 686 * @param y 687 * the distance to translate in the y direction. 688 * @return the new AffineTransformation. 689 */ getRotateInstance(double angle, double x, double y)690 public static AffineTransform getRotateInstance(double angle, double x, double y) { 691 AffineTransform t = new AffineTransform(); 692 t.setToRotation(angle, x, y); 693 return t; 694 } 695 696 /** 697 * Applies a translation to this AffineTransformation. 698 * 699 * @param mx 700 * the distance to translate in the x direction. 701 * @param my 702 * the distance to translate in the y direction. 703 */ translate(double mx, double my)704 public void translate(double mx, double my) { 705 concatenate(AffineTransform.getTranslateInstance(mx, my)); 706 } 707 708 /** 709 * Applies a scaling transformation to this AffineTransformation. 710 * 711 * @param scx 712 * the scaling factor in the x direction. 713 * @param scy 714 * the scaling factor in the y direction. 715 */ scale(double scx, double scy)716 public void scale(double scx, double scy) { 717 concatenate(AffineTransform.getScaleInstance(scx, scy)); 718 } 719 720 /** 721 * Applies a shearing transformation to this AffineTransformation. 722 * 723 * @param shx 724 * the shearing factor in the x direction. 725 * @param shy 726 * the shearing factor in the y direction. 727 */ shear(double shx, double shy)728 public void shear(double shx, double shy) { 729 concatenate(AffineTransform.getShearInstance(shx, shy)); 730 } 731 732 /** 733 * Applies a rotation transformation to this AffineTransformation. 734 * 735 * @param angle 736 * the angle of rotation in radians. 737 */ rotate(double angle)738 public void rotate(double angle) { 739 concatenate(AffineTransform.getRotateInstance(angle)); 740 } 741 742 /** 743 * Applies a rotation and translation transformation to this 744 * AffineTransformation. 745 * 746 * @param angle 747 * the angle of rotation in radians. 748 * @param px 749 * the distance to translate in the x direction. 750 * @param py 751 * the distance to translate in the y direction. 752 */ rotate(double angle, double px, double py)753 public void rotate(double angle, double px, double py) { 754 concatenate(AffineTransform.getRotateInstance(angle, px, py)); 755 } 756 757 /** 758 * Multiplies the matrix representations of two AffineTransform objects. 759 * 760 * @param t1 761 * - the AffineTransform object is a multiplicand 762 * @param t2 763 * - the AffineTransform object is a multiplier 764 * @return an AffineTransform object that is the result of t1 multiplied by 765 * the matrix t2. 766 */ multiply(AffineTransform t1, AffineTransform t2)767 AffineTransform multiply(AffineTransform t1, AffineTransform t2) { 768 return new AffineTransform(t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00 769 t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01 770 t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10 771 t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11 772 t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02 773 t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12);// m12 774 } 775 776 /** 777 * Applies the given AffineTransform to this AffineTransform via matrix 778 * multiplication. 779 * 780 * @param t 781 * the AffineTransform to apply to this AffineTransform. 782 */ concatenate(AffineTransform t)783 public void concatenate(AffineTransform t) { 784 setTransform(multiply(t, this)); 785 } 786 787 /** 788 * Changes the current AffineTransform the one obtained by taking the 789 * transform t and applying this AffineTransform to it. 790 * 791 * @param t 792 * the AffineTransform that this AffineTransform is multiplied 793 * by. 794 */ preConcatenate(AffineTransform t)795 public void preConcatenate(AffineTransform t) { 796 setTransform(multiply(this, t)); 797 } 798 799 /** 800 * Creates an AffineTransform that is the inverse of this transform. 801 * 802 * @return the affine transform that is the inverse of this AffineTransform. 803 * @throws NoninvertibleTransformException 804 * if this AffineTransform cannot be inverted (the determinant 805 * of the linear transformation part is zero). 806 */ createInverse()807 public AffineTransform createInverse() throws NoninvertibleTransformException { 808 double det = getDeterminant(); 809 if (Math.abs(det) < ZERO) { 810 // awt.204=Determinant is zero 811 throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$ 812 } 813 return new AffineTransform(m11 / det, // m00 814 -m10 / det, // m10 815 -m01 / det, // m01 816 m00 / det, // m11 817 (m01 * m12 - m11 * m02) / det, // m02 818 (m10 * m02 - m00 * m12) / det // m12 819 ); 820 } 821 822 /** 823 * Apply the current AffineTransform to the point. 824 * 825 * @param src 826 * the original point. 827 * @param dst 828 * Point2D object to be filled with the destination coordinates 829 * (where the original point is sent by this AffineTransform). 830 * May be null. 831 * @return the point in the AffineTransform's image space where the original 832 * point is sent. 833 */ transform(Point2D src, Point2D dst)834 public Point2D transform(Point2D src, Point2D dst) { 835 if (dst == null) { 836 if (src instanceof Point2D.Double) { 837 dst = new Point2D.Double(); 838 } else { 839 dst = new Point2D.Float(); 840 } 841 } 842 843 double x = src.getX(); 844 double y = src.getY(); 845 846 dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12); 847 return dst; 848 } 849 850 /** 851 * Applies this AffineTransform to an array of points. 852 * 853 * @param src 854 * the array of points to be transformed. 855 * @param srcOff 856 * the offset in the source point array of the first point to be 857 * transformed. 858 * @param dst 859 * the point array where the images of the points (after applying 860 * the AffineTransformation) should be placed. 861 * @param dstOff 862 * the offset in the destination array where the new values 863 * should be written. 864 * @param length 865 * the number of points to transform. 866 * @throws ArrayIndexOutOfBoundsException 867 * if <code>srcOff + length > src.length</code> or 868 * <code>dstOff + length > dst.length</code>. 869 */ transform(Point2D[] src, int srcOff, Point2D[] dst, int dstOff, int length)870 public void transform(Point2D[] src, int srcOff, Point2D[] dst, int dstOff, int length) { 871 while (--length >= 0) { 872 Point2D srcPoint = src[srcOff++]; 873 double x = srcPoint.getX(); 874 double y = srcPoint.getY(); 875 Point2D dstPoint = dst[dstOff]; 876 if (dstPoint == null) { 877 if (srcPoint instanceof Point2D.Double) { 878 dstPoint = new Point2D.Double(); 879 } else { 880 dstPoint = new Point2D.Float(); 881 } 882 } 883 dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12); 884 dst[dstOff++] = dstPoint; 885 } 886 } 887 888 /** 889 * Applies this AffineTransform to a set of points given as an array of 890 * double values where every two values in the array give the coordinates of 891 * a point; the even-indexed values giving the x coordinates and the 892 * odd-indexed values giving the y coordinates. 893 * 894 * @param src 895 * the array of points to be transformed. 896 * @param srcOff 897 * the offset in the source point array of the first point to be 898 * transformed. 899 * @param dst 900 * the point array where the images of the points (after applying 901 * the AffineTransformation) should be placed. 902 * @param dstOff 903 * the offset in the destination array where the new values 904 * should be written. 905 * @param length 906 * the number of points to transform. 907 * @throws ArrayIndexOutOfBoundsException 908 * if <code>srcOff + length*2 > src.length</code> or 909 * <code>dstOff + length*2 > dst.length</code>. 910 */ transform(double[] src, int srcOff, double[] dst, int dstOff, int length)911 public void transform(double[] src, int srcOff, double[] dst, int dstOff, int length) { 912 int step = 2; 913 if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) { 914 srcOff = srcOff + length * 2 - 2; 915 dstOff = dstOff + length * 2 - 2; 916 step = -2; 917 } 918 while (--length >= 0) { 919 double x = src[srcOff + 0]; 920 double y = src[srcOff + 1]; 921 dst[dstOff + 0] = x * m00 + y * m01 + m02; 922 dst[dstOff + 1] = x * m10 + y * m11 + m12; 923 srcOff += step; 924 dstOff += step; 925 } 926 } 927 928 /** 929 * Applies this AffineTransform to a set of points given as an array of 930 * float values where every two values in the array give the coordinates of 931 * a point; the even-indexed values giving the x coordinates and the 932 * odd-indexed values giving the y coordinates. 933 * 934 * @param src 935 * the array of points to be transformed. 936 * @param srcOff 937 * the offset in the source point array of the first point to be 938 * transformed. 939 * @param dst 940 * the point array where the images of the points (after applying 941 * the AffineTransformation) should be placed. 942 * @param dstOff 943 * the offset in the destination array where the new values 944 * should be written. 945 * @param length 946 * the number of points to transform. 947 * @throws ArrayIndexOutOfBoundsException 948 * if <code>srcOff + length*2 > src.length</code> or 949 * <code>dstOff + length*2 > dst.length</code>. 950 */ transform(float[] src, int srcOff, float[] dst, int dstOff, int length)951 public void transform(float[] src, int srcOff, float[] dst, int dstOff, int length) { 952 int step = 2; 953 if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) { 954 srcOff = srcOff + length * 2 - 2; 955 dstOff = dstOff + length * 2 - 2; 956 step = -2; 957 } 958 while (--length >= 0) { 959 float x = src[srcOff + 0]; 960 float y = src[srcOff + 1]; 961 dst[dstOff + 0] = (float)(x * m00 + y * m01 + m02); 962 dst[dstOff + 1] = (float)(x * m10 + y * m11 + m12); 963 srcOff += step; 964 dstOff += step; 965 } 966 } 967 968 /** 969 * Applies this AffineTransform to a set of points given as an array of 970 * float values where every two values in the array give the coordinates of 971 * a point; the even-indexed values giving the x coordinates and the 972 * odd-indexed values giving the y coordinates. The destination coordinates 973 * are given as values of type <code>double</code>. 974 * 975 * @param src 976 * the array of points to be transformed. 977 * @param srcOff 978 * the offset in the source point array of the first point to be 979 * transformed. 980 * @param dst 981 * the point array where the images of the points (after applying 982 * the AffineTransformation) should be placed. 983 * @param dstOff 984 * the offset in the destination array where the new values 985 * should be written. 986 * @param length 987 * the number of points to transform. 988 * @throws ArrayIndexOutOfBoundsException 989 * if <code>srcOff + length*2 > src.length</code> or 990 * <code>dstOff + length*2 > dst.length</code>. 991 */ transform(float[] src, int srcOff, double[] dst, int dstOff, int length)992 public void transform(float[] src, int srcOff, double[] dst, int dstOff, int length) { 993 while (--length >= 0) { 994 float x = src[srcOff++]; 995 float y = src[srcOff++]; 996 dst[dstOff++] = x * m00 + y * m01 + m02; 997 dst[dstOff++] = x * m10 + y * m11 + m12; 998 } 999 } 1000 1001 /** 1002 * Applies this AffineTransform to a set of points given as an array of 1003 * double values where every two values in the array give the coordinates of 1004 * a point; the even-indexed values giving the x coordinates and the 1005 * odd-indexed values giving the y coordinates. The destination coordinates 1006 * are given as values of type <code>float</code>. 1007 * 1008 * @param src 1009 * the array of points to be transformed. 1010 * @param srcOff 1011 * the offset in the source point array of the first point to be 1012 * transformed. 1013 * @param dst 1014 * the point array where the images of the points (after applying 1015 * the AffineTransformation) should be placed. 1016 * @param dstOff 1017 * the offset in the destination array where the new values 1018 * should be written. 1019 * @param length 1020 * the number of points to transform. 1021 * @throws ArrayIndexOutOfBoundsException 1022 * if <code>srcOff + length*2 > src.length</code> or 1023 * <code>dstOff + length*2 > dst.length</code>. 1024 */ transform(double[] src, int srcOff, float[] dst, int dstOff, int length)1025 public void transform(double[] src, int srcOff, float[] dst, int dstOff, int length) { 1026 while (--length >= 0) { 1027 double x = src[srcOff++]; 1028 double y = src[srcOff++]; 1029 dst[dstOff++] = (float)(x * m00 + y * m01 + m02); 1030 dst[dstOff++] = (float)(x * m10 + y * m11 + m12); 1031 } 1032 } 1033 1034 /** 1035 * Transforms the point according to the linear transformation part of this 1036 * AffineTransformation (without applying the translation). 1037 * 1038 * @param src 1039 * the original point. 1040 * @param dst 1041 * the point object where the result of the delta transform is 1042 * written. 1043 * @return the result of applying the delta transform (linear part only) to 1044 * the original point. 1045 */ 1046 // TODO: is this right? if dst is null, we check what it's an 1047 // instance of? Shouldn't it be src instanceof Point2D.Double? deltaTransform(Point2D src, Point2D dst)1048 public Point2D deltaTransform(Point2D src, Point2D dst) { 1049 if (dst == null) { 1050 if (dst instanceof Point2D.Double) { 1051 dst = new Point2D.Double(); 1052 } else { 1053 dst = new Point2D.Float(); 1054 } 1055 } 1056 1057 double x = src.getX(); 1058 double y = src.getY(); 1059 1060 dst.setLocation(x * m00 + y * m01, x * m10 + y * m11); 1061 return dst; 1062 } 1063 1064 /** 1065 * Applies the linear transformation part of this AffineTransform (ignoring 1066 * the translation part) to a set of points given as an array of double 1067 * values where every two values in the array give the coordinates of a 1068 * point; the even-indexed values giving the x coordinates and the 1069 * odd-indexed values giving the y coordinates. 1070 * 1071 * @param src 1072 * the array of points to be transformed. 1073 * @param srcOff 1074 * the offset in the source point array of the first point to be 1075 * transformed. 1076 * @param dst 1077 * the point array where the images of the points (after applying 1078 * the delta transformation) should be placed. 1079 * @param dstOff 1080 * the offset in the destination array where the new values 1081 * should be written. 1082 * @param length 1083 * the number of points to transform. 1084 * @throws ArrayIndexOutOfBoundsException 1085 * if <code>srcOff + length*2 > src.length</code> or 1086 * <code>dstOff + length*2 > dst.length</code>. 1087 */ deltaTransform(double[] src, int srcOff, double[] dst, int dstOff, int length)1088 public void deltaTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) { 1089 while (--length >= 0) { 1090 double x = src[srcOff++]; 1091 double y = src[srcOff++]; 1092 dst[dstOff++] = x * m00 + y * m01; 1093 dst[dstOff++] = x * m10 + y * m11; 1094 } 1095 } 1096 1097 /** 1098 * Transforms the point according to the inverse of this 1099 * AffineTransformation. 1100 * 1101 * @param src 1102 * the original point. 1103 * @param dst 1104 * the point object where the result of the inverse transform is 1105 * written (may be null). 1106 * @return the result of applying the inverse transform. Inverse transform. 1107 * @throws NoninvertibleTransformException 1108 * if this AffineTransform cannot be inverted (the determinant 1109 * of the linear transformation part is zero). 1110 */ inverseTransform(Point2D src, Point2D dst)1111 public Point2D inverseTransform(Point2D src, Point2D dst) 1112 throws NoninvertibleTransformException { 1113 double det = getDeterminant(); 1114 if (Math.abs(det) < ZERO) { 1115 // awt.204=Determinant is zero 1116 throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$ 1117 } 1118 1119 if (dst == null) { 1120 if (src instanceof Point2D.Double) { 1121 dst = new Point2D.Double(); 1122 } else { 1123 dst = new Point2D.Float(); 1124 } 1125 } 1126 1127 double x = src.getX() - m02; 1128 double y = src.getY() - m12; 1129 1130 dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10) / det); 1131 return dst; 1132 } 1133 1134 /** 1135 * Applies the inverse of this AffineTransform to a set of points given as 1136 * an array of double values where every two values in the array give the 1137 * coordinates of a point; the even-indexed values giving the x coordinates 1138 * and the odd-indexed values giving the y coordinates. 1139 * 1140 * @param src 1141 * the array of points to be transformed. 1142 * @param srcOff 1143 * the offset in the source point array of the first point to be 1144 * transformed. 1145 * @param dst 1146 * the point array where the images of the points (after applying 1147 * the inverse of the AffineTransformation) should be placed. 1148 * @param dstOff 1149 * the offset in the destination array where the new values 1150 * should be written. 1151 * @param length 1152 * the number of points to transform. 1153 * @throws ArrayIndexOutOfBoundsException 1154 * if <code>srcOff + length*2 > src.length</code> or 1155 * <code>dstOff + length*2 > dst.length</code>. 1156 * @throws NoninvertibleTransformException 1157 * if this AffineTransform cannot be inverted (the determinant 1158 * of the linear transformation part is zero). 1159 */ inverseTransform(double[] src, int srcOff, double[] dst, int dstOff, int length)1160 public void inverseTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) 1161 throws NoninvertibleTransformException { 1162 double det = getDeterminant(); 1163 if (Math.abs(det) < ZERO) { 1164 // awt.204=Determinant is zero 1165 throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$ 1166 } 1167 1168 while (--length >= 0) { 1169 double x = src[srcOff++] - m02; 1170 double y = src[srcOff++] - m12; 1171 dst[dstOff++] = (x * m11 - y * m01) / det; 1172 dst[dstOff++] = (y * m00 - x * m10) / det; 1173 } 1174 } 1175 1176 /** 1177 * Creates a new shape whose data is given by applying this AffineTransform 1178 * to the specified shape. 1179 * 1180 * @param src 1181 * the original shape whose data is to be transformed. 1182 * @return the new shape found by applying this AffineTransform to the 1183 * original shape. 1184 */ createTransformedShape(Shape src)1185 public Shape createTransformedShape(Shape src) { 1186 if (src == null) { 1187 return null; 1188 } 1189 if (src instanceof GeneralPath) { 1190 return ((GeneralPath)src).createTransformedShape(this); 1191 } 1192 PathIterator path = src.getPathIterator(this); 1193 GeneralPath dst = new GeneralPath(path.getWindingRule()); 1194 dst.append(path, false); 1195 return dst; 1196 } 1197 1198 @Override toString()1199 public String toString() { 1200 return getClass().getName() + "[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ 1201 + m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 1202 } 1203 1204 @Override clone()1205 public Object clone() { 1206 try { 1207 return super.clone(); 1208 } catch (CloneNotSupportedException e) { 1209 throw new InternalError(); 1210 } 1211 } 1212 1213 @Override hashCode()1214 public int hashCode() { 1215 HashCode hash = new HashCode(); 1216 hash.append(m00); 1217 hash.append(m01); 1218 hash.append(m02); 1219 hash.append(m10); 1220 hash.append(m11); 1221 hash.append(m12); 1222 return hash.hashCode(); 1223 } 1224 1225 @Override equals(Object obj)1226 public boolean equals(Object obj) { 1227 if (obj == this) { 1228 return true; 1229 } 1230 if (obj instanceof AffineTransform) { 1231 AffineTransform t = (AffineTransform)obj; 1232 return m00 == t.m00 && m01 == t.m01 && m02 == t.m02 && m10 == t.m10 && m11 == t.m11 1233 && m12 == t.m12; 1234 } 1235 return false; 1236 } 1237 1238 /** 1239 * Writes the AffineTrassform object to the output steam. 1240 * 1241 * @param stream 1242 * - the output stream. 1243 * @throws IOException 1244 * - if there are I/O errors while writing to the output stream. 1245 */ writeObject(java.io.ObjectOutputStream stream)1246 private void writeObject(java.io.ObjectOutputStream stream) throws IOException { 1247 stream.defaultWriteObject(); 1248 } 1249 1250 /** 1251 * Read the AffineTransform object from the input stream. 1252 * 1253 * @param stream 1254 * - the input stream. 1255 * @throws IOException 1256 * - if there are I/O errors while reading from the input 1257 * stream. 1258 * @throws ClassNotFoundException 1259 * - if class could not be found. 1260 */ readObject(java.io.ObjectInputStream stream)1261 private void readObject(java.io.ObjectInputStream stream) throws IOException, 1262 ClassNotFoundException { 1263 stream.defaultReadObject(); 1264 type = TYPE_UNKNOWN; 1265 } 1266 1267 } 1268