1 /* 2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt.geom; 27 28 import java.awt.Shape; 29 import java.beans.ConstructorProperties; 30 31 /** 32 * The {@code AffineTransform} class represents a 2D affine transform 33 * that performs a linear mapping from 2D coordinates to other 2D 34 * coordinates that preserves the "straightness" and 35 * "parallelness" of lines. Affine transformations can be constructed 36 * using sequences of translations, scales, flips, rotations, and shears. 37 * <p> 38 * Such a coordinate transformation can be represented by a 3 row by 39 * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix 40 * transforms source coordinates {@code (x,y)} into 41 * destination coordinates {@code (x',y')} by considering 42 * them to be a column vector and multiplying the coordinate vector 43 * by the matrix according to the following process: 44 * <pre> 45 * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ] 46 * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ] 47 * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] 48 * </pre> 49 * <h3><a name="quadrantapproximation">Handling 90-Degree Rotations</a></h3> 50 * <p> 51 * In some variations of the {@code rotate} methods in the 52 * {@code AffineTransform} class, a double-precision argument 53 * specifies the angle of rotation in radians. 54 * These methods have special handling for rotations of approximately 55 * 90 degrees (including multiples such as 180, 270, and 360 degrees), 56 * so that the common case of quadrant rotation is handled more 57 * efficiently. 58 * This special handling can cause angles very close to multiples of 59 * 90 degrees to be treated as if they were exact multiples of 60 * 90 degrees. 61 * For small multiples of 90 degrees the range of angles treated 62 * as a quadrant rotation is approximately 0.00000121 degrees wide. 63 * This section explains why such special care is needed and how 64 * it is implemented. 65 * <p> 66 * Since 90 degrees is represented as {@code PI/2} in radians, 67 * and since PI is a transcendental (and therefore irrational) number, 68 * it is not possible to exactly represent a multiple of 90 degrees as 69 * an exact double precision value measured in radians. 70 * As a result it is theoretically impossible to describe quadrant 71 * rotations (90, 180, 270 or 360 degrees) using these values. 72 * Double precision floating point values can get very close to 73 * non-zero multiples of {@code PI/2} but never close enough 74 * for the sine or cosine to be exactly 0.0, 1.0 or -1.0. 75 * The implementations of {@code Math.sin()} and 76 * {@code Math.cos()} correspondingly never return 0.0 77 * for any case other than {@code Math.sin(0.0)}. 78 * These same implementations do, however, return exactly 1.0 and 79 * -1.0 for some range of numbers around each multiple of 90 80 * degrees since the correct answer is so close to 1.0 or -1.0 that 81 * the double precision significand cannot represent the difference 82 * as accurately as it can for numbers that are near 0.0. 83 * <p> 84 * The net result of these issues is that if the 85 * {@code Math.sin()} and {@code Math.cos()} methods 86 * are used to directly generate the values for the matrix modifications 87 * during these radian-based rotation operations then the resulting 88 * transform is never strictly classifiable as a quadrant rotation 89 * even for a simple case like {@code rotate(Math.PI/2.0)}, 90 * due to minor variations in the matrix caused by the non-0.0 values 91 * obtained for the sine and cosine. 92 * If these transforms are not classified as quadrant rotations then 93 * subsequent code which attempts to optimize further operations based 94 * upon the type of the transform will be relegated to its most general 95 * implementation. 96 * <p> 97 * Because quadrant rotations are fairly common, 98 * this class should handle these cases reasonably quickly, both in 99 * applying the rotations to the transform and in applying the resulting 100 * transform to the coordinates. 101 * To facilitate this optimal handling, the methods which take an angle 102 * of rotation measured in radians attempt to detect angles that are 103 * intended to be quadrant rotations and treat them as such. 104 * These methods therefore treat an angle <em>theta</em> as a quadrant 105 * rotation if either <code>Math.sin(<em>theta</em>)</code> or 106 * <code>Math.cos(<em>theta</em>)</code> returns exactly 1.0 or -1.0. 107 * As a rule of thumb, this property holds true for a range of 108 * approximately 0.0000000211 radians (or 0.00000121 degrees) around 109 * small multiples of {@code Math.PI/2.0}. 110 * 111 * @author Jim Graham 112 * @since 1.2 113 */ 114 public class AffineTransform implements Cloneable, java.io.Serializable { 115 116 /* 117 * This constant is only useful for the cached type field. 118 * It indicates that the type has been decached and must be recalculated. 119 */ 120 private static final int TYPE_UNKNOWN = -1; 121 122 /** 123 * This constant indicates that the transform defined by this object 124 * is an identity transform. 125 * An identity transform is one in which the output coordinates are 126 * always the same as the input coordinates. 127 * If this transform is anything other than the identity transform, 128 * the type will either be the constant GENERAL_TRANSFORM or a 129 * combination of the appropriate flag bits for the various coordinate 130 * conversions that this transform performs. 131 * @see #TYPE_TRANSLATION 132 * @see #TYPE_UNIFORM_SCALE 133 * @see #TYPE_GENERAL_SCALE 134 * @see #TYPE_FLIP 135 * @see #TYPE_QUADRANT_ROTATION 136 * @see #TYPE_GENERAL_ROTATION 137 * @see #TYPE_GENERAL_TRANSFORM 138 * @see #getType 139 * @since 1.2 140 */ 141 public static final int TYPE_IDENTITY = 0; 142 143 /** 144 * This flag bit indicates that the transform defined by this object 145 * performs a translation in addition to the conversions indicated 146 * by other flag bits. 147 * A translation moves the coordinates by a constant amount in x 148 * and y without changing the length or angle of vectors. 149 * @see #TYPE_IDENTITY 150 * @see #TYPE_UNIFORM_SCALE 151 * @see #TYPE_GENERAL_SCALE 152 * @see #TYPE_FLIP 153 * @see #TYPE_QUADRANT_ROTATION 154 * @see #TYPE_GENERAL_ROTATION 155 * @see #TYPE_GENERAL_TRANSFORM 156 * @see #getType 157 * @since 1.2 158 */ 159 public static final int TYPE_TRANSLATION = 1; 160 161 /** 162 * This flag bit indicates that the transform defined by this object 163 * performs a uniform scale in addition to the conversions indicated 164 * by other flag bits. 165 * A uniform scale multiplies the length of vectors by the same amount 166 * in both the x and y directions without changing the angle between 167 * vectors. 168 * This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag. 169 * @see #TYPE_IDENTITY 170 * @see #TYPE_TRANSLATION 171 * @see #TYPE_GENERAL_SCALE 172 * @see #TYPE_FLIP 173 * @see #TYPE_QUADRANT_ROTATION 174 * @see #TYPE_GENERAL_ROTATION 175 * @see #TYPE_GENERAL_TRANSFORM 176 * @see #getType 177 * @since 1.2 178 */ 179 public static final int TYPE_UNIFORM_SCALE = 2; 180 181 /** 182 * This flag bit indicates that the transform defined by this object 183 * performs a general scale in addition to the conversions indicated 184 * by other flag bits. 185 * A general scale multiplies the length of vectors by different 186 * amounts in the x and y directions without changing the angle 187 * between perpendicular vectors. 188 * This flag bit is mutually exclusive with the TYPE_UNIFORM_SCALE flag. 189 * @see #TYPE_IDENTITY 190 * @see #TYPE_TRANSLATION 191 * @see #TYPE_UNIFORM_SCALE 192 * @see #TYPE_FLIP 193 * @see #TYPE_QUADRANT_ROTATION 194 * @see #TYPE_GENERAL_ROTATION 195 * @see #TYPE_GENERAL_TRANSFORM 196 * @see #getType 197 * @since 1.2 198 */ 199 public static final int TYPE_GENERAL_SCALE = 4; 200 201 /** 202 * This constant is a bit mask for any of the scale flag bits. 203 * @see #TYPE_UNIFORM_SCALE 204 * @see #TYPE_GENERAL_SCALE 205 * @since 1.2 206 */ 207 public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE | 208 TYPE_GENERAL_SCALE); 209 210 /** 211 * This flag bit indicates that the transform defined by this object 212 * performs a mirror image flip about some axis which changes the 213 * normally right handed coordinate system into a left handed 214 * system in addition to the conversions indicated by other flag bits. 215 * A right handed coordinate system is one where the positive X 216 * axis rotates counterclockwise to overlay the positive Y axis 217 * similar to the direction that the fingers on your right hand 218 * curl when you stare end on at your thumb. 219 * A left handed coordinate system is one where the positive X 220 * axis rotates clockwise to overlay the positive Y axis similar 221 * to the direction that the fingers on your left hand curl. 222 * There is no mathematical way to determine the angle of the 223 * original flipping or mirroring transformation since all angles 224 * of flip are identical given an appropriate adjusting rotation. 225 * @see #TYPE_IDENTITY 226 * @see #TYPE_TRANSLATION 227 * @see #TYPE_UNIFORM_SCALE 228 * @see #TYPE_GENERAL_SCALE 229 * @see #TYPE_QUADRANT_ROTATION 230 * @see #TYPE_GENERAL_ROTATION 231 * @see #TYPE_GENERAL_TRANSFORM 232 * @see #getType 233 * @since 1.2 234 */ 235 public static final int TYPE_FLIP = 64; 236 /* NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public 237 * circulation and the flag bits could no longer be conveniently 238 * renumbered without introducing binary incompatibility in outside 239 * code. 240 */ 241 242 /** 243 * This flag bit indicates that the transform defined by this object 244 * performs a quadrant rotation by some multiple of 90 degrees in 245 * addition to the conversions indicated by other flag bits. 246 * A rotation changes the angles of vectors by the same amount 247 * regardless of the original direction of the vector and without 248 * changing the length of the vector. 249 * This flag bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag. 250 * @see #TYPE_IDENTITY 251 * @see #TYPE_TRANSLATION 252 * @see #TYPE_UNIFORM_SCALE 253 * @see #TYPE_GENERAL_SCALE 254 * @see #TYPE_FLIP 255 * @see #TYPE_GENERAL_ROTATION 256 * @see #TYPE_GENERAL_TRANSFORM 257 * @see #getType 258 * @since 1.2 259 */ 260 public static final int TYPE_QUADRANT_ROTATION = 8; 261 262 /** 263 * This flag bit indicates that the transform defined by this object 264 * performs a rotation by an arbitrary angle in addition to the 265 * conversions indicated by other flag bits. 266 * A rotation changes the angles of vectors by the same amount 267 * regardless of the original direction of the vector and without 268 * changing the length of the vector. 269 * This flag bit is mutually exclusive with the 270 * TYPE_QUADRANT_ROTATION flag. 271 * @see #TYPE_IDENTITY 272 * @see #TYPE_TRANSLATION 273 * @see #TYPE_UNIFORM_SCALE 274 * @see #TYPE_GENERAL_SCALE 275 * @see #TYPE_FLIP 276 * @see #TYPE_QUADRANT_ROTATION 277 * @see #TYPE_GENERAL_TRANSFORM 278 * @see #getType 279 * @since 1.2 280 */ 281 public static final int TYPE_GENERAL_ROTATION = 16; 282 283 /** 284 * This constant is a bit mask for any of the rotation flag bits. 285 * @see #TYPE_QUADRANT_ROTATION 286 * @see #TYPE_GENERAL_ROTATION 287 * @since 1.2 288 */ 289 public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION | 290 TYPE_GENERAL_ROTATION); 291 292 /** 293 * This constant indicates that the transform defined by this object 294 * performs an arbitrary conversion of the input coordinates. 295 * If this transform can be classified by any of the above constants, 296 * the type will either be the constant TYPE_IDENTITY or a 297 * combination of the appropriate flag bits for the various coordinate 298 * conversions that this transform performs. 299 * @see #TYPE_IDENTITY 300 * @see #TYPE_TRANSLATION 301 * @see #TYPE_UNIFORM_SCALE 302 * @see #TYPE_GENERAL_SCALE 303 * @see #TYPE_FLIP 304 * @see #TYPE_QUADRANT_ROTATION 305 * @see #TYPE_GENERAL_ROTATION 306 * @see #getType 307 * @since 1.2 308 */ 309 public static final int TYPE_GENERAL_TRANSFORM = 32; 310 311 /** 312 * This constant is used for the internal state variable to indicate 313 * that no calculations need to be performed and that the source 314 * coordinates only need to be copied to their destinations to 315 * complete the transformation equation of this transform. 316 * @see #APPLY_TRANSLATE 317 * @see #APPLY_SCALE 318 * @see #APPLY_SHEAR 319 * @see #state 320 */ 321 static final int APPLY_IDENTITY = 0; 322 323 /** 324 * This constant is used for the internal state variable to indicate 325 * that the translation components of the matrix (m02 and m12) need 326 * to be added to complete the transformation equation of this transform. 327 * @see #APPLY_IDENTITY 328 * @see #APPLY_SCALE 329 * @see #APPLY_SHEAR 330 * @see #state 331 */ 332 static final int APPLY_TRANSLATE = 1; 333 334 /** 335 * This constant is used for the internal state variable to indicate 336 * that the scaling components of the matrix (m00 and m11) need 337 * to be factored in to complete the transformation equation of 338 * this transform. If the APPLY_SHEAR bit is also set then it 339 * indicates that the scaling components are not both 0.0. If the 340 * APPLY_SHEAR bit is not also set then it indicates that the 341 * scaling components are not both 1.0. If neither the APPLY_SHEAR 342 * nor the APPLY_SCALE bits are set then the scaling components 343 * are both 1.0, which means that the x and y components contribute 344 * to the transformed coordinate, but they are not multiplied by 345 * any scaling factor. 346 * @see #APPLY_IDENTITY 347 * @see #APPLY_TRANSLATE 348 * @see #APPLY_SHEAR 349 * @see #state 350 */ 351 static final int APPLY_SCALE = 2; 352 353 /** 354 * This constant is used for the internal state variable to indicate 355 * that the shearing components of the matrix (m01 and m10) need 356 * to be factored in to complete the transformation equation of this 357 * transform. The presence of this bit in the state variable changes 358 * the interpretation of the APPLY_SCALE bit as indicated in its 359 * documentation. 360 * @see #APPLY_IDENTITY 361 * @see #APPLY_TRANSLATE 362 * @see #APPLY_SCALE 363 * @see #state 364 */ 365 static final int APPLY_SHEAR = 4; 366 367 /* 368 * For methods which combine together the state of two separate 369 * transforms and dispatch based upon the combination, these constants 370 * specify how far to shift one of the states so that the two states 371 * are mutually non-interfering and provide constants for testing the 372 * bits of the shifted (HI) state. The methods in this class use 373 * the convention that the state of "this" transform is unshifted and 374 * the state of the "other" or "argument" transform is shifted (HI). 375 */ 376 private static final int HI_SHIFT = 3; 377 private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT; 378 private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT; 379 private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT; 380 private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT; 381 382 /** 383 * The X coordinate scaling element of the 3x3 384 * affine transformation matrix. 385 * 386 * @serial 387 */ 388 double m00; 389 390 /** 391 * The Y coordinate shearing element of the 3x3 392 * affine transformation matrix. 393 * 394 * @serial 395 */ 396 double m10; 397 398 /** 399 * The X coordinate shearing element of the 3x3 400 * affine transformation matrix. 401 * 402 * @serial 403 */ 404 double m01; 405 406 /** 407 * The Y coordinate scaling element of the 3x3 408 * affine transformation matrix. 409 * 410 * @serial 411 */ 412 double m11; 413 414 /** 415 * The X coordinate of the translation element of the 416 * 3x3 affine transformation matrix. 417 * 418 * @serial 419 */ 420 double m02; 421 422 /** 423 * The Y coordinate of the translation element of the 424 * 3x3 affine transformation matrix. 425 * 426 * @serial 427 */ 428 double m12; 429 430 /** 431 * This field keeps track of which components of the matrix need to 432 * be applied when performing a transformation. 433 * @see #APPLY_IDENTITY 434 * @see #APPLY_TRANSLATE 435 * @see #APPLY_SCALE 436 * @see #APPLY_SHEAR 437 */ 438 transient int state; 439 440 /** 441 * This field caches the current transformation type of the matrix. 442 * @see #TYPE_IDENTITY 443 * @see #TYPE_TRANSLATION 444 * @see #TYPE_UNIFORM_SCALE 445 * @see #TYPE_GENERAL_SCALE 446 * @see #TYPE_FLIP 447 * @see #TYPE_QUADRANT_ROTATION 448 * @see #TYPE_GENERAL_ROTATION 449 * @see #TYPE_GENERAL_TRANSFORM 450 * @see #TYPE_UNKNOWN 451 * @see #getType 452 */ 453 private transient int type; 454 455 private AffineTransform(double m00, double m10, 456 double m01, double m11, 457 double m02, double m12, 458 int state) { 459 this.m00 = m00; 460 this.m10 = m10; 461 this.m01 = m01; 462 this.m11 = m11; 463 this.m02 = m02; 464 this.m12 = m12; 465 this.state = state; 466 this.type = TYPE_UNKNOWN; 467 } 468 469 /** 470 * Constructs a new {@code AffineTransform} representing the 471 * Identity transformation. 472 * @since 1.2 473 */ 474 public AffineTransform() { 475 m00 = m11 = 1.0; 476 // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */ 477 // state = APPLY_IDENTITY; /* Not needed. */ 478 // type = TYPE_IDENTITY; /* Not needed. */ 479 } 480 481 /** 482 * Constructs a new {@code AffineTransform} that is a copy of 483 * the specified {@code AffineTransform} object. 484 * @param Tx the {@code AffineTransform} object to copy 485 * @since 1.2 486 */ 487 public AffineTransform(AffineTransform Tx) { 488 this.m00 = Tx.m00; 489 this.m10 = Tx.m10; 490 this.m01 = Tx.m01; 491 this.m11 = Tx.m11; 492 this.m02 = Tx.m02; 493 this.m12 = Tx.m12; 494 this.state = Tx.state; 495 this.type = Tx.type; 496 } 497 498 /** 499 * Constructs a new {@code AffineTransform} from 6 floating point 500 * values representing the 6 specifiable entries of the 3x3 501 * transformation matrix. 502 * 503 * @param m00 the X coordinate scaling element of the 3x3 matrix 504 * @param m10 the Y coordinate shearing element of the 3x3 matrix 505 * @param m01 the X coordinate shearing element of the 3x3 matrix 506 * @param m11 the Y coordinate scaling element of the 3x3 matrix 507 * @param m02 the X coordinate translation element of the 3x3 matrix 508 * @param m12 the Y coordinate translation element of the 3x3 matrix 509 * @since 1.2 510 */ 511 @ConstructorProperties({ "scaleX", "shearY", "shearX", "scaleY", "translateX", "translateY" }) 512 public AffineTransform(float m00, float m10, 513 float m01, float m11, 514 float m02, float m12) { 515 this.m00 = m00; 516 this.m10 = m10; 517 this.m01 = m01; 518 this.m11 = m11; 519 this.m02 = m02; 520 this.m12 = m12; 521 updateState(); 522 } 523 524 /** 525 * Constructs a new {@code AffineTransform} from an array of 526 * floating point values representing either the 4 non-translation 527 * entries or the 6 specifiable entries of the 3x3 transformation 528 * matrix. The values are retrieved from the array as 529 * { m00 m10 m01 m11 [m02 m12]}. 530 * @param flatmatrix the float array containing the values to be set 531 * in the new {@code AffineTransform} object. The length of the 532 * array is assumed to be at least 4. If the length of the array is 533 * less than 6, only the first 4 values are taken. If the length of 534 * the array is greater than 6, the first 6 values are taken. 535 * @since 1.2 536 */ 537 public AffineTransform(float[] flatmatrix) { 538 m00 = flatmatrix[0]; 539 m10 = flatmatrix[1]; 540 m01 = flatmatrix[2]; 541 m11 = flatmatrix[3]; 542 if (flatmatrix.length > 5) { 543 m02 = flatmatrix[4]; 544 m12 = flatmatrix[5]; 545 } 546 updateState(); 547 } 548 549 /** 550 * Constructs a new {@code AffineTransform} from 6 double 551 * precision values representing the 6 specifiable entries of the 3x3 552 * transformation matrix. 553 * 554 * @param m00 the X coordinate scaling element of the 3x3 matrix 555 * @param m10 the Y coordinate shearing element of the 3x3 matrix 556 * @param m01 the X coordinate shearing element of the 3x3 matrix 557 * @param m11 the Y coordinate scaling element of the 3x3 matrix 558 * @param m02 the X coordinate translation element of the 3x3 matrix 559 * @param m12 the Y coordinate translation element of the 3x3 matrix 560 * @since 1.2 561 */ 562 public AffineTransform(double m00, double m10, 563 double m01, double m11, 564 double m02, double m12) { 565 this.m00 = m00; 566 this.m10 = m10; 567 this.m01 = m01; 568 this.m11 = m11; 569 this.m02 = m02; 570 this.m12 = m12; 571 updateState(); 572 } 573 574 /** 575 * Constructs a new {@code AffineTransform} from an array of 576 * double precision values representing either the 4 non-translation 577 * entries or the 6 specifiable entries of the 3x3 transformation 578 * matrix. The values are retrieved from the array as 579 * { m00 m10 m01 m11 [m02 m12]}. 580 * @param flatmatrix the double array containing the values to be set 581 * in the new {@code AffineTransform} object. The length of the 582 * array is assumed to be at least 4. If the length of the array is 583 * less than 6, only the first 4 values are taken. If the length of 584 * the array is greater than 6, the first 6 values are taken. 585 * @since 1.2 586 */ 587 public AffineTransform(double[] flatmatrix) { 588 m00 = flatmatrix[0]; 589 m10 = flatmatrix[1]; 590 m01 = flatmatrix[2]; 591 m11 = flatmatrix[3]; 592 if (flatmatrix.length > 5) { 593 m02 = flatmatrix[4]; 594 m12 = flatmatrix[5]; 595 } 596 updateState(); 597 } 598 599 /** 600 * Returns a transform representing a translation transformation. 601 * The matrix representing the returned transform is: 602 * <pre> 603 * [ 1 0 tx ] 604 * [ 0 1 ty ] 605 * [ 0 0 1 ] 606 * </pre> 607 * @param tx the distance by which coordinates are translated in the 608 * X axis direction 609 * @param ty the distance by which coordinates are translated in the 610 * Y axis direction 611 * @return an {@code AffineTransform} object that represents a 612 * translation transformation, created with the specified vector. 613 * @since 1.2 614 */ 615 public static AffineTransform getTranslateInstance(double tx, double ty) { 616 AffineTransform Tx = new AffineTransform(); 617 Tx.setToTranslation(tx, ty); 618 return Tx; 619 } 620 621 /** 622 * Returns a transform representing a rotation transformation. 623 * The matrix representing the returned transform is: 624 * <pre> 625 * [ cos(theta) -sin(theta) 0 ] 626 * [ sin(theta) cos(theta) 0 ] 627 * [ 0 0 1 ] 628 * </pre> 629 * Rotating by a positive angle theta rotates points on the positive 630 * X axis toward the positive Y axis. 631 * Note also the discussion of 632 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 633 * above. 634 * @param theta the angle of rotation measured in radians 635 * @return an {@code AffineTransform} object that is a rotation 636 * transformation, created with the specified angle of rotation. 637 * @since 1.2 638 */ 639 public static AffineTransform getRotateInstance(double theta) { 640 AffineTransform Tx = new AffineTransform(); 641 Tx.setToRotation(theta); 642 return Tx; 643 } 644 645 /** 646 * Returns a transform that rotates coordinates around an anchor point. 647 * This operation is equivalent to translating the coordinates so 648 * that the anchor point is at the origin (S1), then rotating them 649 * about the new origin (S2), and finally translating so that the 650 * intermediate origin is restored to the coordinates of the original 651 * anchor point (S3). 652 * <p> 653 * This operation is equivalent to the following sequence of calls: 654 * <pre> 655 * AffineTransform Tx = new AffineTransform(); 656 * Tx.translate(anchorx, anchory); // S3: final translation 657 * Tx.rotate(theta); // S2: rotate around anchor 658 * Tx.translate(-anchorx, -anchory); // S1: translate anchor to origin 659 * </pre> 660 * The matrix representing the returned transform is: 661 * <pre> 662 * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 663 * [ sin(theta) cos(theta) y-x*sin-y*cos ] 664 * [ 0 0 1 ] 665 * </pre> 666 * Rotating by a positive angle theta rotates points on the positive 667 * X axis toward the positive Y axis. 668 * Note also the discussion of 669 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 670 * above. 671 * 672 * @param theta the angle of rotation measured in radians 673 * @param anchorx the X coordinate of the rotation anchor point 674 * @param anchory the Y coordinate of the rotation anchor point 675 * @return an {@code AffineTransform} object that rotates 676 * coordinates around the specified point by the specified angle of 677 * rotation. 678 * @since 1.2 679 */ 680 public static AffineTransform getRotateInstance(double theta, 681 double anchorx, 682 double anchory) 683 { 684 AffineTransform Tx = new AffineTransform(); 685 Tx.setToRotation(theta, anchorx, anchory); 686 return Tx; 687 } 688 689 /** 690 * Returns a transform that rotates coordinates according to 691 * a rotation vector. 692 * All coordinates rotate about the origin by the same amount. 693 * The amount of rotation is such that coordinates along the former 694 * positive X axis will subsequently align with the vector pointing 695 * from the origin to the specified vector coordinates. 696 * If both {@code vecx} and {@code vecy} are 0.0, 697 * an identity transform is returned. 698 * This operation is equivalent to calling: 699 * <pre> 700 * AffineTransform.getRotateInstance(Math.atan2(vecy, vecx)); 701 * </pre> 702 * 703 * @param vecx the X coordinate of the rotation vector 704 * @param vecy the Y coordinate of the rotation vector 705 * @return an {@code AffineTransform} object that rotates 706 * coordinates according to the specified rotation vector. 707 * @since 1.6 708 */ 709 public static AffineTransform getRotateInstance(double vecx, double vecy) { 710 AffineTransform Tx = new AffineTransform(); 711 Tx.setToRotation(vecx, vecy); 712 return Tx; 713 } 714 715 /** 716 * Returns a transform that rotates coordinates around an anchor 717 * point according to a rotation vector. 718 * All coordinates rotate about the specified anchor coordinates 719 * by the same amount. 720 * The amount of rotation is such that coordinates along the former 721 * positive X axis will subsequently align with the vector pointing 722 * from the origin to the specified vector coordinates. 723 * If both {@code vecx} and {@code vecy} are 0.0, 724 * an identity transform is returned. 725 * This operation is equivalent to calling: 726 * <pre> 727 * AffineTransform.getRotateInstance(Math.atan2(vecy, vecx), 728 * anchorx, anchory); 729 * </pre> 730 * 731 * @param vecx the X coordinate of the rotation vector 732 * @param vecy the Y coordinate of the rotation vector 733 * @param anchorx the X coordinate of the rotation anchor point 734 * @param anchory the Y coordinate of the rotation anchor point 735 * @return an {@code AffineTransform} object that rotates 736 * coordinates around the specified point according to the 737 * specified rotation vector. 738 * @since 1.6 739 */ 740 public static AffineTransform getRotateInstance(double vecx, 741 double vecy, 742 double anchorx, 743 double anchory) 744 { 745 AffineTransform Tx = new AffineTransform(); 746 Tx.setToRotation(vecx, vecy, anchorx, anchory); 747 return Tx; 748 } 749 750 /** 751 * Returns a transform that rotates coordinates by the specified 752 * number of quadrants. 753 * This operation is equivalent to calling: 754 * <pre> 755 * AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0); 756 * </pre> 757 * Rotating by a positive number of quadrants rotates points on 758 * the positive X axis toward the positive Y axis. 759 * @param numquadrants the number of 90 degree arcs to rotate by 760 * @return an {@code AffineTransform} object that rotates 761 * coordinates by the specified number of quadrants. 762 * @since 1.6 763 */ 764 public static AffineTransform getQuadrantRotateInstance(int numquadrants) { 765 AffineTransform Tx = new AffineTransform(); 766 Tx.setToQuadrantRotation(numquadrants); 767 return Tx; 768 } 769 770 /** 771 * Returns a transform that rotates coordinates by the specified 772 * number of quadrants around the specified anchor point. 773 * This operation is equivalent to calling: 774 * <pre> 775 * AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0, 776 * anchorx, anchory); 777 * </pre> 778 * Rotating by a positive number of quadrants rotates points on 779 * the positive X axis toward the positive Y axis. 780 * 781 * @param numquadrants the number of 90 degree arcs to rotate by 782 * @param anchorx the X coordinate of the rotation anchor point 783 * @param anchory the Y coordinate of the rotation anchor point 784 * @return an {@code AffineTransform} object that rotates 785 * coordinates by the specified number of quadrants around the 786 * specified anchor point. 787 * @since 1.6 788 */ 789 public static AffineTransform getQuadrantRotateInstance(int numquadrants, 790 double anchorx, 791 double anchory) 792 { 793 AffineTransform Tx = new AffineTransform(); 794 Tx.setToQuadrantRotation(numquadrants, anchorx, anchory); 795 return Tx; 796 } 797 798 /** 799 * Returns a transform representing a scaling transformation. 800 * The matrix representing the returned transform is: 801 * <pre> 802 * [ sx 0 0 ] 803 * [ 0 sy 0 ] 804 * [ 0 0 1 ] 805 * </pre> 806 * @param sx the factor by which coordinates are scaled along the 807 * X axis direction 808 * @param sy the factor by which coordinates are scaled along the 809 * Y axis direction 810 * @return an {@code AffineTransform} object that scales 811 * coordinates by the specified factors. 812 * @since 1.2 813 */ 814 public static AffineTransform getScaleInstance(double sx, double sy) { 815 AffineTransform Tx = new AffineTransform(); 816 Tx.setToScale(sx, sy); 817 return Tx; 818 } 819 820 /** 821 * Returns a transform representing a shearing transformation. 822 * The matrix representing the returned transform is: 823 * <pre> 824 * [ 1 shx 0 ] 825 * [ shy 1 0 ] 826 * [ 0 0 1 ] 827 * </pre> 828 * @param shx the multiplier by which coordinates are shifted in the 829 * direction of the positive X axis as a factor of their Y coordinate 830 * @param shy the multiplier by which coordinates are shifted in the 831 * direction of the positive Y axis as a factor of their X coordinate 832 * @return an {@code AffineTransform} object that shears 833 * coordinates by the specified multipliers. 834 * @since 1.2 835 */ 836 public static AffineTransform getShearInstance(double shx, double shy) { 837 AffineTransform Tx = new AffineTransform(); 838 Tx.setToShear(shx, shy); 839 return Tx; 840 } 841 842 /** 843 * Retrieves the flag bits describing the conversion properties of 844 * this transform. 845 * The return value is either one of the constants TYPE_IDENTITY 846 * or TYPE_GENERAL_TRANSFORM, or a combination of the 847 * appropriate flag bits. 848 * A valid combination of flag bits is an exclusive OR operation 849 * that can combine 850 * the TYPE_TRANSLATION flag bit 851 * in addition to either of the 852 * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits 853 * as well as either of the 854 * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits. 855 * @return the OR combination of any of the indicated flags that 856 * apply to this transform 857 * @see #TYPE_IDENTITY 858 * @see #TYPE_TRANSLATION 859 * @see #TYPE_UNIFORM_SCALE 860 * @see #TYPE_GENERAL_SCALE 861 * @see #TYPE_QUADRANT_ROTATION 862 * @see #TYPE_GENERAL_ROTATION 863 * @see #TYPE_GENERAL_TRANSFORM 864 * @since 1.2 865 */ 866 public int getType() { 867 if (type == TYPE_UNKNOWN) { 868 calculateType(); 869 } 870 return type; 871 } 872 873 /** 874 * This is the utility function to calculate the flag bits when 875 * they have not been cached. 876 * @see #getType 877 */ 878 @SuppressWarnings("fallthrough") 879 private void calculateType() { 880 int ret = TYPE_IDENTITY; 881 boolean sgn0, sgn1; 882 double M0, M1, M2, M3; 883 updateState(); 884 switch (state) { 885 default: 886 stateError(); 887 /* NOTREACHED */ 888 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 889 ret = TYPE_TRANSLATION; 890 /* NOBREAK */ 891 case (APPLY_SHEAR | APPLY_SCALE): 892 if ((M0 = m00) * (M2 = m01) + (M3 = m10) * (M1 = m11) != 0) { 893 // Transformed unit vectors are not perpendicular... 894 this.type = TYPE_GENERAL_TRANSFORM; 895 return; 896 } 897 sgn0 = (M0 >= 0.0); 898 sgn1 = (M1 >= 0.0); 899 if (sgn0 == sgn1) { 900 // sgn(M0) == sgn(M1) therefore sgn(M2) == -sgn(M3) 901 // This is the "unflipped" (right-handed) state 902 if (M0 != M1 || M2 != -M3) { 903 ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE); 904 } else if (M0 * M1 - M2 * M3 != 1.0) { 905 ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE); 906 } else { 907 ret |= TYPE_GENERAL_ROTATION; 908 } 909 } else { 910 // sgn(M0) == -sgn(M1) therefore sgn(M2) == sgn(M3) 911 // This is the "flipped" (left-handed) state 912 if (M0 != -M1 || M2 != M3) { 913 ret |= (TYPE_GENERAL_ROTATION | 914 TYPE_FLIP | 915 TYPE_GENERAL_SCALE); 916 } else if (M0 * M1 - M2 * M3 != 1.0) { 917 ret |= (TYPE_GENERAL_ROTATION | 918 TYPE_FLIP | 919 TYPE_UNIFORM_SCALE); 920 } else { 921 ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP); 922 } 923 } 924 break; 925 case (APPLY_SHEAR | APPLY_TRANSLATE): 926 ret = TYPE_TRANSLATION; 927 /* NOBREAK */ 928 case (APPLY_SHEAR): 929 sgn0 = ((M0 = m01) >= 0.0); 930 sgn1 = ((M1 = m10) >= 0.0); 931 if (sgn0 != sgn1) { 932 // Different signs - simple 90 degree rotation 933 if (M0 != -M1) { 934 ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); 935 } else if (M0 != 1.0 && M0 != -1.0) { 936 ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); 937 } else { 938 ret |= TYPE_QUADRANT_ROTATION; 939 } 940 } else { 941 // Same signs - 90 degree rotation plus an axis flip too 942 if (M0 == M1) { 943 ret |= (TYPE_QUADRANT_ROTATION | 944 TYPE_FLIP | 945 TYPE_UNIFORM_SCALE); 946 } else { 947 ret |= (TYPE_QUADRANT_ROTATION | 948 TYPE_FLIP | 949 TYPE_GENERAL_SCALE); 950 } 951 } 952 break; 953 case (APPLY_SCALE | APPLY_TRANSLATE): 954 ret = TYPE_TRANSLATION; 955 /* NOBREAK */ 956 case (APPLY_SCALE): 957 sgn0 = ((M0 = m00) >= 0.0); 958 sgn1 = ((M1 = m11) >= 0.0); 959 if (sgn0 == sgn1) { 960 if (sgn0) { 961 // Both scaling factors non-negative - simple scale 962 // Note: APPLY_SCALE implies M0, M1 are not both 1 963 if (M0 == M1) { 964 ret |= TYPE_UNIFORM_SCALE; 965 } else { 966 ret |= TYPE_GENERAL_SCALE; 967 } 968 } else { 969 // Both scaling factors negative - 180 degree rotation 970 if (M0 != M1) { 971 ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); 972 } else if (M0 != -1.0) { 973 ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); 974 } else { 975 ret |= TYPE_QUADRANT_ROTATION; 976 } 977 } 978 } else { 979 // Scaling factor signs different - flip about some axis 980 if (M0 == -M1) { 981 if (M0 == 1.0 || M0 == -1.0) { 982 ret |= TYPE_FLIP; 983 } else { 984 ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE); 985 } 986 } else { 987 ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE); 988 } 989 } 990 break; 991 case (APPLY_TRANSLATE): 992 ret = TYPE_TRANSLATION; 993 break; 994 case (APPLY_IDENTITY): 995 break; 996 } 997 this.type = ret; 998 } 999 1000 /** 1001 * Returns the determinant of the matrix representation of the transform. 1002 * The determinant is useful both to determine if the transform can 1003 * be inverted and to get a single value representing the 1004 * combined X and Y scaling of the transform. 1005 * <p> 1006 * If the determinant is non-zero, then this transform is 1007 * invertible and the various methods that depend on the inverse 1008 * transform do not need to throw a 1009 * {@link NoninvertibleTransformException}. 1010 * If the determinant is zero then this transform can not be 1011 * inverted since the transform maps all input coordinates onto 1012 * a line or a point. 1013 * If the determinant is near enough to zero then inverse transform 1014 * operations might not carry enough precision to produce meaningful 1015 * results. 1016 * <p> 1017 * If this transform represents a uniform scale, as indicated by 1018 * the {@code getType} method then the determinant also 1019 * represents the square of the uniform scale factor by which all of 1020 * the points are expanded from or contracted towards the origin. 1021 * If this transform represents a non-uniform scale or more general 1022 * transform then the determinant is not likely to represent a 1023 * value useful for any purpose other than determining if inverse 1024 * transforms are possible. 1025 * <p> 1026 * Mathematically, the determinant is calculated using the formula: 1027 * <pre> 1028 * | m00 m01 m02 | 1029 * | m10 m11 m12 | = m00 * m11 - m01 * m10 1030 * | 0 0 1 | 1031 * </pre> 1032 * 1033 * @return the determinant of the matrix used to transform the 1034 * coordinates. 1035 * @see #getType 1036 * @see #createInverse 1037 * @see #inverseTransform 1038 * @see #TYPE_UNIFORM_SCALE 1039 * @since 1.2 1040 */ 1041 @SuppressWarnings("fallthrough") 1042 public double getDeterminant() { 1043 switch (state) { 1044 default: 1045 stateError(); 1046 /* NOTREACHED */ 1047 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 1048 case (APPLY_SHEAR | APPLY_SCALE): 1049 return m00 * m11 - m01 * m10; 1050 case (APPLY_SHEAR | APPLY_TRANSLATE): 1051 case (APPLY_SHEAR): 1052 return -(m01 * m10); 1053 case (APPLY_SCALE | APPLY_TRANSLATE): 1054 case (APPLY_SCALE): 1055 return m00 * m11; 1056 case (APPLY_TRANSLATE): 1057 case (APPLY_IDENTITY): 1058 return 1.0; 1059 } 1060 } 1061 1062 /** 1063 * Manually recalculates the state of the transform when the matrix 1064 * changes too much to predict the effects on the state. 1065 * The following table specifies what the various settings of the 1066 * state field say about the values of the corresponding matrix 1067 * element fields. 1068 * Note that the rules governing the SCALE fields are slightly 1069 * different depending on whether the SHEAR flag is also set. 1070 * <pre> 1071 * SCALE SHEAR TRANSLATE 1072 * m00/m11 m01/m10 m02/m12 1073 * 1074 * IDENTITY 1.0 0.0 0.0 1075 * TRANSLATE (TR) 1.0 0.0 not both 0.0 1076 * SCALE (SC) not both 1.0 0.0 0.0 1077 * TR | SC not both 1.0 0.0 not both 0.0 1078 * SHEAR (SH) 0.0 not both 0.0 0.0 1079 * TR | SH 0.0 not both 0.0 not both 0.0 1080 * SC | SH not both 0.0 not both 0.0 0.0 1081 * TR | SC | SH not both 0.0 not both 0.0 not both 0.0 1082 * </pre> 1083 */ 1084 void updateState() { 1085 if (m01 == 0.0 && m10 == 0.0) { 1086 if (m00 == 1.0 && m11 == 1.0) { 1087 if (m02 == 0.0 && m12 == 0.0) { 1088 state = APPLY_IDENTITY; 1089 type = TYPE_IDENTITY; 1090 } else { 1091 state = APPLY_TRANSLATE; 1092 type = TYPE_TRANSLATION; 1093 } 1094 } else { 1095 if (m02 == 0.0 && m12 == 0.0) { 1096 state = APPLY_SCALE; 1097 type = TYPE_UNKNOWN; 1098 } else { 1099 state = (APPLY_SCALE | APPLY_TRANSLATE); 1100 type = TYPE_UNKNOWN; 1101 } 1102 } 1103 } else { 1104 if (m00 == 0.0 && m11 == 0.0) { 1105 if (m02 == 0.0 && m12 == 0.0) { 1106 state = APPLY_SHEAR; 1107 type = TYPE_UNKNOWN; 1108 } else { 1109 state = (APPLY_SHEAR | APPLY_TRANSLATE); 1110 type = TYPE_UNKNOWN; 1111 } 1112 } else { 1113 if (m02 == 0.0 && m12 == 0.0) { 1114 state = (APPLY_SHEAR | APPLY_SCALE); 1115 type = TYPE_UNKNOWN; 1116 } else { 1117 state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE); 1118 type = TYPE_UNKNOWN; 1119 } 1120 } 1121 } 1122 } 1123 1124 /* 1125 * Convenience method used internally to throw exceptions when 1126 * a case was forgotten in a switch statement. 1127 */ 1128 private void stateError() { 1129 throw new InternalError("missing case in transform state switch"); 1130 } 1131 1132 /** 1133 * Retrieves the 6 specifiable values in the 3x3 affine transformation 1134 * matrix and places them into an array of double precisions values. 1135 * The values are stored in the array as 1136 * { m00 m10 m01 m11 m02 m12 }. 1137 * An array of 4 doubles can also be specified, in which case only the 1138 * first four elements representing the non-transform 1139 * parts of the array are retrieved and the values are stored into 1140 * the array as { m00 m10 m01 m11 } 1141 * @param flatmatrix the double array used to store the returned 1142 * values. 1143 * @see #getScaleX 1144 * @see #getScaleY 1145 * @see #getShearX 1146 * @see #getShearY 1147 * @see #getTranslateX 1148 * @see #getTranslateY 1149 * @since 1.2 1150 */ 1151 public void getMatrix(double[] flatmatrix) { 1152 flatmatrix[0] = m00; 1153 flatmatrix[1] = m10; 1154 flatmatrix[2] = m01; 1155 flatmatrix[3] = m11; 1156 if (flatmatrix.length > 5) { 1157 flatmatrix[4] = m02; 1158 flatmatrix[5] = m12; 1159 } 1160 } 1161 1162 /** 1163 * Returns the X coordinate scaling element (m00) of the 3x3 1164 * affine transformation matrix. However, this return value cannot be used 1165 * to determine the amount by which X-coordinate is stretched or contracted. 1166 * @return a double value that is the X coordinate of the scaling 1167 * element of the affine transformation matrix. 1168 * @see #getMatrix 1169 * @since 1.2 1170 */ 1171 public double getScaleX() { 1172 return m00; 1173 } 1174 1175 /** 1176 * Returns the Y coordinate scaling element (m11) of the 3x3 1177 * affine transformation matrix. However, this return value cannot be used 1178 * to determine the amount by which Y-coordinate is stretched or contracted. 1179 * @return a double value that is the Y coordinate of the scaling 1180 * element of the affine transformation matrix. 1181 * @see #getMatrix 1182 * @since 1.2 1183 */ 1184 public double getScaleY() { 1185 return m11; 1186 } 1187 1188 /** 1189 * Returns the X coordinate shearing element (m01) of the 3x3 1190 * affine transformation matrix. 1191 * @return a double value that is the X coordinate of the shearing 1192 * element of the affine transformation matrix. 1193 * @see #getMatrix 1194 * @since 1.2 1195 */ 1196 public double getShearX() { 1197 return m01; 1198 } 1199 1200 /** 1201 * Returns the Y coordinate shearing element (m10) of the 3x3 1202 * affine transformation matrix. 1203 * @return a double value that is the Y coordinate of the shearing 1204 * element of the affine transformation matrix. 1205 * @see #getMatrix 1206 * @since 1.2 1207 */ 1208 public double getShearY() { 1209 return m10; 1210 } 1211 1212 /** 1213 * Returns the X coordinate of the translation element (m02) of the 1214 * 3x3 affine transformation matrix. 1215 * @return a double value that is the X coordinate of the translation 1216 * element of the affine transformation matrix. 1217 * @see #getMatrix 1218 * @since 1.2 1219 */ 1220 public double getTranslateX() { 1221 return m02; 1222 } 1223 1224 /** 1225 * Returns the Y coordinate of the translation element (m12) of the 1226 * 3x3 affine transformation matrix. 1227 * @return a double value that is the Y coordinate of the translation 1228 * element of the affine transformation matrix. 1229 * @see #getMatrix 1230 * @since 1.2 1231 */ 1232 public double getTranslateY() { 1233 return m12; 1234 } 1235 1236 /** 1237 * Concatenates this transform with a translation transformation. 1238 * This is equivalent to calling concatenate(T), where T is an 1239 * {@code AffineTransform} represented by the following matrix: 1240 * <pre> 1241 * [ 1 0 tx ] 1242 * [ 0 1 ty ] 1243 * [ 0 0 1 ] 1244 * </pre> 1245 * @param tx the distance by which coordinates are translated in the 1246 * X axis direction 1247 * @param ty the distance by which coordinates are translated in the 1248 * Y axis direction 1249 * @since 1.2 1250 */ 1251 public void translate(double tx, double ty) { 1252 switch (state) { 1253 default: 1254 stateError(); 1255 /* NOTREACHED */ 1256 return; 1257 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 1258 m02 = tx * m00 + ty * m01 + m02; 1259 m12 = tx * m10 + ty * m11 + m12; 1260 if (m02 == 0.0 && m12 == 0.0) { 1261 state = APPLY_SHEAR | APPLY_SCALE; 1262 if (type != TYPE_UNKNOWN) { 1263 type -= TYPE_TRANSLATION; 1264 } 1265 } 1266 return; 1267 case (APPLY_SHEAR | APPLY_SCALE): 1268 m02 = tx * m00 + ty * m01; 1269 m12 = tx * m10 + ty * m11; 1270 if (m02 != 0.0 || m12 != 0.0) { 1271 state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE; 1272 type |= TYPE_TRANSLATION; 1273 } 1274 return; 1275 case (APPLY_SHEAR | APPLY_TRANSLATE): 1276 m02 = ty * m01 + m02; 1277 m12 = tx * m10 + m12; 1278 if (m02 == 0.0 && m12 == 0.0) { 1279 state = APPLY_SHEAR; 1280 if (type != TYPE_UNKNOWN) { 1281 type -= TYPE_TRANSLATION; 1282 } 1283 } 1284 return; 1285 case (APPLY_SHEAR): 1286 m02 = ty * m01; 1287 m12 = tx * m10; 1288 if (m02 != 0.0 || m12 != 0.0) { 1289 state = APPLY_SHEAR | APPLY_TRANSLATE; 1290 type |= TYPE_TRANSLATION; 1291 } 1292 return; 1293 case (APPLY_SCALE | APPLY_TRANSLATE): 1294 m02 = tx * m00 + m02; 1295 m12 = ty * m11 + m12; 1296 if (m02 == 0.0 && m12 == 0.0) { 1297 state = APPLY_SCALE; 1298 if (type != TYPE_UNKNOWN) { 1299 type -= TYPE_TRANSLATION; 1300 } 1301 } 1302 return; 1303 case (APPLY_SCALE): 1304 m02 = tx * m00; 1305 m12 = ty * m11; 1306 if (m02 != 0.0 || m12 != 0.0) { 1307 state = APPLY_SCALE | APPLY_TRANSLATE; 1308 type |= TYPE_TRANSLATION; 1309 } 1310 return; 1311 case (APPLY_TRANSLATE): 1312 m02 = tx + m02; 1313 m12 = ty + m12; 1314 if (m02 == 0.0 && m12 == 0.0) { 1315 state = APPLY_IDENTITY; 1316 type = TYPE_IDENTITY; 1317 } 1318 return; 1319 case (APPLY_IDENTITY): 1320 m02 = tx; 1321 m12 = ty; 1322 if (tx != 0.0 || ty != 0.0) { 1323 state = APPLY_TRANSLATE; 1324 type = TYPE_TRANSLATION; 1325 } 1326 return; 1327 } 1328 } 1329 1330 // Utility methods to optimize rotate methods. 1331 // These tables translate the flags during predictable quadrant 1332 // rotations where the shear and scale values are swapped and negated. 1333 private static final int rot90conversion[] = { 1334 /* IDENTITY => */ APPLY_SHEAR, 1335 /* TRANSLATE (TR) => */ APPLY_SHEAR | APPLY_TRANSLATE, 1336 /* SCALE (SC) => */ APPLY_SHEAR, 1337 /* SC | TR => */ APPLY_SHEAR | APPLY_TRANSLATE, 1338 /* SHEAR (SH) => */ APPLY_SCALE, 1339 /* SH | TR => */ APPLY_SCALE | APPLY_TRANSLATE, 1340 /* SH | SC => */ APPLY_SHEAR | APPLY_SCALE, 1341 /* SH | SC | TR => */ APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE, 1342 }; 1343 private final void rotate90() { 1344 double M0 = m00; 1345 m00 = m01; 1346 m01 = -M0; 1347 M0 = m10; 1348 m10 = m11; 1349 m11 = -M0; 1350 int state = rot90conversion[this.state]; 1351 if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && 1352 m00 == 1.0 && m11 == 1.0) 1353 { 1354 state -= APPLY_SCALE; 1355 } 1356 this.state = state; 1357 type = TYPE_UNKNOWN; 1358 } 1359 private final void rotate180() { 1360 m00 = -m00; 1361 m11 = -m11; 1362 int state = this.state; 1363 if ((state & (APPLY_SHEAR)) != 0) { 1364 // If there was a shear, then this rotation has no 1365 // effect on the state. 1366 m01 = -m01; 1367 m10 = -m10; 1368 } else { 1369 // No shear means the SCALE state may toggle when 1370 // m00 and m11 are negated. 1371 if (m00 == 1.0 && m11 == 1.0) { 1372 this.state = state & ~APPLY_SCALE; 1373 } else { 1374 this.state = state | APPLY_SCALE; 1375 } 1376 } 1377 type = TYPE_UNKNOWN; 1378 } 1379 private final void rotate270() { 1380 double M0 = m00; 1381 m00 = -m01; 1382 m01 = M0; 1383 M0 = m10; 1384 m10 = -m11; 1385 m11 = M0; 1386 int state = rot90conversion[this.state]; 1387 if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && 1388 m00 == 1.0 && m11 == 1.0) 1389 { 1390 state -= APPLY_SCALE; 1391 } 1392 this.state = state; 1393 type = TYPE_UNKNOWN; 1394 } 1395 1396 /** 1397 * Concatenates this transform with a rotation transformation. 1398 * This is equivalent to calling concatenate(R), where R is an 1399 * {@code AffineTransform} represented by the following matrix: 1400 * <pre> 1401 * [ cos(theta) -sin(theta) 0 ] 1402 * [ sin(theta) cos(theta) 0 ] 1403 * [ 0 0 1 ] 1404 * </pre> 1405 * Rotating by a positive angle theta rotates points on the positive 1406 * X axis toward the positive Y axis. 1407 * Note also the discussion of 1408 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 1409 * above. 1410 * @param theta the angle of rotation measured in radians 1411 * @since 1.2 1412 */ 1413 public void rotate(double theta) { 1414 double sin = Math.sin(theta); 1415 if (sin == 1.0) { 1416 rotate90(); 1417 } else if (sin == -1.0) { 1418 rotate270(); 1419 } else { 1420 double cos = Math.cos(theta); 1421 if (cos == -1.0) { 1422 rotate180(); 1423 } else if (cos != 1.0) { 1424 double M0, M1; 1425 M0 = m00; 1426 M1 = m01; 1427 m00 = cos * M0 + sin * M1; 1428 m01 = -sin * M0 + cos * M1; 1429 M0 = m10; 1430 M1 = m11; 1431 m10 = cos * M0 + sin * M1; 1432 m11 = -sin * M0 + cos * M1; 1433 updateState(); 1434 } 1435 } 1436 } 1437 1438 /** 1439 * Concatenates this transform with a transform that rotates 1440 * coordinates around an anchor point. 1441 * This operation is equivalent to translating the coordinates so 1442 * that the anchor point is at the origin (S1), then rotating them 1443 * about the new origin (S2), and finally translating so that the 1444 * intermediate origin is restored to the coordinates of the original 1445 * anchor point (S3). 1446 * <p> 1447 * This operation is equivalent to the following sequence of calls: 1448 * <pre> 1449 * translate(anchorx, anchory); // S3: final translation 1450 * rotate(theta); // S2: rotate around anchor 1451 * translate(-anchorx, -anchory); // S1: translate anchor to origin 1452 * </pre> 1453 * Rotating by a positive angle theta rotates points on the positive 1454 * X axis toward the positive Y axis. 1455 * Note also the discussion of 1456 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 1457 * above. 1458 * 1459 * @param theta the angle of rotation measured in radians 1460 * @param anchorx the X coordinate of the rotation anchor point 1461 * @param anchory the Y coordinate of the rotation anchor point 1462 * @since 1.2 1463 */ 1464 public void rotate(double theta, double anchorx, double anchory) { 1465 // REMIND: Simple for now - optimize later 1466 translate(anchorx, anchory); 1467 rotate(theta); 1468 translate(-anchorx, -anchory); 1469 } 1470 1471 /** 1472 * Concatenates this transform with a transform that rotates 1473 * coordinates according to a rotation vector. 1474 * All coordinates rotate about the origin by the same amount. 1475 * The amount of rotation is such that coordinates along the former 1476 * positive X axis will subsequently align with the vector pointing 1477 * from the origin to the specified vector coordinates. 1478 * If both {@code vecx} and {@code vecy} are 0.0, 1479 * no additional rotation is added to this transform. 1480 * This operation is equivalent to calling: 1481 * <pre> 1482 * rotate(Math.atan2(vecy, vecx)); 1483 * </pre> 1484 * 1485 * @param vecx the X coordinate of the rotation vector 1486 * @param vecy the Y coordinate of the rotation vector 1487 * @since 1.6 1488 */ 1489 public void rotate(double vecx, double vecy) { 1490 if (vecy == 0.0) { 1491 if (vecx < 0.0) { 1492 rotate180(); 1493 } 1494 // If vecx > 0.0 - no rotation 1495 // If vecx == 0.0 - undefined rotation - treat as no rotation 1496 } else if (vecx == 0.0) { 1497 if (vecy > 0.0) { 1498 rotate90(); 1499 } else { // vecy must be < 0.0 1500 rotate270(); 1501 } 1502 } else { 1503 double len = Math.sqrt(vecx * vecx + vecy * vecy); 1504 double sin = vecy / len; 1505 double cos = vecx / len; 1506 double M0, M1; 1507 M0 = m00; 1508 M1 = m01; 1509 m00 = cos * M0 + sin * M1; 1510 m01 = -sin * M0 + cos * M1; 1511 M0 = m10; 1512 M1 = m11; 1513 m10 = cos * M0 + sin * M1; 1514 m11 = -sin * M0 + cos * M1; 1515 updateState(); 1516 } 1517 } 1518 1519 /** 1520 * Concatenates this transform with a transform that rotates 1521 * coordinates around an anchor point according to a rotation 1522 * vector. 1523 * All coordinates rotate about the specified anchor coordinates 1524 * by the same amount. 1525 * The amount of rotation is such that coordinates along the former 1526 * positive X axis will subsequently align with the vector pointing 1527 * from the origin to the specified vector coordinates. 1528 * If both {@code vecx} and {@code vecy} are 0.0, 1529 * the transform is not modified in any way. 1530 * This method is equivalent to calling: 1531 * <pre> 1532 * rotate(Math.atan2(vecy, vecx), anchorx, anchory); 1533 * </pre> 1534 * 1535 * @param vecx the X coordinate of the rotation vector 1536 * @param vecy the Y coordinate of the rotation vector 1537 * @param anchorx the X coordinate of the rotation anchor point 1538 * @param anchory the Y coordinate of the rotation anchor point 1539 * @since 1.6 1540 */ 1541 public void rotate(double vecx, double vecy, 1542 double anchorx, double anchory) 1543 { 1544 // REMIND: Simple for now - optimize later 1545 translate(anchorx, anchory); 1546 rotate(vecx, vecy); 1547 translate(-anchorx, -anchory); 1548 } 1549 1550 /** 1551 * Concatenates this transform with a transform that rotates 1552 * coordinates by the specified number of quadrants. 1553 * This is equivalent to calling: 1554 * <pre> 1555 * rotate(numquadrants * Math.PI / 2.0); 1556 * </pre> 1557 * Rotating by a positive number of quadrants rotates points on 1558 * the positive X axis toward the positive Y axis. 1559 * @param numquadrants the number of 90 degree arcs to rotate by 1560 * @since 1.6 1561 */ 1562 public void quadrantRotate(int numquadrants) { 1563 switch (numquadrants & 3) { 1564 case 0: 1565 break; 1566 case 1: 1567 rotate90(); 1568 break; 1569 case 2: 1570 rotate180(); 1571 break; 1572 case 3: 1573 rotate270(); 1574 break; 1575 } 1576 } 1577 1578 /** 1579 * Concatenates this transform with a transform that rotates 1580 * coordinates by the specified number of quadrants around 1581 * the specified anchor point. 1582 * This method is equivalent to calling: 1583 * <pre> 1584 * rotate(numquadrants * Math.PI / 2.0, anchorx, anchory); 1585 * </pre> 1586 * Rotating by a positive number of quadrants rotates points on 1587 * the positive X axis toward the positive Y axis. 1588 * 1589 * @param numquadrants the number of 90 degree arcs to rotate by 1590 * @param anchorx the X coordinate of the rotation anchor point 1591 * @param anchory the Y coordinate of the rotation anchor point 1592 * @since 1.6 1593 */ 1594 public void quadrantRotate(int numquadrants, 1595 double anchorx, double anchory) 1596 { 1597 switch (numquadrants & 3) { 1598 case 0: 1599 return; 1600 case 1: 1601 m02 += anchorx * (m00 - m01) + anchory * (m01 + m00); 1602 m12 += anchorx * (m10 - m11) + anchory * (m11 + m10); 1603 rotate90(); 1604 break; 1605 case 2: 1606 m02 += anchorx * (m00 + m00) + anchory * (m01 + m01); 1607 m12 += anchorx * (m10 + m10) + anchory * (m11 + m11); 1608 rotate180(); 1609 break; 1610 case 3: 1611 m02 += anchorx * (m00 + m01) + anchory * (m01 - m00); 1612 m12 += anchorx * (m10 + m11) + anchory * (m11 - m10); 1613 rotate270(); 1614 break; 1615 } 1616 if (m02 == 0.0 && m12 == 0.0) { 1617 state &= ~APPLY_TRANSLATE; 1618 } else { 1619 state |= APPLY_TRANSLATE; 1620 } 1621 } 1622 1623 /** 1624 * Concatenates this transform with a scaling transformation. 1625 * This is equivalent to calling concatenate(S), where S is an 1626 * {@code AffineTransform} represented by the following matrix: 1627 * <pre> 1628 * [ sx 0 0 ] 1629 * [ 0 sy 0 ] 1630 * [ 0 0 1 ] 1631 * </pre> 1632 * @param sx the factor by which coordinates are scaled along the 1633 * X axis direction 1634 * @param sy the factor by which coordinates are scaled along the 1635 * Y axis direction 1636 * @since 1.2 1637 */ 1638 @SuppressWarnings("fallthrough") 1639 public void scale(double sx, double sy) { 1640 int state = this.state; 1641 switch (state) { 1642 default: 1643 stateError(); 1644 /* NOTREACHED */ 1645 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 1646 case (APPLY_SHEAR | APPLY_SCALE): 1647 m00 *= sx; 1648 m11 *= sy; 1649 /* NOBREAK */ 1650 case (APPLY_SHEAR | APPLY_TRANSLATE): 1651 case (APPLY_SHEAR): 1652 m01 *= sy; 1653 m10 *= sx; 1654 if (m01 == 0 && m10 == 0) { 1655 state &= APPLY_TRANSLATE; 1656 if (m00 == 1.0 && m11 == 1.0) { 1657 this.type = (state == APPLY_IDENTITY 1658 ? TYPE_IDENTITY 1659 : TYPE_TRANSLATION); 1660 } else { 1661 state |= APPLY_SCALE; 1662 this.type = TYPE_UNKNOWN; 1663 } 1664 this.state = state; 1665 } 1666 return; 1667 case (APPLY_SCALE | APPLY_TRANSLATE): 1668 case (APPLY_SCALE): 1669 m00 *= sx; 1670 m11 *= sy; 1671 if (m00 == 1.0 && m11 == 1.0) { 1672 this.state = (state &= APPLY_TRANSLATE); 1673 this.type = (state == APPLY_IDENTITY 1674 ? TYPE_IDENTITY 1675 : TYPE_TRANSLATION); 1676 } else { 1677 this.type = TYPE_UNKNOWN; 1678 } 1679 return; 1680 case (APPLY_TRANSLATE): 1681 case (APPLY_IDENTITY): 1682 m00 = sx; 1683 m11 = sy; 1684 if (sx != 1.0 || sy != 1.0) { 1685 this.state = state | APPLY_SCALE; 1686 this.type = TYPE_UNKNOWN; 1687 } 1688 return; 1689 } 1690 } 1691 1692 /** 1693 * Concatenates this transform with a shearing transformation. 1694 * This is equivalent to calling concatenate(SH), where SH is an 1695 * {@code AffineTransform} represented by the following matrix: 1696 * <pre> 1697 * [ 1 shx 0 ] 1698 * [ shy 1 0 ] 1699 * [ 0 0 1 ] 1700 * </pre> 1701 * @param shx the multiplier by which coordinates are shifted in the 1702 * direction of the positive X axis as a factor of their Y coordinate 1703 * @param shy the multiplier by which coordinates are shifted in the 1704 * direction of the positive Y axis as a factor of their X coordinate 1705 * @since 1.2 1706 */ 1707 public void shear(double shx, double shy) { 1708 int state = this.state; 1709 switch (state) { 1710 default: 1711 stateError(); 1712 /* NOTREACHED */ 1713 return; 1714 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 1715 case (APPLY_SHEAR | APPLY_SCALE): 1716 double M0, M1; 1717 M0 = m00; 1718 M1 = m01; 1719 m00 = M0 + M1 * shy; 1720 m01 = M0 * shx + M1; 1721 1722 M0 = m10; 1723 M1 = m11; 1724 m10 = M0 + M1 * shy; 1725 m11 = M0 * shx + M1; 1726 updateState(); 1727 return; 1728 case (APPLY_SHEAR | APPLY_TRANSLATE): 1729 case (APPLY_SHEAR): 1730 m00 = m01 * shy; 1731 m11 = m10 * shx; 1732 if (m00 != 0.0 || m11 != 0.0) { 1733 this.state = state | APPLY_SCALE; 1734 } 1735 this.type = TYPE_UNKNOWN; 1736 return; 1737 case (APPLY_SCALE | APPLY_TRANSLATE): 1738 case (APPLY_SCALE): 1739 m01 = m00 * shx; 1740 m10 = m11 * shy; 1741 if (m01 != 0.0 || m10 != 0.0) { 1742 this.state = state | APPLY_SHEAR; 1743 } 1744 this.type = TYPE_UNKNOWN; 1745 return; 1746 case (APPLY_TRANSLATE): 1747 case (APPLY_IDENTITY): 1748 m01 = shx; 1749 m10 = shy; 1750 if (m01 != 0.0 || m10 != 0.0) { 1751 this.state = state | APPLY_SCALE | APPLY_SHEAR; 1752 this.type = TYPE_UNKNOWN; 1753 } 1754 return; 1755 } 1756 } 1757 1758 /** 1759 * Resets this transform to the Identity transform. 1760 * @since 1.2 1761 */ 1762 public void setToIdentity() { 1763 m00 = m11 = 1.0; 1764 m10 = m01 = m02 = m12 = 0.0; 1765 state = APPLY_IDENTITY; 1766 type = TYPE_IDENTITY; 1767 } 1768 1769 /** 1770 * Sets this transform to a translation transformation. 1771 * The matrix representing this transform becomes: 1772 * <pre> 1773 * [ 1 0 tx ] 1774 * [ 0 1 ty ] 1775 * [ 0 0 1 ] 1776 * </pre> 1777 * @param tx the distance by which coordinates are translated in the 1778 * X axis direction 1779 * @param ty the distance by which coordinates are translated in the 1780 * Y axis direction 1781 * @since 1.2 1782 */ 1783 public void setToTranslation(double tx, double ty) { 1784 m00 = 1.0; 1785 m10 = 0.0; 1786 m01 = 0.0; 1787 m11 = 1.0; 1788 m02 = tx; 1789 m12 = ty; 1790 if (tx != 0.0 || ty != 0.0) { 1791 state = APPLY_TRANSLATE; 1792 type = TYPE_TRANSLATION; 1793 } else { 1794 state = APPLY_IDENTITY; 1795 type = TYPE_IDENTITY; 1796 } 1797 } 1798 1799 /** 1800 * Sets this transform to a rotation transformation. 1801 * The matrix representing this transform becomes: 1802 * <pre> 1803 * [ cos(theta) -sin(theta) 0 ] 1804 * [ sin(theta) cos(theta) 0 ] 1805 * [ 0 0 1 ] 1806 * </pre> 1807 * Rotating by a positive angle theta rotates points on the positive 1808 * X axis toward the positive Y axis. 1809 * Note also the discussion of 1810 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 1811 * above. 1812 * @param theta the angle of rotation measured in radians 1813 * @since 1.2 1814 */ 1815 public void setToRotation(double theta) { 1816 double sin = Math.sin(theta); 1817 double cos; 1818 if (sin == 1.0 || sin == -1.0) { 1819 cos = 0.0; 1820 state = APPLY_SHEAR; 1821 type = TYPE_QUADRANT_ROTATION; 1822 } else { 1823 cos = Math.cos(theta); 1824 if (cos == -1.0) { 1825 sin = 0.0; 1826 state = APPLY_SCALE; 1827 type = TYPE_QUADRANT_ROTATION; 1828 } else if (cos == 1.0) { 1829 sin = 0.0; 1830 state = APPLY_IDENTITY; 1831 type = TYPE_IDENTITY; 1832 } else { 1833 state = APPLY_SHEAR | APPLY_SCALE; 1834 type = TYPE_GENERAL_ROTATION; 1835 } 1836 } 1837 m00 = cos; 1838 m10 = sin; 1839 m01 = -sin; 1840 m11 = cos; 1841 m02 = 0.0; 1842 m12 = 0.0; 1843 } 1844 1845 /** 1846 * Sets this transform to a translated rotation transformation. 1847 * This operation is equivalent to translating the coordinates so 1848 * that the anchor point is at the origin (S1), then rotating them 1849 * about the new origin (S2), and finally translating so that the 1850 * intermediate origin is restored to the coordinates of the original 1851 * anchor point (S3). 1852 * <p> 1853 * This operation is equivalent to the following sequence of calls: 1854 * <pre> 1855 * setToTranslation(anchorx, anchory); // S3: final translation 1856 * rotate(theta); // S2: rotate around anchor 1857 * translate(-anchorx, -anchory); // S1: translate anchor to origin 1858 * </pre> 1859 * The matrix representing this transform becomes: 1860 * <pre> 1861 * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 1862 * [ sin(theta) cos(theta) y-x*sin-y*cos ] 1863 * [ 0 0 1 ] 1864 * </pre> 1865 * Rotating by a positive angle theta rotates points on the positive 1866 * X axis toward the positive Y axis. 1867 * Note also the discussion of 1868 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 1869 * above. 1870 * 1871 * @param theta the angle of rotation measured in radians 1872 * @param anchorx the X coordinate of the rotation anchor point 1873 * @param anchory the Y coordinate of the rotation anchor point 1874 * @since 1.2 1875 */ 1876 public void setToRotation(double theta, double anchorx, double anchory) { 1877 setToRotation(theta); 1878 double sin = m10; 1879 double oneMinusCos = 1.0 - m00; 1880 m02 = anchorx * oneMinusCos + anchory * sin; 1881 m12 = anchory * oneMinusCos - anchorx * sin; 1882 if (m02 != 0.0 || m12 != 0.0) { 1883 state |= APPLY_TRANSLATE; 1884 type |= TYPE_TRANSLATION; 1885 } 1886 } 1887 1888 /** 1889 * Sets this transform to a rotation transformation that rotates 1890 * coordinates according to a rotation vector. 1891 * All coordinates rotate about the origin by the same amount. 1892 * The amount of rotation is such that coordinates along the former 1893 * positive X axis will subsequently align with the vector pointing 1894 * from the origin to the specified vector coordinates. 1895 * If both {@code vecx} and {@code vecy} are 0.0, 1896 * the transform is set to an identity transform. 1897 * This operation is equivalent to calling: 1898 * <pre> 1899 * setToRotation(Math.atan2(vecy, vecx)); 1900 * </pre> 1901 * 1902 * @param vecx the X coordinate of the rotation vector 1903 * @param vecy the Y coordinate of the rotation vector 1904 * @since 1.6 1905 */ 1906 public void setToRotation(double vecx, double vecy) { 1907 double sin, cos; 1908 if (vecy == 0) { 1909 sin = 0.0; 1910 if (vecx < 0.0) { 1911 cos = -1.0; 1912 state = APPLY_SCALE; 1913 type = TYPE_QUADRANT_ROTATION; 1914 } else { 1915 cos = 1.0; 1916 state = APPLY_IDENTITY; 1917 type = TYPE_IDENTITY; 1918 } 1919 } else if (vecx == 0) { 1920 cos = 0.0; 1921 sin = (vecy > 0.0) ? 1.0 : -1.0; 1922 state = APPLY_SHEAR; 1923 type = TYPE_QUADRANT_ROTATION; 1924 } else { 1925 double len = Math.sqrt(vecx * vecx + vecy * vecy); 1926 cos = vecx / len; 1927 sin = vecy / len; 1928 state = APPLY_SHEAR | APPLY_SCALE; 1929 type = TYPE_GENERAL_ROTATION; 1930 } 1931 m00 = cos; 1932 m10 = sin; 1933 m01 = -sin; 1934 m11 = cos; 1935 m02 = 0.0; 1936 m12 = 0.0; 1937 } 1938 1939 /** 1940 * Sets this transform to a rotation transformation that rotates 1941 * coordinates around an anchor point according to a rotation 1942 * vector. 1943 * All coordinates rotate about the specified anchor coordinates 1944 * by the same amount. 1945 * The amount of rotation is such that coordinates along the former 1946 * positive X axis will subsequently align with the vector pointing 1947 * from the origin to the specified vector coordinates. 1948 * If both {@code vecx} and {@code vecy} are 0.0, 1949 * the transform is set to an identity transform. 1950 * This operation is equivalent to calling: 1951 * <pre> 1952 * setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory); 1953 * </pre> 1954 * 1955 * @param vecx the X coordinate of the rotation vector 1956 * @param vecy the Y coordinate of the rotation vector 1957 * @param anchorx the X coordinate of the rotation anchor point 1958 * @param anchory the Y coordinate of the rotation anchor point 1959 * @since 1.6 1960 */ 1961 public void setToRotation(double vecx, double vecy, 1962 double anchorx, double anchory) 1963 { 1964 setToRotation(vecx, vecy); 1965 double sin = m10; 1966 double oneMinusCos = 1.0 - m00; 1967 m02 = anchorx * oneMinusCos + anchory * sin; 1968 m12 = anchory * oneMinusCos - anchorx * sin; 1969 if (m02 != 0.0 || m12 != 0.0) { 1970 state |= APPLY_TRANSLATE; 1971 type |= TYPE_TRANSLATION; 1972 } 1973 } 1974 1975 /** 1976 * Sets this transform to a rotation transformation that rotates 1977 * coordinates by the specified number of quadrants. 1978 * This operation is equivalent to calling: 1979 * <pre> 1980 * setToRotation(numquadrants * Math.PI / 2.0); 1981 * </pre> 1982 * Rotating by a positive number of quadrants rotates points on 1983 * the positive X axis toward the positive Y axis. 1984 * @param numquadrants the number of 90 degree arcs to rotate by 1985 * @since 1.6 1986 */ 1987 public void setToQuadrantRotation(int numquadrants) { 1988 switch (numquadrants & 3) { 1989 case 0: 1990 m00 = 1.0; 1991 m10 = 0.0; 1992 m01 = 0.0; 1993 m11 = 1.0; 1994 m02 = 0.0; 1995 m12 = 0.0; 1996 state = APPLY_IDENTITY; 1997 type = TYPE_IDENTITY; 1998 break; 1999 case 1: 2000 m00 = 0.0; 2001 m10 = 1.0; 2002 m01 = -1.0; 2003 m11 = 0.0; 2004 m02 = 0.0; 2005 m12 = 0.0; 2006 state = APPLY_SHEAR; 2007 type = TYPE_QUADRANT_ROTATION; 2008 break; 2009 case 2: 2010 m00 = -1.0; 2011 m10 = 0.0; 2012 m01 = 0.0; 2013 m11 = -1.0; 2014 m02 = 0.0; 2015 m12 = 0.0; 2016 state = APPLY_SCALE; 2017 type = TYPE_QUADRANT_ROTATION; 2018 break; 2019 case 3: 2020 m00 = 0.0; 2021 m10 = -1.0; 2022 m01 = 1.0; 2023 m11 = 0.0; 2024 m02 = 0.0; 2025 m12 = 0.0; 2026 state = APPLY_SHEAR; 2027 type = TYPE_QUADRANT_ROTATION; 2028 break; 2029 } 2030 } 2031 2032 /** 2033 * Sets this transform to a translated rotation transformation 2034 * that rotates coordinates by the specified number of quadrants 2035 * around the specified anchor point. 2036 * This operation is equivalent to calling: 2037 * <pre> 2038 * setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory); 2039 * </pre> 2040 * Rotating by a positive number of quadrants rotates points on 2041 * the positive X axis toward the positive Y axis. 2042 * 2043 * @param numquadrants the number of 90 degree arcs to rotate by 2044 * @param anchorx the X coordinate of the rotation anchor point 2045 * @param anchory the Y coordinate of the rotation anchor point 2046 * @since 1.6 2047 */ 2048 public void setToQuadrantRotation(int numquadrants, 2049 double anchorx, double anchory) 2050 { 2051 switch (numquadrants & 3) { 2052 case 0: 2053 m00 = 1.0; 2054 m10 = 0.0; 2055 m01 = 0.0; 2056 m11 = 1.0; 2057 m02 = 0.0; 2058 m12 = 0.0; 2059 state = APPLY_IDENTITY; 2060 type = TYPE_IDENTITY; 2061 break; 2062 case 1: 2063 m00 = 0.0; 2064 m10 = 1.0; 2065 m01 = -1.0; 2066 m11 = 0.0; 2067 m02 = anchorx + anchory; 2068 m12 = anchory - anchorx; 2069 if (m02 == 0.0 && m12 == 0.0) { 2070 state = APPLY_SHEAR; 2071 type = TYPE_QUADRANT_ROTATION; 2072 } else { 2073 state = APPLY_SHEAR | APPLY_TRANSLATE; 2074 type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; 2075 } 2076 break; 2077 case 2: 2078 m00 = -1.0; 2079 m10 = 0.0; 2080 m01 = 0.0; 2081 m11 = -1.0; 2082 m02 = anchorx + anchorx; 2083 m12 = anchory + anchory; 2084 if (m02 == 0.0 && m12 == 0.0) { 2085 state = APPLY_SCALE; 2086 type = TYPE_QUADRANT_ROTATION; 2087 } else { 2088 state = APPLY_SCALE | APPLY_TRANSLATE; 2089 type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; 2090 } 2091 break; 2092 case 3: 2093 m00 = 0.0; 2094 m10 = -1.0; 2095 m01 = 1.0; 2096 m11 = 0.0; 2097 m02 = anchorx - anchory; 2098 m12 = anchory + anchorx; 2099 if (m02 == 0.0 && m12 == 0.0) { 2100 state = APPLY_SHEAR; 2101 type = TYPE_QUADRANT_ROTATION; 2102 } else { 2103 state = APPLY_SHEAR | APPLY_TRANSLATE; 2104 type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; 2105 } 2106 break; 2107 } 2108 } 2109 2110 /** 2111 * Sets this transform to a scaling transformation. 2112 * The matrix representing this transform becomes: 2113 * <pre> 2114 * [ sx 0 0 ] 2115 * [ 0 sy 0 ] 2116 * [ 0 0 1 ] 2117 * </pre> 2118 * @param sx the factor by which coordinates are scaled along the 2119 * X axis direction 2120 * @param sy the factor by which coordinates are scaled along the 2121 * Y axis direction 2122 * @since 1.2 2123 */ 2124 public void setToScale(double sx, double sy) { 2125 m00 = sx; 2126 m10 = 0.0; 2127 m01 = 0.0; 2128 m11 = sy; 2129 m02 = 0.0; 2130 m12 = 0.0; 2131 if (sx != 1.0 || sy != 1.0) { 2132 state = APPLY_SCALE; 2133 type = TYPE_UNKNOWN; 2134 } else { 2135 state = APPLY_IDENTITY; 2136 type = TYPE_IDENTITY; 2137 } 2138 } 2139 2140 /** 2141 * Sets this transform to a shearing transformation. 2142 * The matrix representing this transform becomes: 2143 * <pre> 2144 * [ 1 shx 0 ] 2145 * [ shy 1 0 ] 2146 * [ 0 0 1 ] 2147 * </pre> 2148 * @param shx the multiplier by which coordinates are shifted in the 2149 * direction of the positive X axis as a factor of their Y coordinate 2150 * @param shy the multiplier by which coordinates are shifted in the 2151 * direction of the positive Y axis as a factor of their X coordinate 2152 * @since 1.2 2153 */ 2154 public void setToShear(double shx, double shy) { 2155 m00 = 1.0; 2156 m01 = shx; 2157 m10 = shy; 2158 m11 = 1.0; 2159 m02 = 0.0; 2160 m12 = 0.0; 2161 if (shx != 0.0 || shy != 0.0) { 2162 state = (APPLY_SHEAR | APPLY_SCALE); 2163 type = TYPE_UNKNOWN; 2164 } else { 2165 state = APPLY_IDENTITY; 2166 type = TYPE_IDENTITY; 2167 } 2168 } 2169 2170 /** 2171 * Sets this transform to a copy of the transform in the specified 2172 * {@code AffineTransform} object. 2173 * @param Tx the {@code AffineTransform} object from which to 2174 * copy the transform 2175 * @since 1.2 2176 */ 2177 public void setTransform(AffineTransform Tx) { 2178 this.m00 = Tx.m00; 2179 this.m10 = Tx.m10; 2180 this.m01 = Tx.m01; 2181 this.m11 = Tx.m11; 2182 this.m02 = Tx.m02; 2183 this.m12 = Tx.m12; 2184 this.state = Tx.state; 2185 this.type = Tx.type; 2186 } 2187 2188 /** 2189 * Sets this transform to the matrix specified by the 6 2190 * double precision values. 2191 * 2192 * @param m00 the X coordinate scaling element of the 3x3 matrix 2193 * @param m10 the Y coordinate shearing element of the 3x3 matrix 2194 * @param m01 the X coordinate shearing element of the 3x3 matrix 2195 * @param m11 the Y coordinate scaling element of the 3x3 matrix 2196 * @param m02 the X coordinate translation element of the 3x3 matrix 2197 * @param m12 the Y coordinate translation element of the 3x3 matrix 2198 * @since 1.2 2199 */ 2200 public void setTransform(double m00, double m10, 2201 double m01, double m11, 2202 double m02, double m12) { 2203 this.m00 = m00; 2204 this.m10 = m10; 2205 this.m01 = m01; 2206 this.m11 = m11; 2207 this.m02 = m02; 2208 this.m12 = m12; 2209 updateState(); 2210 } 2211 2212 /** 2213 * Concatenates an {@code AffineTransform Tx} to 2214 * this {@code AffineTransform} Cx in the most commonly useful 2215 * way to provide a new user space 2216 * that is mapped to the former user space by {@code Tx}. 2217 * Cx is updated to perform the combined transformation. 2218 * Transforming a point p by the updated transform Cx' is 2219 * equivalent to first transforming p by {@code Tx} and then 2220 * transforming the result by the original transform Cx like this: 2221 * Cx'(p) = Cx(Tx(p)) 2222 * In matrix notation, if this transform Cx is 2223 * represented by the matrix [this] and {@code Tx} is represented 2224 * by the matrix [Tx] then this method does the following: 2225 * <pre> 2226 * [this] = [this] x [Tx] 2227 * </pre> 2228 * @param Tx the {@code AffineTransform} object to be 2229 * concatenated with this {@code AffineTransform} object. 2230 * @see #preConcatenate 2231 * @since 1.2 2232 */ 2233 @SuppressWarnings("fallthrough") 2234 public void concatenate(AffineTransform Tx) { 2235 double M0, M1; 2236 double T00, T01, T10, T11; 2237 double T02, T12; 2238 int mystate = state; 2239 int txstate = Tx.state; 2240 switch ((txstate << HI_SHIFT) | mystate) { 2241 2242 /* ---------- Tx == IDENTITY cases ---------- */ 2243 case (HI_IDENTITY | APPLY_IDENTITY): 2244 case (HI_IDENTITY | APPLY_TRANSLATE): 2245 case (HI_IDENTITY | APPLY_SCALE): 2246 case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): 2247 case (HI_IDENTITY | APPLY_SHEAR): 2248 case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): 2249 case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): 2250 case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2251 return; 2252 2253 /* ---------- this == IDENTITY cases ---------- */ 2254 case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): 2255 m01 = Tx.m01; 2256 m10 = Tx.m10; 2257 /* NOBREAK */ 2258 case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): 2259 m00 = Tx.m00; 2260 m11 = Tx.m11; 2261 /* NOBREAK */ 2262 case (HI_TRANSLATE | APPLY_IDENTITY): 2263 m02 = Tx.m02; 2264 m12 = Tx.m12; 2265 state = txstate; 2266 type = Tx.type; 2267 return; 2268 case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY): 2269 m01 = Tx.m01; 2270 m10 = Tx.m10; 2271 /* NOBREAK */ 2272 case (HI_SCALE | APPLY_IDENTITY): 2273 m00 = Tx.m00; 2274 m11 = Tx.m11; 2275 state = txstate; 2276 type = Tx.type; 2277 return; 2278 case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY): 2279 m02 = Tx.m02; 2280 m12 = Tx.m12; 2281 /* NOBREAK */ 2282 case (HI_SHEAR | APPLY_IDENTITY): 2283 m01 = Tx.m01; 2284 m10 = Tx.m10; 2285 m00 = m11 = 0.0; 2286 state = txstate; 2287 type = Tx.type; 2288 return; 2289 2290 /* ---------- Tx == TRANSLATE cases ---------- */ 2291 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2292 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): 2293 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): 2294 case (HI_TRANSLATE | APPLY_SHEAR): 2295 case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): 2296 case (HI_TRANSLATE | APPLY_SCALE): 2297 case (HI_TRANSLATE | APPLY_TRANSLATE): 2298 translate(Tx.m02, Tx.m12); 2299 return; 2300 2301 /* ---------- Tx == SCALE cases ---------- */ 2302 case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2303 case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): 2304 case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): 2305 case (HI_SCALE | APPLY_SHEAR): 2306 case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): 2307 case (HI_SCALE | APPLY_SCALE): 2308 case (HI_SCALE | APPLY_TRANSLATE): 2309 scale(Tx.m00, Tx.m11); 2310 return; 2311 2312 /* ---------- Tx == SHEAR cases ---------- */ 2313 case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2314 case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): 2315 T01 = Tx.m01; T10 = Tx.m10; 2316 M0 = m00; 2317 m00 = m01 * T10; 2318 m01 = M0 * T01; 2319 M0 = m10; 2320 m10 = m11 * T10; 2321 m11 = M0 * T01; 2322 type = TYPE_UNKNOWN; 2323 return; 2324 case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): 2325 case (HI_SHEAR | APPLY_SHEAR): 2326 m00 = m01 * Tx.m10; 2327 m01 = 0.0; 2328 m11 = m10 * Tx.m01; 2329 m10 = 0.0; 2330 state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); 2331 type = TYPE_UNKNOWN; 2332 return; 2333 case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2334 case (HI_SHEAR | APPLY_SCALE): 2335 m01 = m00 * Tx.m01; 2336 m00 = 0.0; 2337 m10 = m11 * Tx.m10; 2338 m11 = 0.0; 2339 state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); 2340 type = TYPE_UNKNOWN; 2341 return; 2342 case (HI_SHEAR | APPLY_TRANSLATE): 2343 m00 = 0.0; 2344 m01 = Tx.m01; 2345 m10 = Tx.m10; 2346 m11 = 0.0; 2347 state = APPLY_TRANSLATE | APPLY_SHEAR; 2348 type = TYPE_UNKNOWN; 2349 return; 2350 } 2351 // If Tx has more than one attribute, it is not worth optimizing 2352 // all of those cases... 2353 T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02; 2354 T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12; 2355 switch (mystate) { 2356 default: 2357 stateError(); 2358 /* NOTREACHED */ 2359 case (APPLY_SHEAR | APPLY_SCALE): 2360 state = mystate | txstate; 2361 /* NOBREAK */ 2362 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2363 M0 = m00; 2364 M1 = m01; 2365 m00 = T00 * M0 + T10 * M1; 2366 m01 = T01 * M0 + T11 * M1; 2367 m02 += T02 * M0 + T12 * M1; 2368 2369 M0 = m10; 2370 M1 = m11; 2371 m10 = T00 * M0 + T10 * M1; 2372 m11 = T01 * M0 + T11 * M1; 2373 m12 += T02 * M0 + T12 * M1; 2374 type = TYPE_UNKNOWN; 2375 return; 2376 2377 case (APPLY_SHEAR | APPLY_TRANSLATE): 2378 case (APPLY_SHEAR): 2379 M0 = m01; 2380 m00 = T10 * M0; 2381 m01 = T11 * M0; 2382 m02 += T12 * M0; 2383 2384 M0 = m10; 2385 m10 = T00 * M0; 2386 m11 = T01 * M0; 2387 m12 += T02 * M0; 2388 break; 2389 2390 case (APPLY_SCALE | APPLY_TRANSLATE): 2391 case (APPLY_SCALE): 2392 M0 = m00; 2393 m00 = T00 * M0; 2394 m01 = T01 * M0; 2395 m02 += T02 * M0; 2396 2397 M0 = m11; 2398 m10 = T10 * M0; 2399 m11 = T11 * M0; 2400 m12 += T12 * M0; 2401 break; 2402 2403 case (APPLY_TRANSLATE): 2404 m00 = T00; 2405 m01 = T01; 2406 m02 += T02; 2407 2408 m10 = T10; 2409 m11 = T11; 2410 m12 += T12; 2411 state = txstate | APPLY_TRANSLATE; 2412 type = TYPE_UNKNOWN; 2413 return; 2414 } 2415 updateState(); 2416 } 2417 2418 /** 2419 * Concatenates an {@code AffineTransform Tx} to 2420 * this {@code AffineTransform} Cx 2421 * in a less commonly used way such that {@code Tx} modifies the 2422 * coordinate transformation relative to the absolute pixel 2423 * space rather than relative to the existing user space. 2424 * Cx is updated to perform the combined transformation. 2425 * Transforming a point p by the updated transform Cx' is 2426 * equivalent to first transforming p by the original transform 2427 * Cx and then transforming the result by 2428 * {@code Tx} like this: 2429 * Cx'(p) = Tx(Cx(p)) 2430 * In matrix notation, if this transform Cx 2431 * is represented by the matrix [this] and {@code Tx} is 2432 * represented by the matrix [Tx] then this method does the 2433 * following: 2434 * <pre> 2435 * [this] = [Tx] x [this] 2436 * </pre> 2437 * @param Tx the {@code AffineTransform} object to be 2438 * concatenated with this {@code AffineTransform} object. 2439 * @see #concatenate 2440 * @since 1.2 2441 */ 2442 @SuppressWarnings("fallthrough") 2443 public void preConcatenate(AffineTransform Tx) { 2444 double M0, M1; 2445 double T00, T01, T10, T11; 2446 double T02, T12; 2447 int mystate = state; 2448 int txstate = Tx.state; 2449 switch ((txstate << HI_SHIFT) | mystate) { 2450 case (HI_IDENTITY | APPLY_IDENTITY): 2451 case (HI_IDENTITY | APPLY_TRANSLATE): 2452 case (HI_IDENTITY | APPLY_SCALE): 2453 case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): 2454 case (HI_IDENTITY | APPLY_SHEAR): 2455 case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): 2456 case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): 2457 case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2458 // Tx is IDENTITY... 2459 return; 2460 2461 case (HI_TRANSLATE | APPLY_IDENTITY): 2462 case (HI_TRANSLATE | APPLY_SCALE): 2463 case (HI_TRANSLATE | APPLY_SHEAR): 2464 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): 2465 // Tx is TRANSLATE, this has no TRANSLATE 2466 m02 = Tx.m02; 2467 m12 = Tx.m12; 2468 state = mystate | APPLY_TRANSLATE; 2469 type |= TYPE_TRANSLATION; 2470 return; 2471 2472 case (HI_TRANSLATE | APPLY_TRANSLATE): 2473 case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): 2474 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): 2475 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2476 // Tx is TRANSLATE, this has one too 2477 m02 = m02 + Tx.m02; 2478 m12 = m12 + Tx.m12; 2479 return; 2480 2481 case (HI_SCALE | APPLY_TRANSLATE): 2482 case (HI_SCALE | APPLY_IDENTITY): 2483 // Only these two existing states need a new state 2484 state = mystate | APPLY_SCALE; 2485 /* NOBREAK */ 2486 case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2487 case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): 2488 case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): 2489 case (HI_SCALE | APPLY_SHEAR): 2490 case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): 2491 case (HI_SCALE | APPLY_SCALE): 2492 // Tx is SCALE, this is anything 2493 T00 = Tx.m00; 2494 T11 = Tx.m11; 2495 if ((mystate & APPLY_SHEAR) != 0) { 2496 m01 = m01 * T00; 2497 m10 = m10 * T11; 2498 if ((mystate & APPLY_SCALE) != 0) { 2499 m00 = m00 * T00; 2500 m11 = m11 * T11; 2501 } 2502 } else { 2503 m00 = m00 * T00; 2504 m11 = m11 * T11; 2505 } 2506 if ((mystate & APPLY_TRANSLATE) != 0) { 2507 m02 = m02 * T00; 2508 m12 = m12 * T11; 2509 } 2510 type = TYPE_UNKNOWN; 2511 return; 2512 case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): 2513 case (HI_SHEAR | APPLY_SHEAR): 2514 mystate = mystate | APPLY_SCALE; 2515 /* NOBREAK */ 2516 case (HI_SHEAR | APPLY_TRANSLATE): 2517 case (HI_SHEAR | APPLY_IDENTITY): 2518 case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2519 case (HI_SHEAR | APPLY_SCALE): 2520 state = mystate ^ APPLY_SHEAR; 2521 /* NOBREAK */ 2522 case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2523 case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): 2524 // Tx is SHEAR, this is anything 2525 T01 = Tx.m01; 2526 T10 = Tx.m10; 2527 2528 M0 = m00; 2529 m00 = m10 * T01; 2530 m10 = M0 * T10; 2531 2532 M0 = m01; 2533 m01 = m11 * T01; 2534 m11 = M0 * T10; 2535 2536 M0 = m02; 2537 m02 = m12 * T01; 2538 m12 = M0 * T10; 2539 type = TYPE_UNKNOWN; 2540 return; 2541 } 2542 // If Tx has more than one attribute, it is not worth optimizing 2543 // all of those cases... 2544 T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02; 2545 T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12; 2546 switch (mystate) { 2547 default: 2548 stateError(); 2549 /* NOTREACHED */ 2550 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2551 M0 = m02; 2552 M1 = m12; 2553 T02 += M0 * T00 + M1 * T01; 2554 T12 += M0 * T10 + M1 * T11; 2555 2556 /* NOBREAK */ 2557 case (APPLY_SHEAR | APPLY_SCALE): 2558 m02 = T02; 2559 m12 = T12; 2560 2561 M0 = m00; 2562 M1 = m10; 2563 m00 = M0 * T00 + M1 * T01; 2564 m10 = M0 * T10 + M1 * T11; 2565 2566 M0 = m01; 2567 M1 = m11; 2568 m01 = M0 * T00 + M1 * T01; 2569 m11 = M0 * T10 + M1 * T11; 2570 break; 2571 2572 case (APPLY_SHEAR | APPLY_TRANSLATE): 2573 M0 = m02; 2574 M1 = m12; 2575 T02 += M0 * T00 + M1 * T01; 2576 T12 += M0 * T10 + M1 * T11; 2577 2578 /* NOBREAK */ 2579 case (APPLY_SHEAR): 2580 m02 = T02; 2581 m12 = T12; 2582 2583 M0 = m10; 2584 m00 = M0 * T01; 2585 m10 = M0 * T11; 2586 2587 M0 = m01; 2588 m01 = M0 * T00; 2589 m11 = M0 * T10; 2590 break; 2591 2592 case (APPLY_SCALE | APPLY_TRANSLATE): 2593 M0 = m02; 2594 M1 = m12; 2595 T02 += M0 * T00 + M1 * T01; 2596 T12 += M0 * T10 + M1 * T11; 2597 2598 /* NOBREAK */ 2599 case (APPLY_SCALE): 2600 m02 = T02; 2601 m12 = T12; 2602 2603 M0 = m00; 2604 m00 = M0 * T00; 2605 m10 = M0 * T10; 2606 2607 M0 = m11; 2608 m01 = M0 * T01; 2609 m11 = M0 * T11; 2610 break; 2611 2612 case (APPLY_TRANSLATE): 2613 M0 = m02; 2614 M1 = m12; 2615 T02 += M0 * T00 + M1 * T01; 2616 T12 += M0 * T10 + M1 * T11; 2617 2618 /* NOBREAK */ 2619 case (APPLY_IDENTITY): 2620 m02 = T02; 2621 m12 = T12; 2622 2623 m00 = T00; 2624 m10 = T10; 2625 2626 m01 = T01; 2627 m11 = T11; 2628 2629 state = mystate | txstate; 2630 type = TYPE_UNKNOWN; 2631 return; 2632 } 2633 updateState(); 2634 } 2635 2636 /** 2637 * Returns an {@code AffineTransform} object representing the 2638 * inverse transformation. 2639 * The inverse transform Tx' of this transform Tx 2640 * maps coordinates transformed by Tx back 2641 * to their original coordinates. 2642 * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)). 2643 * <p> 2644 * If this transform maps all coordinates onto a point or a line 2645 * then it will not have an inverse, since coordinates that do 2646 * not lie on the destination point or line will not have an inverse 2647 * mapping. 2648 * The {@code getDeterminant} method can be used to determine if this 2649 * transform has no inverse, in which case an exception will be 2650 * thrown if the {@code createInverse} method is called. 2651 * @return a new {@code AffineTransform} object representing the 2652 * inverse transformation. 2653 * @see #getDeterminant 2654 * @exception NoninvertibleTransformException 2655 * if the matrix cannot be inverted. 2656 * @since 1.2 2657 */ 2658 public AffineTransform createInverse() 2659 throws NoninvertibleTransformException 2660 { 2661 double det; 2662 switch (state) { 2663 default: 2664 stateError(); 2665 /* NOTREACHED */ 2666 return null; 2667 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2668 det = m00 * m11 - m01 * m10; 2669 if (Math.abs(det) <= Double.MIN_VALUE) { 2670 throw new NoninvertibleTransformException("Determinant is "+ 2671 det); 2672 } 2673 return new AffineTransform( m11 / det, -m10 / det, 2674 -m01 / det, m00 / det, 2675 (m01 * m12 - m11 * m02) / det, 2676 (m10 * m02 - m00 * m12) / det, 2677 (APPLY_SHEAR | 2678 APPLY_SCALE | 2679 APPLY_TRANSLATE)); 2680 case (APPLY_SHEAR | APPLY_SCALE): 2681 det = m00 * m11 - m01 * m10; 2682 if (Math.abs(det) <= Double.MIN_VALUE) { 2683 throw new NoninvertibleTransformException("Determinant is "+ 2684 det); 2685 } 2686 return new AffineTransform( m11 / det, -m10 / det, 2687 -m01 / det, m00 / det, 2688 0.0, 0.0, 2689 (APPLY_SHEAR | APPLY_SCALE)); 2690 case (APPLY_SHEAR | APPLY_TRANSLATE): 2691 if (m01 == 0.0 || m10 == 0.0) { 2692 throw new NoninvertibleTransformException("Determinant is 0"); 2693 } 2694 return new AffineTransform( 0.0, 1.0 / m01, 2695 1.0 / m10, 0.0, 2696 -m12 / m10, -m02 / m01, 2697 (APPLY_SHEAR | APPLY_TRANSLATE)); 2698 case (APPLY_SHEAR): 2699 if (m01 == 0.0 || m10 == 0.0) { 2700 throw new NoninvertibleTransformException("Determinant is 0"); 2701 } 2702 return new AffineTransform(0.0, 1.0 / m01, 2703 1.0 / m10, 0.0, 2704 0.0, 0.0, 2705 (APPLY_SHEAR)); 2706 case (APPLY_SCALE | APPLY_TRANSLATE): 2707 if (m00 == 0.0 || m11 == 0.0) { 2708 throw new NoninvertibleTransformException("Determinant is 0"); 2709 } 2710 return new AffineTransform( 1.0 / m00, 0.0, 2711 0.0, 1.0 / m11, 2712 -m02 / m00, -m12 / m11, 2713 (APPLY_SCALE | APPLY_TRANSLATE)); 2714 case (APPLY_SCALE): 2715 if (m00 == 0.0 || m11 == 0.0) { 2716 throw new NoninvertibleTransformException("Determinant is 0"); 2717 } 2718 return new AffineTransform(1.0 / m00, 0.0, 2719 0.0, 1.0 / m11, 2720 0.0, 0.0, 2721 (APPLY_SCALE)); 2722 case (APPLY_TRANSLATE): 2723 return new AffineTransform( 1.0, 0.0, 2724 0.0, 1.0, 2725 -m02, -m12, 2726 (APPLY_TRANSLATE)); 2727 case (APPLY_IDENTITY): 2728 return new AffineTransform(); 2729 } 2730 2731 /* NOTREACHED */ 2732 } 2733 2734 /** 2735 * Sets this transform to the inverse of itself. 2736 * The inverse transform Tx' of this transform Tx 2737 * maps coordinates transformed by Tx back 2738 * to their original coordinates. 2739 * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)). 2740 * <p> 2741 * If this transform maps all coordinates onto a point or a line 2742 * then it will not have an inverse, since coordinates that do 2743 * not lie on the destination point or line will not have an inverse 2744 * mapping. 2745 * The {@code getDeterminant} method can be used to determine if this 2746 * transform has no inverse, in which case an exception will be 2747 * thrown if the {@code invert} method is called. 2748 * @see #getDeterminant 2749 * @exception NoninvertibleTransformException 2750 * if the matrix cannot be inverted. 2751 * @since 1.6 2752 */ 2753 public void invert() 2754 throws NoninvertibleTransformException 2755 { 2756 double M00, M01, M02; 2757 double M10, M11, M12; 2758 double det; 2759 switch (state) { 2760 default: 2761 stateError(); 2762 /* NOTREACHED */ 2763 return; 2764 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2765 M00 = m00; M01 = m01; M02 = m02; 2766 M10 = m10; M11 = m11; M12 = m12; 2767 det = M00 * M11 - M01 * M10; 2768 if (Math.abs(det) <= Double.MIN_VALUE) { 2769 throw new NoninvertibleTransformException("Determinant is "+ 2770 det); 2771 } 2772 m00 = M11 / det; 2773 m10 = -M10 / det; 2774 m01 = -M01 / det; 2775 m11 = M00 / det; 2776 m02 = (M01 * M12 - M11 * M02) / det; 2777 m12 = (M10 * M02 - M00 * M12) / det; 2778 break; 2779 case (APPLY_SHEAR | APPLY_SCALE): 2780 M00 = m00; M01 = m01; 2781 M10 = m10; M11 = m11; 2782 det = M00 * M11 - M01 * M10; 2783 if (Math.abs(det) <= Double.MIN_VALUE) { 2784 throw new NoninvertibleTransformException("Determinant is "+ 2785 det); 2786 } 2787 m00 = M11 / det; 2788 m10 = -M10 / det; 2789 m01 = -M01 / det; 2790 m11 = M00 / det; 2791 // m02 = 0.0; 2792 // m12 = 0.0; 2793 break; 2794 case (APPLY_SHEAR | APPLY_TRANSLATE): 2795 M01 = m01; M02 = m02; 2796 M10 = m10; M12 = m12; 2797 if (M01 == 0.0 || M10 == 0.0) { 2798 throw new NoninvertibleTransformException("Determinant is 0"); 2799 } 2800 // m00 = 0.0; 2801 m10 = 1.0 / M01; 2802 m01 = 1.0 / M10; 2803 // m11 = 0.0; 2804 m02 = -M12 / M10; 2805 m12 = -M02 / M01; 2806 break; 2807 case (APPLY_SHEAR): 2808 M01 = m01; 2809 M10 = m10; 2810 if (M01 == 0.0 || M10 == 0.0) { 2811 throw new NoninvertibleTransformException("Determinant is 0"); 2812 } 2813 // m00 = 0.0; 2814 m10 = 1.0 / M01; 2815 m01 = 1.0 / M10; 2816 // m11 = 0.0; 2817 // m02 = 0.0; 2818 // m12 = 0.0; 2819 break; 2820 case (APPLY_SCALE | APPLY_TRANSLATE): 2821 M00 = m00; M02 = m02; 2822 M11 = m11; M12 = m12; 2823 if (M00 == 0.0 || M11 == 0.0) { 2824 throw new NoninvertibleTransformException("Determinant is 0"); 2825 } 2826 m00 = 1.0 / M00; 2827 // m10 = 0.0; 2828 // m01 = 0.0; 2829 m11 = 1.0 / M11; 2830 m02 = -M02 / M00; 2831 m12 = -M12 / M11; 2832 break; 2833 case (APPLY_SCALE): 2834 M00 = m00; 2835 M11 = m11; 2836 if (M00 == 0.0 || M11 == 0.0) { 2837 throw new NoninvertibleTransformException("Determinant is 0"); 2838 } 2839 m00 = 1.0 / M00; 2840 // m10 = 0.0; 2841 // m01 = 0.0; 2842 m11 = 1.0 / M11; 2843 // m02 = 0.0; 2844 // m12 = 0.0; 2845 break; 2846 case (APPLY_TRANSLATE): 2847 // m00 = 1.0; 2848 // m10 = 0.0; 2849 // m01 = 0.0; 2850 // m11 = 1.0; 2851 m02 = -m02; 2852 m12 = -m12; 2853 break; 2854 case (APPLY_IDENTITY): 2855 // m00 = 1.0; 2856 // m10 = 0.0; 2857 // m01 = 0.0; 2858 // m11 = 1.0; 2859 // m02 = 0.0; 2860 // m12 = 0.0; 2861 break; 2862 } 2863 } 2864 2865 /** 2866 * Transforms the specified {@code ptSrc} and stores the result 2867 * in {@code ptDst}. 2868 * If {@code ptDst} is {@code null}, a new {@link Point2D} 2869 * object is allocated and then the result of the transformation is 2870 * stored in this object. 2871 * In either case, {@code ptDst}, which contains the 2872 * transformed point, is returned for convenience. 2873 * If {@code ptSrc} and {@code ptDst} are the same 2874 * object, the input point is correctly overwritten with 2875 * the transformed point. 2876 * @param ptSrc the specified {@code Point2D} to be transformed 2877 * @param ptDst the specified {@code Point2D} that stores the 2878 * result of transforming {@code ptSrc} 2879 * @return the {@code ptDst} after transforming 2880 * {@code ptSrc} and storing the result in {@code ptDst}. 2881 * @since 1.2 2882 */ 2883 public Point2D transform(Point2D ptSrc, Point2D ptDst) { 2884 if (ptDst == null) { 2885 if (ptSrc instanceof Point2D.Double) { 2886 ptDst = new Point2D.Double(); 2887 } else { 2888 ptDst = new Point2D.Float(); 2889 } 2890 } 2891 // Copy source coords into local variables in case src == dst 2892 double x = ptSrc.getX(); 2893 double y = ptSrc.getY(); 2894 switch (state) { 2895 default: 2896 stateError(); 2897 /* NOTREACHED */ 2898 return null; 2899 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2900 ptDst.setLocation(x * m00 + y * m01 + m02, 2901 x * m10 + y * m11 + m12); 2902 return ptDst; 2903 case (APPLY_SHEAR | APPLY_SCALE): 2904 ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11); 2905 return ptDst; 2906 case (APPLY_SHEAR | APPLY_TRANSLATE): 2907 ptDst.setLocation(y * m01 + m02, x * m10 + m12); 2908 return ptDst; 2909 case (APPLY_SHEAR): 2910 ptDst.setLocation(y * m01, x * m10); 2911 return ptDst; 2912 case (APPLY_SCALE | APPLY_TRANSLATE): 2913 ptDst.setLocation(x * m00 + m02, y * m11 + m12); 2914 return ptDst; 2915 case (APPLY_SCALE): 2916 ptDst.setLocation(x * m00, y * m11); 2917 return ptDst; 2918 case (APPLY_TRANSLATE): 2919 ptDst.setLocation(x + m02, y + m12); 2920 return ptDst; 2921 case (APPLY_IDENTITY): 2922 ptDst.setLocation(x, y); 2923 return ptDst; 2924 } 2925 2926 /* NOTREACHED */ 2927 } 2928 2929 /** 2930 * Transforms an array of point objects by this transform. 2931 * If any element of the {@code ptDst} array is 2932 * {@code null}, a new {@code Point2D} object is allocated 2933 * and stored into that element before storing the results of the 2934 * transformation. 2935 * <p> 2936 * Note that this method does not take any precautions to 2937 * avoid problems caused by storing results into {@code Point2D} 2938 * objects that will be used as the source for calculations 2939 * further down the source array. 2940 * This method does guarantee that if a specified {@code Point2D} 2941 * object is both the source and destination for the same single point 2942 * transform operation then the results will not be stored until 2943 * the calculations are complete to avoid storing the results on 2944 * top of the operands. 2945 * If, however, the destination {@code Point2D} object for one 2946 * operation is the same object as the source {@code Point2D} 2947 * object for another operation further down the source array then 2948 * the original coordinates in that point are overwritten before 2949 * they can be converted. 2950 * @param ptSrc the array containing the source point objects 2951 * @param ptDst the array into which the transform point objects are 2952 * returned 2953 * @param srcOff the offset to the first point object to be 2954 * transformed in the source array 2955 * @param dstOff the offset to the location of the first 2956 * transformed point object that is stored in the destination array 2957 * @param numPts the number of point objects to be transformed 2958 * @since 1.2 2959 */ 2960 public void transform(Point2D[] ptSrc, int srcOff, 2961 Point2D[] ptDst, int dstOff, 2962 int numPts) { 2963 int state = this.state; 2964 while (--numPts >= 0) { 2965 // Copy source coords into local variables in case src == dst 2966 Point2D src = ptSrc[srcOff++]; 2967 double x = src.getX(); 2968 double y = src.getY(); 2969 Point2D dst = ptDst[dstOff++]; 2970 if (dst == null) { 2971 if (src instanceof Point2D.Double) { 2972 dst = new Point2D.Double(); 2973 } else { 2974 dst = new Point2D.Float(); 2975 } 2976 ptDst[dstOff - 1] = dst; 2977 } 2978 switch (state) { 2979 default: 2980 stateError(); 2981 /* NOTREACHED */ 2982 return; 2983 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2984 dst.setLocation(x * m00 + y * m01 + m02, 2985 x * m10 + y * m11 + m12); 2986 break; 2987 case (APPLY_SHEAR | APPLY_SCALE): 2988 dst.setLocation(x * m00 + y * m01, x * m10 + y * m11); 2989 break; 2990 case (APPLY_SHEAR | APPLY_TRANSLATE): 2991 dst.setLocation(y * m01 + m02, x * m10 + m12); 2992 break; 2993 case (APPLY_SHEAR): 2994 dst.setLocation(y * m01, x * m10); 2995 break; 2996 case (APPLY_SCALE | APPLY_TRANSLATE): 2997 dst.setLocation(x * m00 + m02, y * m11 + m12); 2998 break; 2999 case (APPLY_SCALE): 3000 dst.setLocation(x * m00, y * m11); 3001 break; 3002 case (APPLY_TRANSLATE): 3003 dst.setLocation(x + m02, y + m12); 3004 break; 3005 case (APPLY_IDENTITY): 3006 dst.setLocation(x, y); 3007 break; 3008 } 3009 } 3010 3011 /* NOTREACHED */ 3012 } 3013 3014 /** 3015 * Transforms an array of floating point coordinates by this transform. 3016 * The two coordinate array sections can be exactly the same or 3017 * can be overlapping sections of the same array without affecting the 3018 * validity of the results. 3019 * This method ensures that no source coordinates are overwritten by a 3020 * previous operation before they can be transformed. 3021 * The coordinates are stored in the arrays starting at the specified 3022 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3023 * @param srcPts the array containing the source point coordinates. 3024 * Each point is stored as a pair of x, y coordinates. 3025 * @param dstPts the array into which the transformed point coordinates 3026 * are returned. Each point is stored as a pair of x, y 3027 * coordinates. 3028 * @param srcOff the offset to the first point to be transformed 3029 * in the source array 3030 * @param dstOff the offset to the location of the first 3031 * transformed point that is stored in the destination array 3032 * @param numPts the number of points to be transformed 3033 * @since 1.2 3034 */ 3035 public void transform(float[] srcPts, int srcOff, 3036 float[] dstPts, int dstOff, 3037 int numPts) { 3038 double M00, M01, M02, M10, M11, M12; // For caching 3039 if (dstPts == srcPts && 3040 dstOff > srcOff && dstOff < srcOff + numPts * 2) 3041 { 3042 // If the arrays overlap partially with the destination higher 3043 // than the source and we transform the coordinates normally 3044 // we would overwrite some of the later source coordinates 3045 // with results of previous transformations. 3046 // To get around this we use arraycopy to copy the points 3047 // to their final destination with correct overwrite 3048 // handling and then transform them in place in the new 3049 // safer location. 3050 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); 3051 // srcPts = dstPts; // They are known to be equal. 3052 srcOff = dstOff; 3053 } 3054 switch (state) { 3055 default: 3056 stateError(); 3057 /* NOTREACHED */ 3058 return; 3059 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3060 M00 = m00; M01 = m01; M02 = m02; 3061 M10 = m10; M11 = m11; M12 = m12; 3062 while (--numPts >= 0) { 3063 double x = srcPts[srcOff++]; 3064 double y = srcPts[srcOff++]; 3065 dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02); 3066 dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12); 3067 } 3068 return; 3069 case (APPLY_SHEAR | APPLY_SCALE): 3070 M00 = m00; M01 = m01; 3071 M10 = m10; M11 = m11; 3072 while (--numPts >= 0) { 3073 double x = srcPts[srcOff++]; 3074 double y = srcPts[srcOff++]; 3075 dstPts[dstOff++] = (float) (M00 * x + M01 * y); 3076 dstPts[dstOff++] = (float) (M10 * x + M11 * y); 3077 } 3078 return; 3079 case (APPLY_SHEAR | APPLY_TRANSLATE): 3080 M01 = m01; M02 = m02; 3081 M10 = m10; M12 = m12; 3082 while (--numPts >= 0) { 3083 double x = srcPts[srcOff++]; 3084 dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02); 3085 dstPts[dstOff++] = (float) (M10 * x + M12); 3086 } 3087 return; 3088 case (APPLY_SHEAR): 3089 M01 = m01; M10 = m10; 3090 while (--numPts >= 0) { 3091 double x = srcPts[srcOff++]; 3092 dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]); 3093 dstPts[dstOff++] = (float) (M10 * x); 3094 } 3095 return; 3096 case (APPLY_SCALE | APPLY_TRANSLATE): 3097 M00 = m00; M02 = m02; 3098 M11 = m11; M12 = m12; 3099 while (--numPts >= 0) { 3100 dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02); 3101 dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12); 3102 } 3103 return; 3104 case (APPLY_SCALE): 3105 M00 = m00; M11 = m11; 3106 while (--numPts >= 0) { 3107 dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]); 3108 dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]); 3109 } 3110 return; 3111 case (APPLY_TRANSLATE): 3112 M02 = m02; M12 = m12; 3113 while (--numPts >= 0) { 3114 dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02); 3115 dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12); 3116 } 3117 return; 3118 case (APPLY_IDENTITY): 3119 if (srcPts != dstPts || srcOff != dstOff) { 3120 System.arraycopy(srcPts, srcOff, dstPts, dstOff, 3121 numPts * 2); 3122 } 3123 return; 3124 } 3125 3126 /* NOTREACHED */ 3127 } 3128 3129 /** 3130 * Transforms an array of double precision coordinates by this transform. 3131 * The two coordinate array sections can be exactly the same or 3132 * can be overlapping sections of the same array without affecting the 3133 * validity of the results. 3134 * This method ensures that no source coordinates are 3135 * overwritten by a previous operation before they can be transformed. 3136 * The coordinates are stored in the arrays starting at the indicated 3137 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3138 * @param srcPts the array containing the source point coordinates. 3139 * Each point is stored as a pair of x, y coordinates. 3140 * @param dstPts the array into which the transformed point 3141 * coordinates are returned. Each point is stored as a pair of 3142 * x, y coordinates. 3143 * @param srcOff the offset to the first point to be transformed 3144 * in the source array 3145 * @param dstOff the offset to the location of the first 3146 * transformed point that is stored in the destination array 3147 * @param numPts the number of point objects to be transformed 3148 * @since 1.2 3149 */ 3150 public void transform(double[] srcPts, int srcOff, 3151 double[] dstPts, int dstOff, 3152 int numPts) { 3153 double M00, M01, M02, M10, M11, M12; // For caching 3154 if (dstPts == srcPts && 3155 dstOff > srcOff && dstOff < srcOff + numPts * 2) 3156 { 3157 // If the arrays overlap partially with the destination higher 3158 // than the source and we transform the coordinates normally 3159 // we would overwrite some of the later source coordinates 3160 // with results of previous transformations. 3161 // To get around this we use arraycopy to copy the points 3162 // to their final destination with correct overwrite 3163 // handling and then transform them in place in the new 3164 // safer location. 3165 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); 3166 // srcPts = dstPts; // They are known to be equal. 3167 srcOff = dstOff; 3168 } 3169 switch (state) { 3170 default: 3171 stateError(); 3172 /* NOTREACHED */ 3173 return; 3174 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3175 M00 = m00; M01 = m01; M02 = m02; 3176 M10 = m10; M11 = m11; M12 = m12; 3177 while (--numPts >= 0) { 3178 double x = srcPts[srcOff++]; 3179 double y = srcPts[srcOff++]; 3180 dstPts[dstOff++] = M00 * x + M01 * y + M02; 3181 dstPts[dstOff++] = M10 * x + M11 * y + M12; 3182 } 3183 return; 3184 case (APPLY_SHEAR | APPLY_SCALE): 3185 M00 = m00; M01 = m01; 3186 M10 = m10; M11 = m11; 3187 while (--numPts >= 0) { 3188 double x = srcPts[srcOff++]; 3189 double y = srcPts[srcOff++]; 3190 dstPts[dstOff++] = M00 * x + M01 * y; 3191 dstPts[dstOff++] = M10 * x + M11 * y; 3192 } 3193 return; 3194 case (APPLY_SHEAR | APPLY_TRANSLATE): 3195 M01 = m01; M02 = m02; 3196 M10 = m10; M12 = m12; 3197 while (--numPts >= 0) { 3198 double x = srcPts[srcOff++]; 3199 dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02; 3200 dstPts[dstOff++] = M10 * x + M12; 3201 } 3202 return; 3203 case (APPLY_SHEAR): 3204 M01 = m01; M10 = m10; 3205 while (--numPts >= 0) { 3206 double x = srcPts[srcOff++]; 3207 dstPts[dstOff++] = M01 * srcPts[srcOff++]; 3208 dstPts[dstOff++] = M10 * x; 3209 } 3210 return; 3211 case (APPLY_SCALE | APPLY_TRANSLATE): 3212 M00 = m00; M02 = m02; 3213 M11 = m11; M12 = m12; 3214 while (--numPts >= 0) { 3215 dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02; 3216 dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12; 3217 } 3218 return; 3219 case (APPLY_SCALE): 3220 M00 = m00; M11 = m11; 3221 while (--numPts >= 0) { 3222 dstPts[dstOff++] = M00 * srcPts[srcOff++]; 3223 dstPts[dstOff++] = M11 * srcPts[srcOff++]; 3224 } 3225 return; 3226 case (APPLY_TRANSLATE): 3227 M02 = m02; M12 = m12; 3228 while (--numPts >= 0) { 3229 dstPts[dstOff++] = srcPts[srcOff++] + M02; 3230 dstPts[dstOff++] = srcPts[srcOff++] + M12; 3231 } 3232 return; 3233 case (APPLY_IDENTITY): 3234 if (srcPts != dstPts || srcOff != dstOff) { 3235 System.arraycopy(srcPts, srcOff, dstPts, dstOff, 3236 numPts * 2); 3237 } 3238 return; 3239 } 3240 3241 /* NOTREACHED */ 3242 } 3243 3244 /** 3245 * Transforms an array of floating point coordinates by this transform 3246 * and stores the results into an array of doubles. 3247 * The coordinates are stored in the arrays starting at the specified 3248 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3249 * @param srcPts the array containing the source point coordinates. 3250 * Each point is stored as a pair of x, y coordinates. 3251 * @param dstPts the array into which the transformed point coordinates 3252 * are returned. Each point is stored as a pair of x, y 3253 * coordinates. 3254 * @param srcOff the offset to the first point to be transformed 3255 * in the source array 3256 * @param dstOff the offset to the location of the first 3257 * transformed point that is stored in the destination array 3258 * @param numPts the number of points to be transformed 3259 * @since 1.2 3260 */ 3261 public void transform(float[] srcPts, int srcOff, 3262 double[] dstPts, int dstOff, 3263 int numPts) { 3264 double M00, M01, M02, M10, M11, M12; // For caching 3265 switch (state) { 3266 default: 3267 stateError(); 3268 /* NOTREACHED */ 3269 return; 3270 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3271 M00 = m00; M01 = m01; M02 = m02; 3272 M10 = m10; M11 = m11; M12 = m12; 3273 while (--numPts >= 0) { 3274 double x = srcPts[srcOff++]; 3275 double y = srcPts[srcOff++]; 3276 dstPts[dstOff++] = M00 * x + M01 * y + M02; 3277 dstPts[dstOff++] = M10 * x + M11 * y + M12; 3278 } 3279 return; 3280 case (APPLY_SHEAR | APPLY_SCALE): 3281 M00 = m00; M01 = m01; 3282 M10 = m10; M11 = m11; 3283 while (--numPts >= 0) { 3284 double x = srcPts[srcOff++]; 3285 double y = srcPts[srcOff++]; 3286 dstPts[dstOff++] = M00 * x + M01 * y; 3287 dstPts[dstOff++] = M10 * x + M11 * y; 3288 } 3289 return; 3290 case (APPLY_SHEAR | APPLY_TRANSLATE): 3291 M01 = m01; M02 = m02; 3292 M10 = m10; M12 = m12; 3293 while (--numPts >= 0) { 3294 double x = srcPts[srcOff++]; 3295 dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02; 3296 dstPts[dstOff++] = M10 * x + M12; 3297 } 3298 return; 3299 case (APPLY_SHEAR): 3300 M01 = m01; M10 = m10; 3301 while (--numPts >= 0) { 3302 double x = srcPts[srcOff++]; 3303 dstPts[dstOff++] = M01 * srcPts[srcOff++]; 3304 dstPts[dstOff++] = M10 * x; 3305 } 3306 return; 3307 case (APPLY_SCALE | APPLY_TRANSLATE): 3308 M00 = m00; M02 = m02; 3309 M11 = m11; M12 = m12; 3310 while (--numPts >= 0) { 3311 dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02; 3312 dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12; 3313 } 3314 return; 3315 case (APPLY_SCALE): 3316 M00 = m00; M11 = m11; 3317 while (--numPts >= 0) { 3318 dstPts[dstOff++] = M00 * srcPts[srcOff++]; 3319 dstPts[dstOff++] = M11 * srcPts[srcOff++]; 3320 } 3321 return; 3322 case (APPLY_TRANSLATE): 3323 M02 = m02; M12 = m12; 3324 while (--numPts >= 0) { 3325 dstPts[dstOff++] = srcPts[srcOff++] + M02; 3326 dstPts[dstOff++] = srcPts[srcOff++] + M12; 3327 } 3328 return; 3329 case (APPLY_IDENTITY): 3330 while (--numPts >= 0) { 3331 dstPts[dstOff++] = srcPts[srcOff++]; 3332 dstPts[dstOff++] = srcPts[srcOff++]; 3333 } 3334 return; 3335 } 3336 3337 /* NOTREACHED */ 3338 } 3339 3340 /** 3341 * Transforms an array of double precision coordinates by this transform 3342 * and stores the results into an array of floats. 3343 * The coordinates are stored in the arrays starting at the specified 3344 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3345 * @param srcPts the array containing the source point coordinates. 3346 * Each point is stored as a pair of x, y coordinates. 3347 * @param dstPts the array into which the transformed point 3348 * coordinates are returned. Each point is stored as a pair of 3349 * x, y coordinates. 3350 * @param srcOff the offset to the first point to be transformed 3351 * in the source array 3352 * @param dstOff the offset to the location of the first 3353 * transformed point that is stored in the destination array 3354 * @param numPts the number of point objects to be transformed 3355 * @since 1.2 3356 */ 3357 public void transform(double[] srcPts, int srcOff, 3358 float[] dstPts, int dstOff, 3359 int numPts) { 3360 double M00, M01, M02, M10, M11, M12; // For caching 3361 switch (state) { 3362 default: 3363 stateError(); 3364 /* NOTREACHED */ 3365 return; 3366 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3367 M00 = m00; M01 = m01; M02 = m02; 3368 M10 = m10; M11 = m11; M12 = m12; 3369 while (--numPts >= 0) { 3370 double x = srcPts[srcOff++]; 3371 double y = srcPts[srcOff++]; 3372 dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02); 3373 dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12); 3374 } 3375 return; 3376 case (APPLY_SHEAR | APPLY_SCALE): 3377 M00 = m00; M01 = m01; 3378 M10 = m10; M11 = m11; 3379 while (--numPts >= 0) { 3380 double x = srcPts[srcOff++]; 3381 double y = srcPts[srcOff++]; 3382 dstPts[dstOff++] = (float) (M00 * x + M01 * y); 3383 dstPts[dstOff++] = (float) (M10 * x + M11 * y); 3384 } 3385 return; 3386 case (APPLY_SHEAR | APPLY_TRANSLATE): 3387 M01 = m01; M02 = m02; 3388 M10 = m10; M12 = m12; 3389 while (--numPts >= 0) { 3390 double x = srcPts[srcOff++]; 3391 dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02); 3392 dstPts[dstOff++] = (float) (M10 * x + M12); 3393 } 3394 return; 3395 case (APPLY_SHEAR): 3396 M01 = m01; M10 = m10; 3397 while (--numPts >= 0) { 3398 double x = srcPts[srcOff++]; 3399 dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]); 3400 dstPts[dstOff++] = (float) (M10 * x); 3401 } 3402 return; 3403 case (APPLY_SCALE | APPLY_TRANSLATE): 3404 M00 = m00; M02 = m02; 3405 M11 = m11; M12 = m12; 3406 while (--numPts >= 0) { 3407 dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02); 3408 dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12); 3409 } 3410 return; 3411 case (APPLY_SCALE): 3412 M00 = m00; M11 = m11; 3413 while (--numPts >= 0) { 3414 dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]); 3415 dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]); 3416 } 3417 return; 3418 case (APPLY_TRANSLATE): 3419 M02 = m02; M12 = m12; 3420 while (--numPts >= 0) { 3421 dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02); 3422 dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12); 3423 } 3424 return; 3425 case (APPLY_IDENTITY): 3426 while (--numPts >= 0) { 3427 dstPts[dstOff++] = (float) (srcPts[srcOff++]); 3428 dstPts[dstOff++] = (float) (srcPts[srcOff++]); 3429 } 3430 return; 3431 } 3432 3433 /* NOTREACHED */ 3434 } 3435 3436 /** 3437 * Inverse transforms the specified {@code ptSrc} and stores the 3438 * result in {@code ptDst}. 3439 * If {@code ptDst} is {@code null}, a new 3440 * {@code Point2D} object is allocated and then the result of the 3441 * transform is stored in this object. 3442 * In either case, {@code ptDst}, which contains the transformed 3443 * point, is returned for convenience. 3444 * If {@code ptSrc} and {@code ptDst} are the same 3445 * object, the input point is correctly overwritten with the 3446 * transformed point. 3447 * @param ptSrc the point to be inverse transformed 3448 * @param ptDst the resulting transformed point 3449 * @return {@code ptDst}, which contains the result of the 3450 * inverse transform. 3451 * @exception NoninvertibleTransformException if the matrix cannot be 3452 * inverted. 3453 * @since 1.2 3454 */ 3455 @SuppressWarnings("fallthrough") 3456 public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst) 3457 throws NoninvertibleTransformException 3458 { 3459 if (ptDst == null) { 3460 if (ptSrc instanceof Point2D.Double) { 3461 ptDst = new Point2D.Double(); 3462 } else { 3463 ptDst = new Point2D.Float(); 3464 } 3465 } 3466 // Copy source coords into local variables in case src == dst 3467 double x = ptSrc.getX(); 3468 double y = ptSrc.getY(); 3469 switch (state) { 3470 default: 3471 stateError(); 3472 /* NOTREACHED */ 3473 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3474 x -= m02; 3475 y -= m12; 3476 /* NOBREAK */ 3477 case (APPLY_SHEAR | APPLY_SCALE): 3478 double det = m00 * m11 - m01 * m10; 3479 if (Math.abs(det) <= Double.MIN_VALUE) { 3480 throw new NoninvertibleTransformException("Determinant is "+ 3481 det); 3482 } 3483 ptDst.setLocation((x * m11 - y * m01) / det, 3484 (y * m00 - x * m10) / det); 3485 return ptDst; 3486 case (APPLY_SHEAR | APPLY_TRANSLATE): 3487 x -= m02; 3488 y -= m12; 3489 /* NOBREAK */ 3490 case (APPLY_SHEAR): 3491 if (m01 == 0.0 || m10 == 0.0) { 3492 throw new NoninvertibleTransformException("Determinant is 0"); 3493 } 3494 ptDst.setLocation(y / m10, x / m01); 3495 return ptDst; 3496 case (APPLY_SCALE | APPLY_TRANSLATE): 3497 x -= m02; 3498 y -= m12; 3499 /* NOBREAK */ 3500 case (APPLY_SCALE): 3501 if (m00 == 0.0 || m11 == 0.0) { 3502 throw new NoninvertibleTransformException("Determinant is 0"); 3503 } 3504 ptDst.setLocation(x / m00, y / m11); 3505 return ptDst; 3506 case (APPLY_TRANSLATE): 3507 ptDst.setLocation(x - m02, y - m12); 3508 return ptDst; 3509 case (APPLY_IDENTITY): 3510 ptDst.setLocation(x, y); 3511 return ptDst; 3512 } 3513 3514 /* NOTREACHED */ 3515 } 3516 3517 /** 3518 * Inverse transforms an array of double precision coordinates by 3519 * this transform. 3520 * The two coordinate array sections can be exactly the same or 3521 * can be overlapping sections of the same array without affecting the 3522 * validity of the results. 3523 * This method ensures that no source coordinates are 3524 * overwritten by a previous operation before they can be transformed. 3525 * The coordinates are stored in the arrays starting at the specified 3526 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3527 * @param srcPts the array containing the source point coordinates. 3528 * Each point is stored as a pair of x, y coordinates. 3529 * @param dstPts the array into which the transformed point 3530 * coordinates are returned. Each point is stored as a pair of 3531 * x, y coordinates. 3532 * @param srcOff the offset to the first point to be transformed 3533 * in the source array 3534 * @param dstOff the offset to the location of the first 3535 * transformed point that is stored in the destination array 3536 * @param numPts the number of point objects to be transformed 3537 * @exception NoninvertibleTransformException if the matrix cannot be 3538 * inverted. 3539 * @since 1.2 3540 */ 3541 public void inverseTransform(double[] srcPts, int srcOff, 3542 double[] dstPts, int dstOff, 3543 int numPts) 3544 throws NoninvertibleTransformException 3545 { 3546 double M00, M01, M02, M10, M11, M12; // For caching 3547 double det; 3548 if (dstPts == srcPts && 3549 dstOff > srcOff && dstOff < srcOff + numPts * 2) 3550 { 3551 // If the arrays overlap partially with the destination higher 3552 // than the source and we transform the coordinates normally 3553 // we would overwrite some of the later source coordinates 3554 // with results of previous transformations. 3555 // To get around this we use arraycopy to copy the points 3556 // to their final destination with correct overwrite 3557 // handling and then transform them in place in the new 3558 // safer location. 3559 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); 3560 // srcPts = dstPts; // They are known to be equal. 3561 srcOff = dstOff; 3562 } 3563 switch (state) { 3564 default: 3565 stateError(); 3566 /* NOTREACHED */ 3567 return; 3568 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3569 M00 = m00; M01 = m01; M02 = m02; 3570 M10 = m10; M11 = m11; M12 = m12; 3571 det = M00 * M11 - M01 * M10; 3572 if (Math.abs(det) <= Double.MIN_VALUE) { 3573 throw new NoninvertibleTransformException("Determinant is "+ 3574 det); 3575 } 3576 while (--numPts >= 0) { 3577 double x = srcPts[srcOff++] - M02; 3578 double y = srcPts[srcOff++] - M12; 3579 dstPts[dstOff++] = (x * M11 - y * M01) / det; 3580 dstPts[dstOff++] = (y * M00 - x * M10) / det; 3581 } 3582 return; 3583 case (APPLY_SHEAR | APPLY_SCALE): 3584 M00 = m00; M01 = m01; 3585 M10 = m10; M11 = m11; 3586 det = M00 * M11 - M01 * M10; 3587 if (Math.abs(det) <= Double.MIN_VALUE) { 3588 throw new NoninvertibleTransformException("Determinant is "+ 3589 det); 3590 } 3591 while (--numPts >= 0) { 3592 double x = srcPts[srcOff++]; 3593 double y = srcPts[srcOff++]; 3594 dstPts[dstOff++] = (x * M11 - y * M01) / det; 3595 dstPts[dstOff++] = (y * M00 - x * M10) / det; 3596 } 3597 return; 3598 case (APPLY_SHEAR | APPLY_TRANSLATE): 3599 M01 = m01; M02 = m02; 3600 M10 = m10; M12 = m12; 3601 if (M01 == 0.0 || M10 == 0.0) { 3602 throw new NoninvertibleTransformException("Determinant is 0"); 3603 } 3604 while (--numPts >= 0) { 3605 double x = srcPts[srcOff++] - M02; 3606 dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10; 3607 dstPts[dstOff++] = x / M01; 3608 } 3609 return; 3610 case (APPLY_SHEAR): 3611 M01 = m01; M10 = m10; 3612 if (M01 == 0.0 || M10 == 0.0) { 3613 throw new NoninvertibleTransformException("Determinant is 0"); 3614 } 3615 while (--numPts >= 0) { 3616 double x = srcPts[srcOff++]; 3617 dstPts[dstOff++] = srcPts[srcOff++] / M10; 3618 dstPts[dstOff++] = x / M01; 3619 } 3620 return; 3621 case (APPLY_SCALE | APPLY_TRANSLATE): 3622 M00 = m00; M02 = m02; 3623 M11 = m11; M12 = m12; 3624 if (M00 == 0.0 || M11 == 0.0) { 3625 throw new NoninvertibleTransformException("Determinant is 0"); 3626 } 3627 while (--numPts >= 0) { 3628 dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00; 3629 dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11; 3630 } 3631 return; 3632 case (APPLY_SCALE): 3633 M00 = m00; M11 = m11; 3634 if (M00 == 0.0 || M11 == 0.0) { 3635 throw new NoninvertibleTransformException("Determinant is 0"); 3636 } 3637 while (--numPts >= 0) { 3638 dstPts[dstOff++] = srcPts[srcOff++] / M00; 3639 dstPts[dstOff++] = srcPts[srcOff++] / M11; 3640 } 3641 return; 3642 case (APPLY_TRANSLATE): 3643 M02 = m02; M12 = m12; 3644 while (--numPts >= 0) { 3645 dstPts[dstOff++] = srcPts[srcOff++] - M02; 3646 dstPts[dstOff++] = srcPts[srcOff++] - M12; 3647 } 3648 return; 3649 case (APPLY_IDENTITY): 3650 if (srcPts != dstPts || srcOff != dstOff) { 3651 System.arraycopy(srcPts, srcOff, dstPts, dstOff, 3652 numPts * 2); 3653 } 3654 return; 3655 } 3656 3657 /* NOTREACHED */ 3658 } 3659 3660 /** 3661 * Transforms the relative distance vector specified by 3662 * {@code ptSrc} and stores the result in {@code ptDst}. 3663 * A relative distance vector is transformed without applying the 3664 * translation components of the affine transformation matrix 3665 * using the following equations: 3666 * <pre> 3667 * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] 3668 * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] 3669 * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] 3670 * </pre> 3671 * If {@code ptDst} is {@code null}, a new 3672 * {@code Point2D} object is allocated and then the result of the 3673 * transform is stored in this object. 3674 * In either case, {@code ptDst}, which contains the 3675 * transformed point, is returned for convenience. 3676 * If {@code ptSrc} and {@code ptDst} are the same object, 3677 * the input point is correctly overwritten with the transformed 3678 * point. 3679 * @param ptSrc the distance vector to be delta transformed 3680 * @param ptDst the resulting transformed distance vector 3681 * @return {@code ptDst}, which contains the result of the 3682 * transformation. 3683 * @since 1.2 3684 */ 3685 public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) { 3686 if (ptDst == null) { 3687 if (ptSrc instanceof Point2D.Double) { 3688 ptDst = new Point2D.Double(); 3689 } else { 3690 ptDst = new Point2D.Float(); 3691 } 3692 } 3693 // Copy source coords into local variables in case src == dst 3694 double x = ptSrc.getX(); 3695 double y = ptSrc.getY(); 3696 switch (state) { 3697 default: 3698 stateError(); 3699 /* NOTREACHED */ 3700 return null; 3701 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3702 case (APPLY_SHEAR | APPLY_SCALE): 3703 ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11); 3704 return ptDst; 3705 case (APPLY_SHEAR | APPLY_TRANSLATE): 3706 case (APPLY_SHEAR): 3707 ptDst.setLocation(y * m01, x * m10); 3708 return ptDst; 3709 case (APPLY_SCALE | APPLY_TRANSLATE): 3710 case (APPLY_SCALE): 3711 ptDst.setLocation(x * m00, y * m11); 3712 return ptDst; 3713 case (APPLY_TRANSLATE): 3714 case (APPLY_IDENTITY): 3715 ptDst.setLocation(x, y); 3716 return ptDst; 3717 } 3718 3719 /* NOTREACHED */ 3720 } 3721 3722 /** 3723 * Transforms an array of relative distance vectors by this 3724 * transform. 3725 * A relative distance vector is transformed without applying the 3726 * translation components of the affine transformation matrix 3727 * using the following equations: 3728 * <pre> 3729 * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] 3730 * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] 3731 * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] 3732 * </pre> 3733 * The two coordinate array sections can be exactly the same or 3734 * can be overlapping sections of the same array without affecting the 3735 * validity of the results. 3736 * This method ensures that no source coordinates are 3737 * overwritten by a previous operation before they can be transformed. 3738 * The coordinates are stored in the arrays starting at the indicated 3739 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3740 * @param srcPts the array containing the source distance vectors. 3741 * Each vector is stored as a pair of relative x, y coordinates. 3742 * @param dstPts the array into which the transformed distance vectors 3743 * are returned. Each vector is stored as a pair of relative 3744 * x, y coordinates. 3745 * @param srcOff the offset to the first vector to be transformed 3746 * in the source array 3747 * @param dstOff the offset to the location of the first 3748 * transformed vector that is stored in the destination array 3749 * @param numPts the number of vector coordinate pairs to be 3750 * transformed 3751 * @since 1.2 3752 */ 3753 public void deltaTransform(double[] srcPts, int srcOff, 3754 double[] dstPts, int dstOff, 3755 int numPts) { 3756 double M00, M01, M10, M11; // For caching 3757 if (dstPts == srcPts && 3758 dstOff > srcOff && dstOff < srcOff + numPts * 2) 3759 { 3760 // If the arrays overlap partially with the destination higher 3761 // than the source and we transform the coordinates normally 3762 // we would overwrite some of the later source coordinates 3763 // with results of previous transformations. 3764 // To get around this we use arraycopy to copy the points 3765 // to their final destination with correct overwrite 3766 // handling and then transform them in place in the new 3767 // safer location. 3768 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); 3769 // srcPts = dstPts; // They are known to be equal. 3770 srcOff = dstOff; 3771 } 3772 switch (state) { 3773 default: 3774 stateError(); 3775 /* NOTREACHED */ 3776 return; 3777 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3778 case (APPLY_SHEAR | APPLY_SCALE): 3779 M00 = m00; M01 = m01; 3780 M10 = m10; M11 = m11; 3781 while (--numPts >= 0) { 3782 double x = srcPts[srcOff++]; 3783 double y = srcPts[srcOff++]; 3784 dstPts[dstOff++] = x * M00 + y * M01; 3785 dstPts[dstOff++] = x * M10 + y * M11; 3786 } 3787 return; 3788 case (APPLY_SHEAR | APPLY_TRANSLATE): 3789 case (APPLY_SHEAR): 3790 M01 = m01; M10 = m10; 3791 while (--numPts >= 0) { 3792 double x = srcPts[srcOff++]; 3793 dstPts[dstOff++] = srcPts[srcOff++] * M01; 3794 dstPts[dstOff++] = x * M10; 3795 } 3796 return; 3797 case (APPLY_SCALE | APPLY_TRANSLATE): 3798 case (APPLY_SCALE): 3799 M00 = m00; M11 = m11; 3800 while (--numPts >= 0) { 3801 dstPts[dstOff++] = srcPts[srcOff++] * M00; 3802 dstPts[dstOff++] = srcPts[srcOff++] * M11; 3803 } 3804 return; 3805 case (APPLY_TRANSLATE): 3806 case (APPLY_IDENTITY): 3807 if (srcPts != dstPts || srcOff != dstOff) { 3808 System.arraycopy(srcPts, srcOff, dstPts, dstOff, 3809 numPts * 2); 3810 } 3811 return; 3812 } 3813 3814 /* NOTREACHED */ 3815 } 3816 3817 /** 3818 * Returns a new {@link Shape} object defined by the geometry of the 3819 * specified {@code Shape} after it has been transformed by 3820 * this transform. 3821 * @param pSrc the specified {@code Shape} object to be 3822 * transformed by this transform. 3823 * @return a new {@code Shape} object that defines the geometry 3824 * of the transformed {@code Shape}, or null if {@code pSrc} is null. 3825 * @since 1.2 3826 */ 3827 public Shape createTransformedShape(Shape pSrc) { 3828 if (pSrc == null) { 3829 return null; 3830 } 3831 return new Path2D.Double(pSrc, this); 3832 } 3833 3834 // Round values to sane precision for printing 3835 // Note that Math.sin(Math.PI) has an error of about 10^-16 3836 private static double _matround(double matval) { 3837 return Math.rint(matval * 1E15) / 1E15; 3838 } 3839 3840 /** 3841 * Returns a {@code String} that represents the value of this 3842 * {@link Object}. 3843 * @return a {@code String} representing the value of this 3844 * {@code Object}. 3845 * @since 1.2 3846 */ 3847 public String toString() { 3848 return ("AffineTransform[[" 3849 + _matround(m00) + ", " 3850 + _matround(m01) + ", " 3851 + _matround(m02) + "], [" 3852 + _matround(m10) + ", " 3853 + _matround(m11) + ", " 3854 + _matround(m12) + "]]"); 3855 } 3856 3857 /** 3858 * Returns {@code true} if this {@code AffineTransform} is 3859 * an identity transform. 3860 * @return {@code true} if this {@code AffineTransform} is 3861 * an identity transform; {@code false} otherwise. 3862 * @since 1.2 3863 */ 3864 public boolean isIdentity() { 3865 return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY)); 3866 } 3867 3868 /** 3869 * Returns a copy of this {@code AffineTransform} object. 3870 * @return an {@code Object} that is a copy of this 3871 * {@code AffineTransform} object. 3872 * @since 1.2 3873 */ 3874 public Object clone() { 3875 try { 3876 return super.clone(); 3877 } catch (CloneNotSupportedException e) { 3878 // this shouldn't happen, since we are Cloneable 3879 throw new InternalError(e); 3880 } 3881 } 3882 3883 /** 3884 * Returns the hashcode for this transform. 3885 * @return a hash code for this transform. 3886 * @since 1.2 3887 */ 3888 public int hashCode() { 3889 long bits = Double.doubleToLongBits(m00); 3890 bits = bits * 31 + Double.doubleToLongBits(m01); 3891 bits = bits * 31 + Double.doubleToLongBits(m02); 3892 bits = bits * 31 + Double.doubleToLongBits(m10); 3893 bits = bits * 31 + Double.doubleToLongBits(m11); 3894 bits = bits * 31 + Double.doubleToLongBits(m12); 3895 return (((int) bits) ^ ((int) (bits >> 32))); 3896 } 3897 3898 /** 3899 * Returns {@code true} if this {@code AffineTransform} 3900 * represents the same affine coordinate transform as the specified 3901 * argument. 3902 * @param obj the {@code Object} to test for equality with this 3903 * {@code AffineTransform} 3904 * @return {@code true} if {@code obj} equals this 3905 * {@code AffineTransform} object; {@code false} otherwise. 3906 * @since 1.2 3907 */ 3908 public boolean equals(Object obj) { 3909 if (!(obj instanceof AffineTransform)) { 3910 return false; 3911 } 3912 3913 AffineTransform a = (AffineTransform)obj; 3914 3915 return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) && 3916 (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12)); 3917 } 3918 3919 /* Serialization support. A readObject method is neccessary because 3920 * the state field is part of the implementation of this particular 3921 * AffineTransform and not part of the public specification. The 3922 * state variable's value needs to be recalculated on the fly by the 3923 * readObject method as it is in the 6-argument matrix constructor. 3924 */ 3925 3926 /* 3927 * JDK 1.2 serialVersionUID 3928 */ 3929 private static final long serialVersionUID = 1330973210523860834L; 3930 3931 private void writeObject(java.io.ObjectOutputStream s) 3932 throws java.lang.ClassNotFoundException, java.io.IOException 3933 { 3934 s.defaultWriteObject(); 3935 } 3936 3937 private void readObject(java.io.ObjectInputStream s) 3938 throws java.lang.ClassNotFoundException, java.io.IOException 3939 { 3940 s.defaultReadObject(); 3941 updateState(); 3942 } 3943 }