1 /* 2 * Copyright (c) 1996, 2017, 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 id="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 {@code m00} element of the 3x3 affine transformation matrix. 1164 * This matrix factor determines how input X coordinates will affect output 1165 * X coordinates and is one element of the scale of the transform. 1166 * To measure the full amount by which X coordinates are stretched or 1167 * contracted by this transform, use the following code: 1168 * <pre> 1169 * Point2D p = new Point2D.Double(1, 0); 1170 * p = tx.deltaTransform(p, p); 1171 * double scaleX = p.distance(0, 0); 1172 * </pre> 1173 * @return a double value that is {@code m00} element of the 1174 * 3x3 affine transformation matrix. 1175 * @see #getMatrix 1176 * @since 1.2 1177 */ 1178 public double getScaleX() { 1179 return m00; 1180 } 1181 1182 /** 1183 * Returns the {@code m11} element of the 3x3 affine transformation matrix. 1184 * This matrix factor determines how input Y coordinates will affect output 1185 * Y coordinates and is one element of the scale of the transform. 1186 * To measure the full amount by which Y coordinates are stretched or 1187 * contracted by this transform, use the following code: 1188 * <pre> 1189 * Point2D p = new Point2D.Double(0, 1); 1190 * p = tx.deltaTransform(p, p); 1191 * double scaleY = p.distance(0, 0); 1192 * </pre> 1193 * @return a double value that is {@code m11} element of the 1194 * 3x3 affine transformation matrix. 1195 * @see #getMatrix 1196 * @since 1.2 1197 */ 1198 public double getScaleY() { 1199 return m11; 1200 } 1201 1202 /** 1203 * Returns the X coordinate shearing element (m01) of the 3x3 1204 * affine transformation matrix. 1205 * @return a double value that is the X coordinate of the shearing 1206 * element of the affine transformation matrix. 1207 * @see #getMatrix 1208 * @since 1.2 1209 */ 1210 public double getShearX() { 1211 return m01; 1212 } 1213 1214 /** 1215 * Returns the Y coordinate shearing element (m10) of the 3x3 1216 * affine transformation matrix. 1217 * @return a double value that is the Y coordinate of the shearing 1218 * element of the affine transformation matrix. 1219 * @see #getMatrix 1220 * @since 1.2 1221 */ 1222 public double getShearY() { 1223 return m10; 1224 } 1225 1226 /** 1227 * Returns the X coordinate of the translation element (m02) of the 1228 * 3x3 affine transformation matrix. 1229 * @return a double value that is the X coordinate of the translation 1230 * element of the affine transformation matrix. 1231 * @see #getMatrix 1232 * @since 1.2 1233 */ 1234 public double getTranslateX() { 1235 return m02; 1236 } 1237 1238 /** 1239 * Returns the Y coordinate of the translation element (m12) of the 1240 * 3x3 affine transformation matrix. 1241 * @return a double value that is the Y coordinate of the translation 1242 * element of the affine transformation matrix. 1243 * @see #getMatrix 1244 * @since 1.2 1245 */ 1246 public double getTranslateY() { 1247 return m12; 1248 } 1249 1250 /** 1251 * Concatenates this transform with a translation transformation. 1252 * This is equivalent to calling concatenate(T), where T is an 1253 * {@code AffineTransform} represented by the following matrix: 1254 * <pre> 1255 * [ 1 0 tx ] 1256 * [ 0 1 ty ] 1257 * [ 0 0 1 ] 1258 * </pre> 1259 * @param tx the distance by which coordinates are translated in the 1260 * X axis direction 1261 * @param ty the distance by which coordinates are translated in the 1262 * Y axis direction 1263 * @since 1.2 1264 */ 1265 public void translate(double tx, double ty) { 1266 switch (state) { 1267 default: 1268 stateError(); 1269 /* NOTREACHED */ 1270 return; 1271 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 1272 m02 = tx * m00 + ty * m01 + m02; 1273 m12 = tx * m10 + ty * m11 + m12; 1274 if (m02 == 0.0 && m12 == 0.0) { 1275 state = APPLY_SHEAR | APPLY_SCALE; 1276 if (type != TYPE_UNKNOWN) { 1277 type -= TYPE_TRANSLATION; 1278 } 1279 } 1280 return; 1281 case (APPLY_SHEAR | APPLY_SCALE): 1282 m02 = tx * m00 + ty * m01; 1283 m12 = tx * m10 + ty * m11; 1284 if (m02 != 0.0 || m12 != 0.0) { 1285 state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE; 1286 type |= TYPE_TRANSLATION; 1287 } 1288 return; 1289 case (APPLY_SHEAR | APPLY_TRANSLATE): 1290 m02 = ty * m01 + m02; 1291 m12 = tx * m10 + m12; 1292 if (m02 == 0.0 && m12 == 0.0) { 1293 state = APPLY_SHEAR; 1294 if (type != TYPE_UNKNOWN) { 1295 type -= TYPE_TRANSLATION; 1296 } 1297 } 1298 return; 1299 case (APPLY_SHEAR): 1300 m02 = ty * m01; 1301 m12 = tx * m10; 1302 if (m02 != 0.0 || m12 != 0.0) { 1303 state = APPLY_SHEAR | APPLY_TRANSLATE; 1304 type |= TYPE_TRANSLATION; 1305 } 1306 return; 1307 case (APPLY_SCALE | APPLY_TRANSLATE): 1308 m02 = tx * m00 + m02; 1309 m12 = ty * m11 + m12; 1310 if (m02 == 0.0 && m12 == 0.0) { 1311 state = APPLY_SCALE; 1312 if (type != TYPE_UNKNOWN) { 1313 type -= TYPE_TRANSLATION; 1314 } 1315 } 1316 return; 1317 case (APPLY_SCALE): 1318 m02 = tx * m00; 1319 m12 = ty * m11; 1320 if (m02 != 0.0 || m12 != 0.0) { 1321 state = APPLY_SCALE | APPLY_TRANSLATE; 1322 type |= TYPE_TRANSLATION; 1323 } 1324 return; 1325 case (APPLY_TRANSLATE): 1326 m02 = tx + m02; 1327 m12 = ty + m12; 1328 if (m02 == 0.0 && m12 == 0.0) { 1329 state = APPLY_IDENTITY; 1330 type = TYPE_IDENTITY; 1331 } 1332 return; 1333 case (APPLY_IDENTITY): 1334 m02 = tx; 1335 m12 = ty; 1336 if (tx != 0.0 || ty != 0.0) { 1337 state = APPLY_TRANSLATE; 1338 type = TYPE_TRANSLATION; 1339 } 1340 return; 1341 } 1342 } 1343 1344 // Utility methods to optimize rotate methods. 1345 // These tables translate the flags during predictable quadrant 1346 // rotations where the shear and scale values are swapped and negated. 1347 private static final int rot90conversion[] = { 1348 /* IDENTITY => */ APPLY_SHEAR, 1349 /* TRANSLATE (TR) => */ APPLY_SHEAR | APPLY_TRANSLATE, 1350 /* SCALE (SC) => */ APPLY_SHEAR, 1351 /* SC | TR => */ APPLY_SHEAR | APPLY_TRANSLATE, 1352 /* SHEAR (SH) => */ APPLY_SCALE, 1353 /* SH | TR => */ APPLY_SCALE | APPLY_TRANSLATE, 1354 /* SH | SC => */ APPLY_SHEAR | APPLY_SCALE, 1355 /* SH | SC | TR => */ APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE, 1356 }; 1357 private void rotate90() { 1358 double M0 = m00; 1359 m00 = m01; 1360 m01 = -M0; 1361 M0 = m10; 1362 m10 = m11; 1363 m11 = -M0; 1364 int state = rot90conversion[this.state]; 1365 if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && 1366 m00 == 1.0 && m11 == 1.0) 1367 { 1368 state -= APPLY_SCALE; 1369 } 1370 this.state = state; 1371 type = TYPE_UNKNOWN; 1372 } 1373 private void rotate180() { 1374 m00 = -m00; 1375 m11 = -m11; 1376 int state = this.state; 1377 if ((state & (APPLY_SHEAR)) != 0) { 1378 // If there was a shear, then this rotation has no 1379 // effect on the state. 1380 m01 = -m01; 1381 m10 = -m10; 1382 } else { 1383 // No shear means the SCALE state may toggle when 1384 // m00 and m11 are negated. 1385 if (m00 == 1.0 && m11 == 1.0) { 1386 this.state = state & ~APPLY_SCALE; 1387 } else { 1388 this.state = state | APPLY_SCALE; 1389 } 1390 } 1391 type = TYPE_UNKNOWN; 1392 } 1393 private void rotate270() { 1394 double M0 = m00; 1395 m00 = -m01; 1396 m01 = M0; 1397 M0 = m10; 1398 m10 = -m11; 1399 m11 = M0; 1400 int state = rot90conversion[this.state]; 1401 if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && 1402 m00 == 1.0 && m11 == 1.0) 1403 { 1404 state -= APPLY_SCALE; 1405 } 1406 this.state = state; 1407 type = TYPE_UNKNOWN; 1408 } 1409 1410 /** 1411 * Concatenates this transform with a rotation transformation. 1412 * This is equivalent to calling concatenate(R), where R is an 1413 * {@code AffineTransform} represented by the following matrix: 1414 * <pre> 1415 * [ cos(theta) -sin(theta) 0 ] 1416 * [ sin(theta) cos(theta) 0 ] 1417 * [ 0 0 1 ] 1418 * </pre> 1419 * Rotating by a positive angle theta rotates points on the positive 1420 * X axis toward the positive Y axis. 1421 * Note also the discussion of 1422 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 1423 * above. 1424 * @param theta the angle of rotation measured in radians 1425 * @since 1.2 1426 */ 1427 public void rotate(double theta) { 1428 double sin = Math.sin(theta); 1429 if (sin == 1.0) { 1430 rotate90(); 1431 } else if (sin == -1.0) { 1432 rotate270(); 1433 } else { 1434 double cos = Math.cos(theta); 1435 if (cos == -1.0) { 1436 rotate180(); 1437 } else if (cos != 1.0) { 1438 double M0, M1; 1439 M0 = m00; 1440 M1 = m01; 1441 m00 = cos * M0 + sin * M1; 1442 m01 = -sin * M0 + cos * M1; 1443 M0 = m10; 1444 M1 = m11; 1445 m10 = cos * M0 + sin * M1; 1446 m11 = -sin * M0 + cos * M1; 1447 updateState(); 1448 } 1449 } 1450 } 1451 1452 /** 1453 * Concatenates this transform with a transform that rotates 1454 * coordinates around an anchor point. 1455 * This operation is equivalent to translating the coordinates so 1456 * that the anchor point is at the origin (S1), then rotating them 1457 * about the new origin (S2), and finally translating so that the 1458 * intermediate origin is restored to the coordinates of the original 1459 * anchor point (S3). 1460 * <p> 1461 * This operation is equivalent to the following sequence of calls: 1462 * <pre> 1463 * translate(anchorx, anchory); // S3: final translation 1464 * rotate(theta); // S2: rotate around anchor 1465 * translate(-anchorx, -anchory); // S1: translate anchor to origin 1466 * </pre> 1467 * Rotating by a positive angle theta rotates points on the positive 1468 * X axis toward the positive Y axis. 1469 * Note also the discussion of 1470 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 1471 * above. 1472 * 1473 * @param theta the angle of rotation measured in radians 1474 * @param anchorx the X coordinate of the rotation anchor point 1475 * @param anchory the Y coordinate of the rotation anchor point 1476 * @since 1.2 1477 */ 1478 public void rotate(double theta, double anchorx, double anchory) { 1479 // REMIND: Simple for now - optimize later 1480 translate(anchorx, anchory); 1481 rotate(theta); 1482 translate(-anchorx, -anchory); 1483 } 1484 1485 /** 1486 * Concatenates this transform with a transform that rotates 1487 * coordinates according to a rotation vector. 1488 * All coordinates rotate about the origin by the same amount. 1489 * The amount of rotation is such that coordinates along the former 1490 * positive X axis will subsequently align with the vector pointing 1491 * from the origin to the specified vector coordinates. 1492 * If both {@code vecx} and {@code vecy} are 0.0, 1493 * no additional rotation is added to this transform. 1494 * This operation is equivalent to calling: 1495 * <pre> 1496 * rotate(Math.atan2(vecy, vecx)); 1497 * </pre> 1498 * 1499 * @param vecx the X coordinate of the rotation vector 1500 * @param vecy the Y coordinate of the rotation vector 1501 * @since 1.6 1502 */ 1503 public void rotate(double vecx, double vecy) { 1504 if (vecy == 0.0) { 1505 if (vecx < 0.0) { 1506 rotate180(); 1507 } 1508 // If vecx > 0.0 - no rotation 1509 // If vecx == 0.0 - undefined rotation - treat as no rotation 1510 } else if (vecx == 0.0) { 1511 if (vecy > 0.0) { 1512 rotate90(); 1513 } else { // vecy must be < 0.0 1514 rotate270(); 1515 } 1516 } else { 1517 double len = Math.sqrt(vecx * vecx + vecy * vecy); 1518 double sin = vecy / len; 1519 double cos = vecx / len; 1520 double M0, M1; 1521 M0 = m00; 1522 M1 = m01; 1523 m00 = cos * M0 + sin * M1; 1524 m01 = -sin * M0 + cos * M1; 1525 M0 = m10; 1526 M1 = m11; 1527 m10 = cos * M0 + sin * M1; 1528 m11 = -sin * M0 + cos * M1; 1529 updateState(); 1530 } 1531 } 1532 1533 /** 1534 * Concatenates this transform with a transform that rotates 1535 * coordinates around an anchor point according to a rotation 1536 * vector. 1537 * All coordinates rotate about the specified anchor coordinates 1538 * by the same amount. 1539 * The amount of rotation is such that coordinates along the former 1540 * positive X axis will subsequently align with the vector pointing 1541 * from the origin to the specified vector coordinates. 1542 * If both {@code vecx} and {@code vecy} are 0.0, 1543 * the transform is not modified in any way. 1544 * This method is equivalent to calling: 1545 * <pre> 1546 * rotate(Math.atan2(vecy, vecx), anchorx, anchory); 1547 * </pre> 1548 * 1549 * @param vecx the X coordinate of the rotation vector 1550 * @param vecy the Y coordinate of the rotation vector 1551 * @param anchorx the X coordinate of the rotation anchor point 1552 * @param anchory the Y coordinate of the rotation anchor point 1553 * @since 1.6 1554 */ 1555 public void rotate(double vecx, double vecy, 1556 double anchorx, double anchory) 1557 { 1558 // REMIND: Simple for now - optimize later 1559 translate(anchorx, anchory); 1560 rotate(vecx, vecy); 1561 translate(-anchorx, -anchory); 1562 } 1563 1564 /** 1565 * Concatenates this transform with a transform that rotates 1566 * coordinates by the specified number of quadrants. 1567 * This is equivalent to calling: 1568 * <pre> 1569 * rotate(numquadrants * Math.PI / 2.0); 1570 * </pre> 1571 * Rotating by a positive number of quadrants rotates points on 1572 * the positive X axis toward the positive Y axis. 1573 * @param numquadrants the number of 90 degree arcs to rotate by 1574 * @since 1.6 1575 */ 1576 public void quadrantRotate(int numquadrants) { 1577 switch (numquadrants & 3) { 1578 case 0: 1579 break; 1580 case 1: 1581 rotate90(); 1582 break; 1583 case 2: 1584 rotate180(); 1585 break; 1586 case 3: 1587 rotate270(); 1588 break; 1589 } 1590 } 1591 1592 /** 1593 * Concatenates this transform with a transform that rotates 1594 * coordinates by the specified number of quadrants around 1595 * the specified anchor point. 1596 * This method is equivalent to calling: 1597 * <pre> 1598 * rotate(numquadrants * Math.PI / 2.0, anchorx, anchory); 1599 * </pre> 1600 * Rotating by a positive number of quadrants rotates points on 1601 * the positive X axis toward the positive Y axis. 1602 * 1603 * @param numquadrants the number of 90 degree arcs to rotate by 1604 * @param anchorx the X coordinate of the rotation anchor point 1605 * @param anchory the Y coordinate of the rotation anchor point 1606 * @since 1.6 1607 */ 1608 public void quadrantRotate(int numquadrants, 1609 double anchorx, double anchory) 1610 { 1611 switch (numquadrants & 3) { 1612 case 0: 1613 return; 1614 case 1: 1615 m02 += anchorx * (m00 - m01) + anchory * (m01 + m00); 1616 m12 += anchorx * (m10 - m11) + anchory * (m11 + m10); 1617 rotate90(); 1618 break; 1619 case 2: 1620 m02 += anchorx * (m00 + m00) + anchory * (m01 + m01); 1621 m12 += anchorx * (m10 + m10) + anchory * (m11 + m11); 1622 rotate180(); 1623 break; 1624 case 3: 1625 m02 += anchorx * (m00 + m01) + anchory * (m01 - m00); 1626 m12 += anchorx * (m10 + m11) + anchory * (m11 - m10); 1627 rotate270(); 1628 break; 1629 } 1630 if (m02 == 0.0 && m12 == 0.0) { 1631 state &= ~APPLY_TRANSLATE; 1632 } else { 1633 state |= APPLY_TRANSLATE; 1634 } 1635 } 1636 1637 /** 1638 * Concatenates this transform with a scaling transformation. 1639 * This is equivalent to calling concatenate(S), where S is an 1640 * {@code AffineTransform} represented by the following matrix: 1641 * <pre> 1642 * [ sx 0 0 ] 1643 * [ 0 sy 0 ] 1644 * [ 0 0 1 ] 1645 * </pre> 1646 * @param sx the factor by which coordinates are scaled along the 1647 * X axis direction 1648 * @param sy the factor by which coordinates are scaled along the 1649 * Y axis direction 1650 * @since 1.2 1651 */ 1652 @SuppressWarnings("fallthrough") 1653 public void scale(double sx, double sy) { 1654 int state = this.state; 1655 switch (state) { 1656 default: 1657 stateError(); 1658 /* NOTREACHED */ 1659 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 1660 case (APPLY_SHEAR | APPLY_SCALE): 1661 m00 *= sx; 1662 m11 *= sy; 1663 /* NOBREAK */ 1664 case (APPLY_SHEAR | APPLY_TRANSLATE): 1665 case (APPLY_SHEAR): 1666 m01 *= sy; 1667 m10 *= sx; 1668 if (m01 == 0 && m10 == 0) { 1669 state &= APPLY_TRANSLATE; 1670 if (m00 == 1.0 && m11 == 1.0) { 1671 this.type = (state == APPLY_IDENTITY 1672 ? TYPE_IDENTITY 1673 : TYPE_TRANSLATION); 1674 } else { 1675 state |= APPLY_SCALE; 1676 this.type = TYPE_UNKNOWN; 1677 } 1678 this.state = state; 1679 } 1680 return; 1681 case (APPLY_SCALE | APPLY_TRANSLATE): 1682 case (APPLY_SCALE): 1683 m00 *= sx; 1684 m11 *= sy; 1685 if (m00 == 1.0 && m11 == 1.0) { 1686 this.state = (state &= APPLY_TRANSLATE); 1687 this.type = (state == APPLY_IDENTITY 1688 ? TYPE_IDENTITY 1689 : TYPE_TRANSLATION); 1690 } else { 1691 this.type = TYPE_UNKNOWN; 1692 } 1693 return; 1694 case (APPLY_TRANSLATE): 1695 case (APPLY_IDENTITY): 1696 m00 = sx; 1697 m11 = sy; 1698 if (sx != 1.0 || sy != 1.0) { 1699 this.state = state | APPLY_SCALE; 1700 this.type = TYPE_UNKNOWN; 1701 } 1702 return; 1703 } 1704 } 1705 1706 /** 1707 * Concatenates this transform with a shearing transformation. 1708 * This is equivalent to calling concatenate(SH), where SH is an 1709 * {@code AffineTransform} represented by the following matrix: 1710 * <pre> 1711 * [ 1 shx 0 ] 1712 * [ shy 1 0 ] 1713 * [ 0 0 1 ] 1714 * </pre> 1715 * @param shx the multiplier by which coordinates are shifted in the 1716 * direction of the positive X axis as a factor of their Y coordinate 1717 * @param shy the multiplier by which coordinates are shifted in the 1718 * direction of the positive Y axis as a factor of their X coordinate 1719 * @since 1.2 1720 */ 1721 public void shear(double shx, double shy) { 1722 int state = this.state; 1723 switch (state) { 1724 default: 1725 stateError(); 1726 /* NOTREACHED */ 1727 return; 1728 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 1729 case (APPLY_SHEAR | APPLY_SCALE): 1730 double M0, M1; 1731 M0 = m00; 1732 M1 = m01; 1733 m00 = M0 + M1 * shy; 1734 m01 = M0 * shx + M1; 1735 1736 M0 = m10; 1737 M1 = m11; 1738 m10 = M0 + M1 * shy; 1739 m11 = M0 * shx + M1; 1740 updateState(); 1741 return; 1742 case (APPLY_SHEAR | APPLY_TRANSLATE): 1743 case (APPLY_SHEAR): 1744 m00 = m01 * shy; 1745 m11 = m10 * shx; 1746 if (m00 != 0.0 || m11 != 0.0) { 1747 this.state = state | APPLY_SCALE; 1748 } 1749 this.type = TYPE_UNKNOWN; 1750 return; 1751 case (APPLY_SCALE | APPLY_TRANSLATE): 1752 case (APPLY_SCALE): 1753 m01 = m00 * shx; 1754 m10 = m11 * shy; 1755 if (m01 != 0.0 || m10 != 0.0) { 1756 this.state = state | APPLY_SHEAR; 1757 } 1758 this.type = TYPE_UNKNOWN; 1759 return; 1760 case (APPLY_TRANSLATE): 1761 case (APPLY_IDENTITY): 1762 m01 = shx; 1763 m10 = shy; 1764 if (m01 != 0.0 || m10 != 0.0) { 1765 this.state = state | APPLY_SCALE | APPLY_SHEAR; 1766 this.type = TYPE_UNKNOWN; 1767 } 1768 return; 1769 } 1770 } 1771 1772 /** 1773 * Resets this transform to the Identity transform. 1774 * @since 1.2 1775 */ 1776 public void setToIdentity() { 1777 m00 = m11 = 1.0; 1778 m10 = m01 = m02 = m12 = 0.0; 1779 state = APPLY_IDENTITY; 1780 type = TYPE_IDENTITY; 1781 } 1782 1783 /** 1784 * Sets this transform to a translation transformation. 1785 * The matrix representing this transform becomes: 1786 * <pre> 1787 * [ 1 0 tx ] 1788 * [ 0 1 ty ] 1789 * [ 0 0 1 ] 1790 * </pre> 1791 * @param tx the distance by which coordinates are translated in the 1792 * X axis direction 1793 * @param ty the distance by which coordinates are translated in the 1794 * Y axis direction 1795 * @since 1.2 1796 */ 1797 public void setToTranslation(double tx, double ty) { 1798 m00 = 1.0; 1799 m10 = 0.0; 1800 m01 = 0.0; 1801 m11 = 1.0; 1802 m02 = tx; 1803 m12 = ty; 1804 if (tx != 0.0 || ty != 0.0) { 1805 state = APPLY_TRANSLATE; 1806 type = TYPE_TRANSLATION; 1807 } else { 1808 state = APPLY_IDENTITY; 1809 type = TYPE_IDENTITY; 1810 } 1811 } 1812 1813 /** 1814 * Sets this transform to a rotation transformation. 1815 * The matrix representing this transform becomes: 1816 * <pre> 1817 * [ cos(theta) -sin(theta) 0 ] 1818 * [ sin(theta) cos(theta) 0 ] 1819 * [ 0 0 1 ] 1820 * </pre> 1821 * Rotating by a positive angle theta rotates points on the positive 1822 * X axis toward the positive Y axis. 1823 * Note also the discussion of 1824 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 1825 * above. 1826 * @param theta the angle of rotation measured in radians 1827 * @since 1.2 1828 */ 1829 public void setToRotation(double theta) { 1830 double sin = Math.sin(theta); 1831 double cos; 1832 if (sin == 1.0 || sin == -1.0) { 1833 cos = 0.0; 1834 state = APPLY_SHEAR; 1835 type = TYPE_QUADRANT_ROTATION; 1836 } else { 1837 cos = Math.cos(theta); 1838 if (cos == -1.0) { 1839 sin = 0.0; 1840 state = APPLY_SCALE; 1841 type = TYPE_QUADRANT_ROTATION; 1842 } else if (cos == 1.0) { 1843 sin = 0.0; 1844 state = APPLY_IDENTITY; 1845 type = TYPE_IDENTITY; 1846 } else { 1847 state = APPLY_SHEAR | APPLY_SCALE; 1848 type = TYPE_GENERAL_ROTATION; 1849 } 1850 } 1851 m00 = cos; 1852 m10 = sin; 1853 m01 = -sin; 1854 m11 = cos; 1855 m02 = 0.0; 1856 m12 = 0.0; 1857 } 1858 1859 /** 1860 * Sets this transform to a translated rotation transformation. 1861 * This operation is equivalent to translating the coordinates so 1862 * that the anchor point is at the origin (S1), then rotating them 1863 * about the new origin (S2), and finally translating so that the 1864 * intermediate origin is restored to the coordinates of the original 1865 * anchor point (S3). 1866 * <p> 1867 * This operation is equivalent to the following sequence of calls: 1868 * <pre> 1869 * setToTranslation(anchorx, anchory); // S3: final translation 1870 * rotate(theta); // S2: rotate around anchor 1871 * translate(-anchorx, -anchory); // S1: translate anchor to origin 1872 * </pre> 1873 * The matrix representing this transform becomes: 1874 * <pre> 1875 * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 1876 * [ sin(theta) cos(theta) y-x*sin-y*cos ] 1877 * [ 0 0 1 ] 1878 * </pre> 1879 * Rotating by a positive angle theta rotates points on the positive 1880 * X axis toward the positive Y axis. 1881 * Note also the discussion of 1882 * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a> 1883 * above. 1884 * 1885 * @param theta the angle of rotation measured in radians 1886 * @param anchorx the X coordinate of the rotation anchor point 1887 * @param anchory the Y coordinate of the rotation anchor point 1888 * @since 1.2 1889 */ 1890 public void setToRotation(double theta, double anchorx, double anchory) { 1891 setToRotation(theta); 1892 double sin = m10; 1893 double oneMinusCos = 1.0 - m00; 1894 m02 = anchorx * oneMinusCos + anchory * sin; 1895 m12 = anchory * oneMinusCos - anchorx * sin; 1896 if (m02 != 0.0 || m12 != 0.0) { 1897 state |= APPLY_TRANSLATE; 1898 type |= TYPE_TRANSLATION; 1899 } 1900 } 1901 1902 /** 1903 * Sets this transform to a rotation transformation that rotates 1904 * coordinates according to a rotation vector. 1905 * All coordinates rotate about the origin by the same amount. 1906 * The amount of rotation is such that coordinates along the former 1907 * positive X axis will subsequently align with the vector pointing 1908 * from the origin to the specified vector coordinates. 1909 * If both {@code vecx} and {@code vecy} are 0.0, 1910 * the transform is set to an identity transform. 1911 * This operation is equivalent to calling: 1912 * <pre> 1913 * setToRotation(Math.atan2(vecy, vecx)); 1914 * </pre> 1915 * 1916 * @param vecx the X coordinate of the rotation vector 1917 * @param vecy the Y coordinate of the rotation vector 1918 * @since 1.6 1919 */ 1920 public void setToRotation(double vecx, double vecy) { 1921 double sin, cos; 1922 if (vecy == 0) { 1923 sin = 0.0; 1924 if (vecx < 0.0) { 1925 cos = -1.0; 1926 state = APPLY_SCALE; 1927 type = TYPE_QUADRANT_ROTATION; 1928 } else { 1929 cos = 1.0; 1930 state = APPLY_IDENTITY; 1931 type = TYPE_IDENTITY; 1932 } 1933 } else if (vecx == 0) { 1934 cos = 0.0; 1935 sin = (vecy > 0.0) ? 1.0 : -1.0; 1936 state = APPLY_SHEAR; 1937 type = TYPE_QUADRANT_ROTATION; 1938 } else { 1939 double len = Math.sqrt(vecx * vecx + vecy * vecy); 1940 cos = vecx / len; 1941 sin = vecy / len; 1942 state = APPLY_SHEAR | APPLY_SCALE; 1943 type = TYPE_GENERAL_ROTATION; 1944 } 1945 m00 = cos; 1946 m10 = sin; 1947 m01 = -sin; 1948 m11 = cos; 1949 m02 = 0.0; 1950 m12 = 0.0; 1951 } 1952 1953 /** 1954 * Sets this transform to a rotation transformation that rotates 1955 * coordinates around an anchor point according to a rotation 1956 * vector. 1957 * All coordinates rotate about the specified anchor coordinates 1958 * by the same amount. 1959 * The amount of rotation is such that coordinates along the former 1960 * positive X axis will subsequently align with the vector pointing 1961 * from the origin to the specified vector coordinates. 1962 * If both {@code vecx} and {@code vecy} are 0.0, 1963 * the transform is set to an identity transform. 1964 * This operation is equivalent to calling: 1965 * <pre> 1966 * setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory); 1967 * </pre> 1968 * 1969 * @param vecx the X coordinate of the rotation vector 1970 * @param vecy the Y coordinate of the rotation vector 1971 * @param anchorx the X coordinate of the rotation anchor point 1972 * @param anchory the Y coordinate of the rotation anchor point 1973 * @since 1.6 1974 */ 1975 public void setToRotation(double vecx, double vecy, 1976 double anchorx, double anchory) 1977 { 1978 setToRotation(vecx, vecy); 1979 double sin = m10; 1980 double oneMinusCos = 1.0 - m00; 1981 m02 = anchorx * oneMinusCos + anchory * sin; 1982 m12 = anchory * oneMinusCos - anchorx * sin; 1983 if (m02 != 0.0 || m12 != 0.0) { 1984 state |= APPLY_TRANSLATE; 1985 type |= TYPE_TRANSLATION; 1986 } 1987 } 1988 1989 /** 1990 * Sets this transform to a rotation transformation that rotates 1991 * coordinates by the specified number of quadrants. 1992 * This operation is equivalent to calling: 1993 * <pre> 1994 * setToRotation(numquadrants * Math.PI / 2.0); 1995 * </pre> 1996 * Rotating by a positive number of quadrants rotates points on 1997 * the positive X axis toward the positive Y axis. 1998 * @param numquadrants the number of 90 degree arcs to rotate by 1999 * @since 1.6 2000 */ 2001 public void setToQuadrantRotation(int numquadrants) { 2002 switch (numquadrants & 3) { 2003 case 0: 2004 m00 = 1.0; 2005 m10 = 0.0; 2006 m01 = 0.0; 2007 m11 = 1.0; 2008 m02 = 0.0; 2009 m12 = 0.0; 2010 state = APPLY_IDENTITY; 2011 type = TYPE_IDENTITY; 2012 break; 2013 case 1: 2014 m00 = 0.0; 2015 m10 = 1.0; 2016 m01 = -1.0; 2017 m11 = 0.0; 2018 m02 = 0.0; 2019 m12 = 0.0; 2020 state = APPLY_SHEAR; 2021 type = TYPE_QUADRANT_ROTATION; 2022 break; 2023 case 2: 2024 m00 = -1.0; 2025 m10 = 0.0; 2026 m01 = 0.0; 2027 m11 = -1.0; 2028 m02 = 0.0; 2029 m12 = 0.0; 2030 state = APPLY_SCALE; 2031 type = TYPE_QUADRANT_ROTATION; 2032 break; 2033 case 3: 2034 m00 = 0.0; 2035 m10 = -1.0; 2036 m01 = 1.0; 2037 m11 = 0.0; 2038 m02 = 0.0; 2039 m12 = 0.0; 2040 state = APPLY_SHEAR; 2041 type = TYPE_QUADRANT_ROTATION; 2042 break; 2043 } 2044 } 2045 2046 /** 2047 * Sets this transform to a translated rotation transformation 2048 * that rotates coordinates by the specified number of quadrants 2049 * around the specified anchor point. 2050 * This operation is equivalent to calling: 2051 * <pre> 2052 * setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory); 2053 * </pre> 2054 * Rotating by a positive number of quadrants rotates points on 2055 * the positive X axis toward the positive Y axis. 2056 * 2057 * @param numquadrants the number of 90 degree arcs to rotate by 2058 * @param anchorx the X coordinate of the rotation anchor point 2059 * @param anchory the Y coordinate of the rotation anchor point 2060 * @since 1.6 2061 */ 2062 public void setToQuadrantRotation(int numquadrants, 2063 double anchorx, double anchory) 2064 { 2065 switch (numquadrants & 3) { 2066 case 0: 2067 m00 = 1.0; 2068 m10 = 0.0; 2069 m01 = 0.0; 2070 m11 = 1.0; 2071 m02 = 0.0; 2072 m12 = 0.0; 2073 state = APPLY_IDENTITY; 2074 type = TYPE_IDENTITY; 2075 break; 2076 case 1: 2077 m00 = 0.0; 2078 m10 = 1.0; 2079 m01 = -1.0; 2080 m11 = 0.0; 2081 m02 = anchorx + anchory; 2082 m12 = anchory - anchorx; 2083 if (m02 == 0.0 && m12 == 0.0) { 2084 state = APPLY_SHEAR; 2085 type = TYPE_QUADRANT_ROTATION; 2086 } else { 2087 state = APPLY_SHEAR | APPLY_TRANSLATE; 2088 type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; 2089 } 2090 break; 2091 case 2: 2092 m00 = -1.0; 2093 m10 = 0.0; 2094 m01 = 0.0; 2095 m11 = -1.0; 2096 m02 = anchorx + anchorx; 2097 m12 = anchory + anchory; 2098 if (m02 == 0.0 && m12 == 0.0) { 2099 state = APPLY_SCALE; 2100 type = TYPE_QUADRANT_ROTATION; 2101 } else { 2102 state = APPLY_SCALE | APPLY_TRANSLATE; 2103 type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; 2104 } 2105 break; 2106 case 3: 2107 m00 = 0.0; 2108 m10 = -1.0; 2109 m01 = 1.0; 2110 m11 = 0.0; 2111 m02 = anchorx - anchory; 2112 m12 = anchory + anchorx; 2113 if (m02 == 0.0 && m12 == 0.0) { 2114 state = APPLY_SHEAR; 2115 type = TYPE_QUADRANT_ROTATION; 2116 } else { 2117 state = APPLY_SHEAR | APPLY_TRANSLATE; 2118 type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; 2119 } 2120 break; 2121 } 2122 } 2123 2124 /** 2125 * Sets this transform to a scaling transformation. 2126 * The matrix representing this transform becomes: 2127 * <pre> 2128 * [ sx 0 0 ] 2129 * [ 0 sy 0 ] 2130 * [ 0 0 1 ] 2131 * </pre> 2132 * @param sx the factor by which coordinates are scaled along the 2133 * X axis direction 2134 * @param sy the factor by which coordinates are scaled along the 2135 * Y axis direction 2136 * @since 1.2 2137 */ 2138 public void setToScale(double sx, double sy) { 2139 m00 = sx; 2140 m10 = 0.0; 2141 m01 = 0.0; 2142 m11 = sy; 2143 m02 = 0.0; 2144 m12 = 0.0; 2145 if (sx != 1.0 || sy != 1.0) { 2146 state = APPLY_SCALE; 2147 type = TYPE_UNKNOWN; 2148 } else { 2149 state = APPLY_IDENTITY; 2150 type = TYPE_IDENTITY; 2151 } 2152 } 2153 2154 /** 2155 * Sets this transform to a shearing transformation. 2156 * The matrix representing this transform becomes: 2157 * <pre> 2158 * [ 1 shx 0 ] 2159 * [ shy 1 0 ] 2160 * [ 0 0 1 ] 2161 * </pre> 2162 * @param shx the multiplier by which coordinates are shifted in the 2163 * direction of the positive X axis as a factor of their Y coordinate 2164 * @param shy the multiplier by which coordinates are shifted in the 2165 * direction of the positive Y axis as a factor of their X coordinate 2166 * @since 1.2 2167 */ 2168 public void setToShear(double shx, double shy) { 2169 m00 = 1.0; 2170 m01 = shx; 2171 m10 = shy; 2172 m11 = 1.0; 2173 m02 = 0.0; 2174 m12 = 0.0; 2175 if (shx != 0.0 || shy != 0.0) { 2176 state = (APPLY_SHEAR | APPLY_SCALE); 2177 type = TYPE_UNKNOWN; 2178 } else { 2179 state = APPLY_IDENTITY; 2180 type = TYPE_IDENTITY; 2181 } 2182 } 2183 2184 /** 2185 * Sets this transform to a copy of the transform in the specified 2186 * {@code AffineTransform} object. 2187 * @param Tx the {@code AffineTransform} object from which to 2188 * copy the transform 2189 * @since 1.2 2190 */ 2191 public void setTransform(AffineTransform Tx) { 2192 this.m00 = Tx.m00; 2193 this.m10 = Tx.m10; 2194 this.m01 = Tx.m01; 2195 this.m11 = Tx.m11; 2196 this.m02 = Tx.m02; 2197 this.m12 = Tx.m12; 2198 this.state = Tx.state; 2199 this.type = Tx.type; 2200 } 2201 2202 /** 2203 * Sets this transform to the matrix specified by the 6 2204 * double precision values. 2205 * 2206 * @param m00 the X coordinate scaling element of the 3x3 matrix 2207 * @param m10 the Y coordinate shearing element of the 3x3 matrix 2208 * @param m01 the X coordinate shearing element of the 3x3 matrix 2209 * @param m11 the Y coordinate scaling element of the 3x3 matrix 2210 * @param m02 the X coordinate translation element of the 3x3 matrix 2211 * @param m12 the Y coordinate translation element of the 3x3 matrix 2212 * @since 1.2 2213 */ 2214 public void setTransform(double m00, double m10, 2215 double m01, double m11, 2216 double m02, double m12) { 2217 this.m00 = m00; 2218 this.m10 = m10; 2219 this.m01 = m01; 2220 this.m11 = m11; 2221 this.m02 = m02; 2222 this.m12 = m12; 2223 updateState(); 2224 } 2225 2226 /** 2227 * Concatenates an {@code AffineTransform Tx} to 2228 * this {@code AffineTransform} Cx in the most commonly useful 2229 * way to provide a new user space 2230 * that is mapped to the former user space by {@code Tx}. 2231 * Cx is updated to perform the combined transformation. 2232 * Transforming a point p by the updated transform Cx' is 2233 * equivalent to first transforming p by {@code Tx} and then 2234 * transforming the result by the original transform Cx like this: 2235 * Cx'(p) = Cx(Tx(p)) 2236 * In matrix notation, if this transform Cx is 2237 * represented by the matrix [this] and {@code Tx} is represented 2238 * by the matrix [Tx] then this method does the following: 2239 * <pre> 2240 * [this] = [this] x [Tx] 2241 * </pre> 2242 * @param Tx the {@code AffineTransform} object to be 2243 * concatenated with this {@code AffineTransform} object. 2244 * @see #preConcatenate 2245 * @since 1.2 2246 */ 2247 @SuppressWarnings("fallthrough") 2248 public void concatenate(AffineTransform Tx) { 2249 double M0, M1; 2250 double T00, T01, T10, T11; 2251 double T02, T12; 2252 int mystate = state; 2253 int txstate = Tx.state; 2254 switch ((txstate << HI_SHIFT) | mystate) { 2255 2256 /* ---------- Tx == IDENTITY cases ---------- */ 2257 case (HI_IDENTITY | APPLY_IDENTITY): 2258 case (HI_IDENTITY | APPLY_TRANSLATE): 2259 case (HI_IDENTITY | APPLY_SCALE): 2260 case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): 2261 case (HI_IDENTITY | APPLY_SHEAR): 2262 case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): 2263 case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): 2264 case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2265 return; 2266 2267 /* ---------- this == IDENTITY cases ---------- */ 2268 case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): 2269 m01 = Tx.m01; 2270 m10 = Tx.m10; 2271 /* NOBREAK */ 2272 case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): 2273 m00 = Tx.m00; 2274 m11 = Tx.m11; 2275 /* NOBREAK */ 2276 case (HI_TRANSLATE | APPLY_IDENTITY): 2277 m02 = Tx.m02; 2278 m12 = Tx.m12; 2279 state = txstate; 2280 type = Tx.type; 2281 return; 2282 case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY): 2283 m01 = Tx.m01; 2284 m10 = Tx.m10; 2285 /* NOBREAK */ 2286 case (HI_SCALE | APPLY_IDENTITY): 2287 m00 = Tx.m00; 2288 m11 = Tx.m11; 2289 state = txstate; 2290 type = Tx.type; 2291 return; 2292 case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY): 2293 m02 = Tx.m02; 2294 m12 = Tx.m12; 2295 /* NOBREAK */ 2296 case (HI_SHEAR | APPLY_IDENTITY): 2297 m01 = Tx.m01; 2298 m10 = Tx.m10; 2299 m00 = m11 = 0.0; 2300 state = txstate; 2301 type = Tx.type; 2302 return; 2303 2304 /* ---------- Tx == TRANSLATE cases ---------- */ 2305 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2306 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): 2307 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): 2308 case (HI_TRANSLATE | APPLY_SHEAR): 2309 case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): 2310 case (HI_TRANSLATE | APPLY_SCALE): 2311 case (HI_TRANSLATE | APPLY_TRANSLATE): 2312 translate(Tx.m02, Tx.m12); 2313 return; 2314 2315 /* ---------- Tx == SCALE cases ---------- */ 2316 case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2317 case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): 2318 case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): 2319 case (HI_SCALE | APPLY_SHEAR): 2320 case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): 2321 case (HI_SCALE | APPLY_SCALE): 2322 case (HI_SCALE | APPLY_TRANSLATE): 2323 scale(Tx.m00, Tx.m11); 2324 return; 2325 2326 /* ---------- Tx == SHEAR cases ---------- */ 2327 case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2328 case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): 2329 T01 = Tx.m01; T10 = Tx.m10; 2330 M0 = m00; 2331 m00 = m01 * T10; 2332 m01 = M0 * T01; 2333 M0 = m10; 2334 m10 = m11 * T10; 2335 m11 = M0 * T01; 2336 type = TYPE_UNKNOWN; 2337 return; 2338 case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): 2339 case (HI_SHEAR | APPLY_SHEAR): 2340 m00 = m01 * Tx.m10; 2341 m01 = 0.0; 2342 m11 = m10 * Tx.m01; 2343 m10 = 0.0; 2344 state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); 2345 type = TYPE_UNKNOWN; 2346 return; 2347 case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2348 case (HI_SHEAR | APPLY_SCALE): 2349 m01 = m00 * Tx.m01; 2350 m00 = 0.0; 2351 m10 = m11 * Tx.m10; 2352 m11 = 0.0; 2353 state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); 2354 type = TYPE_UNKNOWN; 2355 return; 2356 case (HI_SHEAR | APPLY_TRANSLATE): 2357 m00 = 0.0; 2358 m01 = Tx.m01; 2359 m10 = Tx.m10; 2360 m11 = 0.0; 2361 state = APPLY_TRANSLATE | APPLY_SHEAR; 2362 type = TYPE_UNKNOWN; 2363 return; 2364 } 2365 // If Tx has more than one attribute, it is not worth optimizing 2366 // all of those cases... 2367 T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02; 2368 T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12; 2369 switch (mystate) { 2370 default: 2371 stateError(); 2372 /* NOTREACHED */ 2373 case (APPLY_SHEAR | APPLY_SCALE): 2374 state = mystate | txstate; 2375 /* NOBREAK */ 2376 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2377 M0 = m00; 2378 M1 = m01; 2379 m00 = T00 * M0 + T10 * M1; 2380 m01 = T01 * M0 + T11 * M1; 2381 m02 += T02 * M0 + T12 * M1; 2382 2383 M0 = m10; 2384 M1 = m11; 2385 m10 = T00 * M0 + T10 * M1; 2386 m11 = T01 * M0 + T11 * M1; 2387 m12 += T02 * M0 + T12 * M1; 2388 type = TYPE_UNKNOWN; 2389 return; 2390 2391 case (APPLY_SHEAR | APPLY_TRANSLATE): 2392 case (APPLY_SHEAR): 2393 M0 = m01; 2394 m00 = T10 * M0; 2395 m01 = T11 * M0; 2396 m02 += T12 * M0; 2397 2398 M0 = m10; 2399 m10 = T00 * M0; 2400 m11 = T01 * M0; 2401 m12 += T02 * M0; 2402 break; 2403 2404 case (APPLY_SCALE | APPLY_TRANSLATE): 2405 case (APPLY_SCALE): 2406 M0 = m00; 2407 m00 = T00 * M0; 2408 m01 = T01 * M0; 2409 m02 += T02 * M0; 2410 2411 M0 = m11; 2412 m10 = T10 * M0; 2413 m11 = T11 * M0; 2414 m12 += T12 * M0; 2415 break; 2416 2417 case (APPLY_TRANSLATE): 2418 m00 = T00; 2419 m01 = T01; 2420 m02 += T02; 2421 2422 m10 = T10; 2423 m11 = T11; 2424 m12 += T12; 2425 state = txstate | APPLY_TRANSLATE; 2426 type = TYPE_UNKNOWN; 2427 return; 2428 } 2429 updateState(); 2430 } 2431 2432 /** 2433 * Concatenates an {@code AffineTransform Tx} to 2434 * this {@code AffineTransform} Cx 2435 * in a less commonly used way such that {@code Tx} modifies the 2436 * coordinate transformation relative to the absolute pixel 2437 * space rather than relative to the existing user space. 2438 * Cx is updated to perform the combined transformation. 2439 * Transforming a point p by the updated transform Cx' is 2440 * equivalent to first transforming p by the original transform 2441 * Cx and then transforming the result by 2442 * {@code Tx} like this: 2443 * Cx'(p) = Tx(Cx(p)) 2444 * In matrix notation, if this transform Cx 2445 * is represented by the matrix [this] and {@code Tx} is 2446 * represented by the matrix [Tx] then this method does the 2447 * following: 2448 * <pre> 2449 * [this] = [Tx] x [this] 2450 * </pre> 2451 * @param Tx the {@code AffineTransform} object to be 2452 * concatenated with this {@code AffineTransform} object. 2453 * @see #concatenate 2454 * @since 1.2 2455 */ 2456 @SuppressWarnings("fallthrough") 2457 public void preConcatenate(AffineTransform Tx) { 2458 double M0, M1; 2459 double T00, T01, T10, T11; 2460 double T02, T12; 2461 int mystate = state; 2462 int txstate = Tx.state; 2463 switch ((txstate << HI_SHIFT) | mystate) { 2464 case (HI_IDENTITY | APPLY_IDENTITY): 2465 case (HI_IDENTITY | APPLY_TRANSLATE): 2466 case (HI_IDENTITY | APPLY_SCALE): 2467 case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): 2468 case (HI_IDENTITY | APPLY_SHEAR): 2469 case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): 2470 case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): 2471 case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2472 // Tx is IDENTITY... 2473 return; 2474 2475 case (HI_TRANSLATE | APPLY_IDENTITY): 2476 case (HI_TRANSLATE | APPLY_SCALE): 2477 case (HI_TRANSLATE | APPLY_SHEAR): 2478 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): 2479 // Tx is TRANSLATE, this has no TRANSLATE 2480 m02 = Tx.m02; 2481 m12 = Tx.m12; 2482 state = mystate | APPLY_TRANSLATE; 2483 type |= TYPE_TRANSLATION; 2484 return; 2485 2486 case (HI_TRANSLATE | APPLY_TRANSLATE): 2487 case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): 2488 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): 2489 case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2490 // Tx is TRANSLATE, this has one too 2491 m02 = m02 + Tx.m02; 2492 m12 = m12 + Tx.m12; 2493 return; 2494 2495 case (HI_SCALE | APPLY_TRANSLATE): 2496 case (HI_SCALE | APPLY_IDENTITY): 2497 // Only these two existing states need a new state 2498 state = mystate | APPLY_SCALE; 2499 /* NOBREAK */ 2500 case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2501 case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): 2502 case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): 2503 case (HI_SCALE | APPLY_SHEAR): 2504 case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): 2505 case (HI_SCALE | APPLY_SCALE): 2506 // Tx is SCALE, this is anything 2507 T00 = Tx.m00; 2508 T11 = Tx.m11; 2509 if ((mystate & APPLY_SHEAR) != 0) { 2510 m01 = m01 * T00; 2511 m10 = m10 * T11; 2512 if ((mystate & APPLY_SCALE) != 0) { 2513 m00 = m00 * T00; 2514 m11 = m11 * T11; 2515 } 2516 } else { 2517 m00 = m00 * T00; 2518 m11 = m11 * T11; 2519 } 2520 if ((mystate & APPLY_TRANSLATE) != 0) { 2521 m02 = m02 * T00; 2522 m12 = m12 * T11; 2523 } 2524 type = TYPE_UNKNOWN; 2525 return; 2526 case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): 2527 case (HI_SHEAR | APPLY_SHEAR): 2528 mystate = mystate | APPLY_SCALE; 2529 /* NOBREAK */ 2530 case (HI_SHEAR | APPLY_TRANSLATE): 2531 case (HI_SHEAR | APPLY_IDENTITY): 2532 case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2533 case (HI_SHEAR | APPLY_SCALE): 2534 state = mystate ^ APPLY_SHEAR; 2535 /* NOBREAK */ 2536 case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2537 case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): 2538 // Tx is SHEAR, this is anything 2539 T01 = Tx.m01; 2540 T10 = Tx.m10; 2541 2542 M0 = m00; 2543 m00 = m10 * T01; 2544 m10 = M0 * T10; 2545 2546 M0 = m01; 2547 m01 = m11 * T01; 2548 m11 = M0 * T10; 2549 2550 M0 = m02; 2551 m02 = m12 * T01; 2552 m12 = M0 * T10; 2553 type = TYPE_UNKNOWN; 2554 return; 2555 } 2556 // If Tx has more than one attribute, it is not worth optimizing 2557 // all of those cases... 2558 T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02; 2559 T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12; 2560 switch (mystate) { 2561 default: 2562 stateError(); 2563 /* NOTREACHED */ 2564 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2565 M0 = m02; 2566 M1 = m12; 2567 T02 += M0 * T00 + M1 * T01; 2568 T12 += M0 * T10 + M1 * T11; 2569 2570 /* NOBREAK */ 2571 case (APPLY_SHEAR | APPLY_SCALE): 2572 m02 = T02; 2573 m12 = T12; 2574 2575 M0 = m00; 2576 M1 = m10; 2577 m00 = M0 * T00 + M1 * T01; 2578 m10 = M0 * T10 + M1 * T11; 2579 2580 M0 = m01; 2581 M1 = m11; 2582 m01 = M0 * T00 + M1 * T01; 2583 m11 = M0 * T10 + M1 * T11; 2584 break; 2585 2586 case (APPLY_SHEAR | APPLY_TRANSLATE): 2587 M0 = m02; 2588 M1 = m12; 2589 T02 += M0 * T00 + M1 * T01; 2590 T12 += M0 * T10 + M1 * T11; 2591 2592 /* NOBREAK */ 2593 case (APPLY_SHEAR): 2594 m02 = T02; 2595 m12 = T12; 2596 2597 M0 = m10; 2598 m00 = M0 * T01; 2599 m10 = M0 * T11; 2600 2601 M0 = m01; 2602 m01 = M0 * T00; 2603 m11 = M0 * T10; 2604 break; 2605 2606 case (APPLY_SCALE | APPLY_TRANSLATE): 2607 M0 = m02; 2608 M1 = m12; 2609 T02 += M0 * T00 + M1 * T01; 2610 T12 += M0 * T10 + M1 * T11; 2611 2612 /* NOBREAK */ 2613 case (APPLY_SCALE): 2614 m02 = T02; 2615 m12 = T12; 2616 2617 M0 = m00; 2618 m00 = M0 * T00; 2619 m10 = M0 * T10; 2620 2621 M0 = m11; 2622 m01 = M0 * T01; 2623 m11 = M0 * T11; 2624 break; 2625 2626 case (APPLY_TRANSLATE): 2627 M0 = m02; 2628 M1 = m12; 2629 T02 += M0 * T00 + M1 * T01; 2630 T12 += M0 * T10 + M1 * T11; 2631 2632 /* NOBREAK */ 2633 case (APPLY_IDENTITY): 2634 m02 = T02; 2635 m12 = T12; 2636 2637 m00 = T00; 2638 m10 = T10; 2639 2640 m01 = T01; 2641 m11 = T11; 2642 2643 state = mystate | txstate; 2644 type = TYPE_UNKNOWN; 2645 return; 2646 } 2647 updateState(); 2648 } 2649 2650 /** 2651 * Returns an {@code AffineTransform} object representing the 2652 * inverse transformation. 2653 * The inverse transform Tx' of this transform Tx 2654 * maps coordinates transformed by Tx back 2655 * to their original coordinates. 2656 * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)). 2657 * <p> 2658 * If this transform maps all coordinates onto a point or a line 2659 * then it will not have an inverse, since coordinates that do 2660 * not lie on the destination point or line will not have an inverse 2661 * mapping. 2662 * The {@code getDeterminant} method can be used to determine if this 2663 * transform has no inverse, in which case an exception will be 2664 * thrown if the {@code createInverse} method is called. 2665 * @return a new {@code AffineTransform} object representing the 2666 * inverse transformation. 2667 * @see #getDeterminant 2668 * @exception NoninvertibleTransformException 2669 * if the matrix cannot be inverted. 2670 * @since 1.2 2671 */ 2672 public AffineTransform createInverse() 2673 throws NoninvertibleTransformException 2674 { 2675 double det; 2676 switch (state) { 2677 default: 2678 stateError(); 2679 /* NOTREACHED */ 2680 return null; 2681 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2682 det = m00 * m11 - m01 * m10; 2683 if (Math.abs(det) <= Double.MIN_VALUE) { 2684 throw new NoninvertibleTransformException("Determinant is "+ 2685 det); 2686 } 2687 return new AffineTransform( m11 / det, -m10 / det, 2688 -m01 / det, m00 / det, 2689 (m01 * m12 - m11 * m02) / det, 2690 (m10 * m02 - m00 * m12) / det, 2691 (APPLY_SHEAR | 2692 APPLY_SCALE | 2693 APPLY_TRANSLATE)); 2694 case (APPLY_SHEAR | APPLY_SCALE): 2695 det = m00 * m11 - m01 * m10; 2696 if (Math.abs(det) <= Double.MIN_VALUE) { 2697 throw new NoninvertibleTransformException("Determinant is "+ 2698 det); 2699 } 2700 return new AffineTransform( m11 / det, -m10 / det, 2701 -m01 / det, m00 / det, 2702 0.0, 0.0, 2703 (APPLY_SHEAR | APPLY_SCALE)); 2704 case (APPLY_SHEAR | APPLY_TRANSLATE): 2705 if (m01 == 0.0 || m10 == 0.0) { 2706 throw new NoninvertibleTransformException("Determinant is 0"); 2707 } 2708 return new AffineTransform( 0.0, 1.0 / m01, 2709 1.0 / m10, 0.0, 2710 -m12 / m10, -m02 / m01, 2711 (APPLY_SHEAR | APPLY_TRANSLATE)); 2712 case (APPLY_SHEAR): 2713 if (m01 == 0.0 || m10 == 0.0) { 2714 throw new NoninvertibleTransformException("Determinant is 0"); 2715 } 2716 return new AffineTransform(0.0, 1.0 / m01, 2717 1.0 / m10, 0.0, 2718 0.0, 0.0, 2719 (APPLY_SHEAR)); 2720 case (APPLY_SCALE | APPLY_TRANSLATE): 2721 if (m00 == 0.0 || m11 == 0.0) { 2722 throw new NoninvertibleTransformException("Determinant is 0"); 2723 } 2724 return new AffineTransform( 1.0 / m00, 0.0, 2725 0.0, 1.0 / m11, 2726 -m02 / m00, -m12 / m11, 2727 (APPLY_SCALE | APPLY_TRANSLATE)); 2728 case (APPLY_SCALE): 2729 if (m00 == 0.0 || m11 == 0.0) { 2730 throw new NoninvertibleTransformException("Determinant is 0"); 2731 } 2732 return new AffineTransform(1.0 / m00, 0.0, 2733 0.0, 1.0 / m11, 2734 0.0, 0.0, 2735 (APPLY_SCALE)); 2736 case (APPLY_TRANSLATE): 2737 return new AffineTransform( 1.0, 0.0, 2738 0.0, 1.0, 2739 -m02, -m12, 2740 (APPLY_TRANSLATE)); 2741 case (APPLY_IDENTITY): 2742 return new AffineTransform(); 2743 } 2744 2745 /* NOTREACHED */ 2746 } 2747 2748 /** 2749 * Sets this transform to the inverse of itself. 2750 * The inverse transform Tx' of this transform Tx 2751 * maps coordinates transformed by Tx back 2752 * to their original coordinates. 2753 * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)). 2754 * <p> 2755 * If this transform maps all coordinates onto a point or a line 2756 * then it will not have an inverse, since coordinates that do 2757 * not lie on the destination point or line will not have an inverse 2758 * mapping. 2759 * The {@code getDeterminant} method can be used to determine if this 2760 * transform has no inverse, in which case an exception will be 2761 * thrown if the {@code invert} method is called. 2762 * @see #getDeterminant 2763 * @exception NoninvertibleTransformException 2764 * if the matrix cannot be inverted. 2765 * @since 1.6 2766 */ 2767 public void invert() 2768 throws NoninvertibleTransformException 2769 { 2770 double M00, M01, M02; 2771 double M10, M11, M12; 2772 double det; 2773 switch (state) { 2774 default: 2775 stateError(); 2776 /* NOTREACHED */ 2777 return; 2778 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2779 M00 = m00; M01 = m01; M02 = m02; 2780 M10 = m10; M11 = m11; M12 = m12; 2781 det = M00 * M11 - M01 * M10; 2782 if (Math.abs(det) <= Double.MIN_VALUE) { 2783 throw new NoninvertibleTransformException("Determinant is "+ 2784 det); 2785 } 2786 m00 = M11 / det; 2787 m10 = -M10 / det; 2788 m01 = -M01 / det; 2789 m11 = M00 / det; 2790 m02 = (M01 * M12 - M11 * M02) / det; 2791 m12 = (M10 * M02 - M00 * M12) / det; 2792 break; 2793 case (APPLY_SHEAR | APPLY_SCALE): 2794 M00 = m00; M01 = m01; 2795 M10 = m10; M11 = m11; 2796 det = M00 * M11 - M01 * M10; 2797 if (Math.abs(det) <= Double.MIN_VALUE) { 2798 throw new NoninvertibleTransformException("Determinant is "+ 2799 det); 2800 } 2801 m00 = M11 / det; 2802 m10 = -M10 / det; 2803 m01 = -M01 / det; 2804 m11 = M00 / det; 2805 // m02 = 0.0; 2806 // m12 = 0.0; 2807 break; 2808 case (APPLY_SHEAR | APPLY_TRANSLATE): 2809 M01 = m01; M02 = m02; 2810 M10 = m10; M12 = m12; 2811 if (M01 == 0.0 || M10 == 0.0) { 2812 throw new NoninvertibleTransformException("Determinant is 0"); 2813 } 2814 // m00 = 0.0; 2815 m10 = 1.0 / M01; 2816 m01 = 1.0 / M10; 2817 // m11 = 0.0; 2818 m02 = -M12 / M10; 2819 m12 = -M02 / M01; 2820 break; 2821 case (APPLY_SHEAR): 2822 M01 = m01; 2823 M10 = m10; 2824 if (M01 == 0.0 || M10 == 0.0) { 2825 throw new NoninvertibleTransformException("Determinant is 0"); 2826 } 2827 // m00 = 0.0; 2828 m10 = 1.0 / M01; 2829 m01 = 1.0 / M10; 2830 // m11 = 0.0; 2831 // m02 = 0.0; 2832 // m12 = 0.0; 2833 break; 2834 case (APPLY_SCALE | APPLY_TRANSLATE): 2835 M00 = m00; M02 = m02; 2836 M11 = m11; M12 = m12; 2837 if (M00 == 0.0 || M11 == 0.0) { 2838 throw new NoninvertibleTransformException("Determinant is 0"); 2839 } 2840 m00 = 1.0 / M00; 2841 // m10 = 0.0; 2842 // m01 = 0.0; 2843 m11 = 1.0 / M11; 2844 m02 = -M02 / M00; 2845 m12 = -M12 / M11; 2846 break; 2847 case (APPLY_SCALE): 2848 M00 = m00; 2849 M11 = m11; 2850 if (M00 == 0.0 || M11 == 0.0) { 2851 throw new NoninvertibleTransformException("Determinant is 0"); 2852 } 2853 m00 = 1.0 / M00; 2854 // m10 = 0.0; 2855 // m01 = 0.0; 2856 m11 = 1.0 / M11; 2857 // m02 = 0.0; 2858 // m12 = 0.0; 2859 break; 2860 case (APPLY_TRANSLATE): 2861 // m00 = 1.0; 2862 // m10 = 0.0; 2863 // m01 = 0.0; 2864 // m11 = 1.0; 2865 m02 = -m02; 2866 m12 = -m12; 2867 break; 2868 case (APPLY_IDENTITY): 2869 // m00 = 1.0; 2870 // m10 = 0.0; 2871 // m01 = 0.0; 2872 // m11 = 1.0; 2873 // m02 = 0.0; 2874 // m12 = 0.0; 2875 break; 2876 } 2877 } 2878 2879 /** 2880 * Transforms the specified {@code ptSrc} and stores the result 2881 * in {@code ptDst}. 2882 * If {@code ptDst} is {@code null}, a new {@link Point2D} 2883 * object is allocated and then the result of the transformation is 2884 * stored in this object. 2885 * In either case, {@code ptDst}, which contains the 2886 * transformed point, is returned for convenience. 2887 * If {@code ptSrc} and {@code ptDst} are the same 2888 * object, the input point is correctly overwritten with 2889 * the transformed point. 2890 * @param ptSrc the specified {@code Point2D} to be transformed 2891 * @param ptDst the specified {@code Point2D} that stores the 2892 * result of transforming {@code ptSrc} 2893 * @return the {@code ptDst} after transforming 2894 * {@code ptSrc} and storing the result in {@code ptDst}. 2895 * @since 1.2 2896 */ 2897 public Point2D transform(Point2D ptSrc, Point2D ptDst) { 2898 if (ptDst == null) { 2899 if (ptSrc instanceof Point2D.Double) { 2900 ptDst = new Point2D.Double(); 2901 } else { 2902 ptDst = new Point2D.Float(); 2903 } 2904 } 2905 // Copy source coords into local variables in case src == dst 2906 double x = ptSrc.getX(); 2907 double y = ptSrc.getY(); 2908 switch (state) { 2909 default: 2910 stateError(); 2911 /* NOTREACHED */ 2912 return null; 2913 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2914 ptDst.setLocation(x * m00 + y * m01 + m02, 2915 x * m10 + y * m11 + m12); 2916 return ptDst; 2917 case (APPLY_SHEAR | APPLY_SCALE): 2918 ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11); 2919 return ptDst; 2920 case (APPLY_SHEAR | APPLY_TRANSLATE): 2921 ptDst.setLocation(y * m01 + m02, x * m10 + m12); 2922 return ptDst; 2923 case (APPLY_SHEAR): 2924 ptDst.setLocation(y * m01, x * m10); 2925 return ptDst; 2926 case (APPLY_SCALE | APPLY_TRANSLATE): 2927 ptDst.setLocation(x * m00 + m02, y * m11 + m12); 2928 return ptDst; 2929 case (APPLY_SCALE): 2930 ptDst.setLocation(x * m00, y * m11); 2931 return ptDst; 2932 case (APPLY_TRANSLATE): 2933 ptDst.setLocation(x + m02, y + m12); 2934 return ptDst; 2935 case (APPLY_IDENTITY): 2936 ptDst.setLocation(x, y); 2937 return ptDst; 2938 } 2939 2940 /* NOTREACHED */ 2941 } 2942 2943 /** 2944 * Transforms an array of point objects by this transform. 2945 * If any element of the {@code ptDst} array is 2946 * {@code null}, a new {@code Point2D} object is allocated 2947 * and stored into that element before storing the results of the 2948 * transformation. 2949 * <p> 2950 * Note that this method does not take any precautions to 2951 * avoid problems caused by storing results into {@code Point2D} 2952 * objects that will be used as the source for calculations 2953 * further down the source array. 2954 * This method does guarantee that if a specified {@code Point2D} 2955 * object is both the source and destination for the same single point 2956 * transform operation then the results will not be stored until 2957 * the calculations are complete to avoid storing the results on 2958 * top of the operands. 2959 * If, however, the destination {@code Point2D} object for one 2960 * operation is the same object as the source {@code Point2D} 2961 * object for another operation further down the source array then 2962 * the original coordinates in that point are overwritten before 2963 * they can be converted. 2964 * @param ptSrc the array containing the source point objects 2965 * @param ptDst the array into which the transform point objects are 2966 * returned 2967 * @param srcOff the offset to the first point object to be 2968 * transformed in the source array 2969 * @param dstOff the offset to the location of the first 2970 * transformed point object that is stored in the destination array 2971 * @param numPts the number of point objects to be transformed 2972 * @since 1.2 2973 */ 2974 public void transform(Point2D[] ptSrc, int srcOff, 2975 Point2D[] ptDst, int dstOff, 2976 int numPts) { 2977 int state = this.state; 2978 while (--numPts >= 0) { 2979 // Copy source coords into local variables in case src == dst 2980 Point2D src = ptSrc[srcOff++]; 2981 double x = src.getX(); 2982 double y = src.getY(); 2983 Point2D dst = ptDst[dstOff++]; 2984 if (dst == null) { 2985 if (src instanceof Point2D.Double) { 2986 dst = new Point2D.Double(); 2987 } else { 2988 dst = new Point2D.Float(); 2989 } 2990 ptDst[dstOff - 1] = dst; 2991 } 2992 switch (state) { 2993 default: 2994 stateError(); 2995 /* NOTREACHED */ 2996 return; 2997 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 2998 dst.setLocation(x * m00 + y * m01 + m02, 2999 x * m10 + y * m11 + m12); 3000 break; 3001 case (APPLY_SHEAR | APPLY_SCALE): 3002 dst.setLocation(x * m00 + y * m01, x * m10 + y * m11); 3003 break; 3004 case (APPLY_SHEAR | APPLY_TRANSLATE): 3005 dst.setLocation(y * m01 + m02, x * m10 + m12); 3006 break; 3007 case (APPLY_SHEAR): 3008 dst.setLocation(y * m01, x * m10); 3009 break; 3010 case (APPLY_SCALE | APPLY_TRANSLATE): 3011 dst.setLocation(x * m00 + m02, y * m11 + m12); 3012 break; 3013 case (APPLY_SCALE): 3014 dst.setLocation(x * m00, y * m11); 3015 break; 3016 case (APPLY_TRANSLATE): 3017 dst.setLocation(x + m02, y + m12); 3018 break; 3019 case (APPLY_IDENTITY): 3020 dst.setLocation(x, y); 3021 break; 3022 } 3023 } 3024 3025 /* NOTREACHED */ 3026 } 3027 3028 /** 3029 * Transforms an array of floating point coordinates by this transform. 3030 * The two coordinate array sections can be exactly the same or 3031 * can be overlapping sections of the same array without affecting the 3032 * validity of the results. 3033 * This method ensures that no source coordinates are overwritten by a 3034 * previous operation before they can be transformed. 3035 * The coordinates are stored in the arrays starting at the specified 3036 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3037 * @param srcPts the array containing the source point coordinates. 3038 * Each point is stored as a pair of x, y coordinates. 3039 * @param dstPts the array into which the transformed point coordinates 3040 * are returned. Each point is stored as a pair of x, y 3041 * coordinates. 3042 * @param srcOff the offset to the first point to be transformed 3043 * in the source array 3044 * @param dstOff the offset to the location of the first 3045 * transformed point that is stored in the destination array 3046 * @param numPts the number of points to be transformed 3047 * @since 1.2 3048 */ 3049 public void transform(float[] srcPts, int srcOff, 3050 float[] dstPts, int dstOff, 3051 int numPts) { 3052 double M00, M01, M02, M10, M11, M12; // For caching 3053 if (dstPts == srcPts && 3054 dstOff > srcOff && dstOff < srcOff + numPts * 2) 3055 { 3056 // If the arrays overlap partially with the destination higher 3057 // than the source and we transform the coordinates normally 3058 // we would overwrite some of the later source coordinates 3059 // with results of previous transformations. 3060 // To get around this we use arraycopy to copy the points 3061 // to their final destination with correct overwrite 3062 // handling and then transform them in place in the new 3063 // safer location. 3064 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); 3065 // srcPts = dstPts; // They are known to be equal. 3066 srcOff = dstOff; 3067 } 3068 switch (state) { 3069 default: 3070 stateError(); 3071 /* NOTREACHED */ 3072 return; 3073 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3074 M00 = m00; M01 = m01; M02 = m02; 3075 M10 = m10; M11 = m11; M12 = m12; 3076 while (--numPts >= 0) { 3077 double x = srcPts[srcOff++]; 3078 double y = srcPts[srcOff++]; 3079 dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02); 3080 dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12); 3081 } 3082 return; 3083 case (APPLY_SHEAR | APPLY_SCALE): 3084 M00 = m00; M01 = m01; 3085 M10 = m10; M11 = m11; 3086 while (--numPts >= 0) { 3087 double x = srcPts[srcOff++]; 3088 double y = srcPts[srcOff++]; 3089 dstPts[dstOff++] = (float) (M00 * x + M01 * y); 3090 dstPts[dstOff++] = (float) (M10 * x + M11 * y); 3091 } 3092 return; 3093 case (APPLY_SHEAR | APPLY_TRANSLATE): 3094 M01 = m01; M02 = m02; 3095 M10 = m10; M12 = m12; 3096 while (--numPts >= 0) { 3097 double x = srcPts[srcOff++]; 3098 dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02); 3099 dstPts[dstOff++] = (float) (M10 * x + M12); 3100 } 3101 return; 3102 case (APPLY_SHEAR): 3103 M01 = m01; M10 = m10; 3104 while (--numPts >= 0) { 3105 double x = srcPts[srcOff++]; 3106 dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]); 3107 dstPts[dstOff++] = (float) (M10 * x); 3108 } 3109 return; 3110 case (APPLY_SCALE | APPLY_TRANSLATE): 3111 M00 = m00; M02 = m02; 3112 M11 = m11; M12 = m12; 3113 while (--numPts >= 0) { 3114 dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02); 3115 dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12); 3116 } 3117 return; 3118 case (APPLY_SCALE): 3119 M00 = m00; M11 = m11; 3120 while (--numPts >= 0) { 3121 dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]); 3122 dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]); 3123 } 3124 return; 3125 case (APPLY_TRANSLATE): 3126 M02 = m02; M12 = m12; 3127 while (--numPts >= 0) { 3128 dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02); 3129 dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12); 3130 } 3131 return; 3132 case (APPLY_IDENTITY): 3133 if (srcPts != dstPts || srcOff != dstOff) { 3134 System.arraycopy(srcPts, srcOff, dstPts, dstOff, 3135 numPts * 2); 3136 } 3137 return; 3138 } 3139 3140 /* NOTREACHED */ 3141 } 3142 3143 /** 3144 * Transforms an array of double precision coordinates by this transform. 3145 * The two coordinate array sections can be exactly the same or 3146 * can be overlapping sections of the same array without affecting the 3147 * validity of the results. 3148 * This method ensures that no source coordinates are 3149 * overwritten by a previous operation before they can be transformed. 3150 * The coordinates are stored in the arrays starting at the indicated 3151 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3152 * @param srcPts the array containing the source point coordinates. 3153 * Each point is stored as a pair of x, y coordinates. 3154 * @param dstPts the array into which the transformed point 3155 * coordinates are returned. Each point is stored as a pair of 3156 * x, y coordinates. 3157 * @param srcOff the offset to the first point to be transformed 3158 * in the source array 3159 * @param dstOff the offset to the location of the first 3160 * transformed point that is stored in the destination array 3161 * @param numPts the number of point objects to be transformed 3162 * @since 1.2 3163 */ 3164 public void transform(double[] srcPts, int srcOff, 3165 double[] dstPts, int dstOff, 3166 int numPts) { 3167 double M00, M01, M02, M10, M11, M12; // For caching 3168 if (dstPts == srcPts && 3169 dstOff > srcOff && dstOff < srcOff + numPts * 2) 3170 { 3171 // If the arrays overlap partially with the destination higher 3172 // than the source and we transform the coordinates normally 3173 // we would overwrite some of the later source coordinates 3174 // with results of previous transformations. 3175 // To get around this we use arraycopy to copy the points 3176 // to their final destination with correct overwrite 3177 // handling and then transform them in place in the new 3178 // safer location. 3179 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); 3180 // srcPts = dstPts; // They are known to be equal. 3181 srcOff = dstOff; 3182 } 3183 switch (state) { 3184 default: 3185 stateError(); 3186 /* NOTREACHED */ 3187 return; 3188 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3189 M00 = m00; M01 = m01; M02 = m02; 3190 M10 = m10; M11 = m11; M12 = m12; 3191 while (--numPts >= 0) { 3192 double x = srcPts[srcOff++]; 3193 double y = srcPts[srcOff++]; 3194 dstPts[dstOff++] = M00 * x + M01 * y + M02; 3195 dstPts[dstOff++] = M10 * x + M11 * y + M12; 3196 } 3197 return; 3198 case (APPLY_SHEAR | APPLY_SCALE): 3199 M00 = m00; M01 = m01; 3200 M10 = m10; M11 = m11; 3201 while (--numPts >= 0) { 3202 double x = srcPts[srcOff++]; 3203 double y = srcPts[srcOff++]; 3204 dstPts[dstOff++] = M00 * x + M01 * y; 3205 dstPts[dstOff++] = M10 * x + M11 * y; 3206 } 3207 return; 3208 case (APPLY_SHEAR | APPLY_TRANSLATE): 3209 M01 = m01; M02 = m02; 3210 M10 = m10; M12 = m12; 3211 while (--numPts >= 0) { 3212 double x = srcPts[srcOff++]; 3213 dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02; 3214 dstPts[dstOff++] = M10 * x + M12; 3215 } 3216 return; 3217 case (APPLY_SHEAR): 3218 M01 = m01; M10 = m10; 3219 while (--numPts >= 0) { 3220 double x = srcPts[srcOff++]; 3221 dstPts[dstOff++] = M01 * srcPts[srcOff++]; 3222 dstPts[dstOff++] = M10 * x; 3223 } 3224 return; 3225 case (APPLY_SCALE | APPLY_TRANSLATE): 3226 M00 = m00; M02 = m02; 3227 M11 = m11; M12 = m12; 3228 while (--numPts >= 0) { 3229 dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02; 3230 dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12; 3231 } 3232 return; 3233 case (APPLY_SCALE): 3234 M00 = m00; M11 = m11; 3235 while (--numPts >= 0) { 3236 dstPts[dstOff++] = M00 * srcPts[srcOff++]; 3237 dstPts[dstOff++] = M11 * srcPts[srcOff++]; 3238 } 3239 return; 3240 case (APPLY_TRANSLATE): 3241 M02 = m02; M12 = m12; 3242 while (--numPts >= 0) { 3243 dstPts[dstOff++] = srcPts[srcOff++] + M02; 3244 dstPts[dstOff++] = srcPts[srcOff++] + M12; 3245 } 3246 return; 3247 case (APPLY_IDENTITY): 3248 if (srcPts != dstPts || srcOff != dstOff) { 3249 System.arraycopy(srcPts, srcOff, dstPts, dstOff, 3250 numPts * 2); 3251 } 3252 return; 3253 } 3254 3255 /* NOTREACHED */ 3256 } 3257 3258 /** 3259 * Transforms an array of floating point coordinates by this transform 3260 * and stores the results into an array of doubles. 3261 * The coordinates are stored in the arrays starting at the specified 3262 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3263 * @param srcPts the array containing the source point coordinates. 3264 * Each point is stored as a pair of x, y coordinates. 3265 * @param dstPts the array into which the transformed point coordinates 3266 * are returned. Each point is stored as a pair of x, y 3267 * coordinates. 3268 * @param srcOff the offset to the first point to be transformed 3269 * in the source array 3270 * @param dstOff the offset to the location of the first 3271 * transformed point that is stored in the destination array 3272 * @param numPts the number of points to be transformed 3273 * @since 1.2 3274 */ 3275 public void transform(float[] srcPts, int srcOff, 3276 double[] dstPts, int dstOff, 3277 int numPts) { 3278 double M00, M01, M02, M10, M11, M12; // For caching 3279 switch (state) { 3280 default: 3281 stateError(); 3282 /* NOTREACHED */ 3283 return; 3284 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3285 M00 = m00; M01 = m01; M02 = m02; 3286 M10 = m10; M11 = m11; M12 = m12; 3287 while (--numPts >= 0) { 3288 double x = srcPts[srcOff++]; 3289 double y = srcPts[srcOff++]; 3290 dstPts[dstOff++] = M00 * x + M01 * y + M02; 3291 dstPts[dstOff++] = M10 * x + M11 * y + M12; 3292 } 3293 return; 3294 case (APPLY_SHEAR | APPLY_SCALE): 3295 M00 = m00; M01 = m01; 3296 M10 = m10; M11 = m11; 3297 while (--numPts >= 0) { 3298 double x = srcPts[srcOff++]; 3299 double y = srcPts[srcOff++]; 3300 dstPts[dstOff++] = M00 * x + M01 * y; 3301 dstPts[dstOff++] = M10 * x + M11 * y; 3302 } 3303 return; 3304 case (APPLY_SHEAR | APPLY_TRANSLATE): 3305 M01 = m01; M02 = m02; 3306 M10 = m10; M12 = m12; 3307 while (--numPts >= 0) { 3308 double x = srcPts[srcOff++]; 3309 dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02; 3310 dstPts[dstOff++] = M10 * x + M12; 3311 } 3312 return; 3313 case (APPLY_SHEAR): 3314 M01 = m01; M10 = m10; 3315 while (--numPts >= 0) { 3316 double x = srcPts[srcOff++]; 3317 dstPts[dstOff++] = M01 * srcPts[srcOff++]; 3318 dstPts[dstOff++] = M10 * x; 3319 } 3320 return; 3321 case (APPLY_SCALE | APPLY_TRANSLATE): 3322 M00 = m00; M02 = m02; 3323 M11 = m11; M12 = m12; 3324 while (--numPts >= 0) { 3325 dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02; 3326 dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12; 3327 } 3328 return; 3329 case (APPLY_SCALE): 3330 M00 = m00; M11 = m11; 3331 while (--numPts >= 0) { 3332 dstPts[dstOff++] = M00 * srcPts[srcOff++]; 3333 dstPts[dstOff++] = M11 * srcPts[srcOff++]; 3334 } 3335 return; 3336 case (APPLY_TRANSLATE): 3337 M02 = m02; M12 = m12; 3338 while (--numPts >= 0) { 3339 dstPts[dstOff++] = srcPts[srcOff++] + M02; 3340 dstPts[dstOff++] = srcPts[srcOff++] + M12; 3341 } 3342 return; 3343 case (APPLY_IDENTITY): 3344 while (--numPts >= 0) { 3345 dstPts[dstOff++] = srcPts[srcOff++]; 3346 dstPts[dstOff++] = srcPts[srcOff++]; 3347 } 3348 return; 3349 } 3350 3351 /* NOTREACHED */ 3352 } 3353 3354 /** 3355 * Transforms an array of double precision coordinates by this transform 3356 * and stores the results into an array of floats. 3357 * The coordinates are stored in the arrays starting at the specified 3358 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3359 * @param srcPts the array containing the source point coordinates. 3360 * Each point is stored as a pair of x, y coordinates. 3361 * @param dstPts the array into which the transformed point 3362 * coordinates are returned. Each point is stored as a pair of 3363 * x, y coordinates. 3364 * @param srcOff the offset to the first point to be transformed 3365 * in the source array 3366 * @param dstOff the offset to the location of the first 3367 * transformed point that is stored in the destination array 3368 * @param numPts the number of point objects to be transformed 3369 * @since 1.2 3370 */ 3371 public void transform(double[] srcPts, int srcOff, 3372 float[] dstPts, int dstOff, 3373 int numPts) { 3374 double M00, M01, M02, M10, M11, M12; // For caching 3375 switch (state) { 3376 default: 3377 stateError(); 3378 /* NOTREACHED */ 3379 return; 3380 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3381 M00 = m00; M01 = m01; M02 = m02; 3382 M10 = m10; M11 = m11; M12 = m12; 3383 while (--numPts >= 0) { 3384 double x = srcPts[srcOff++]; 3385 double y = srcPts[srcOff++]; 3386 dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02); 3387 dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12); 3388 } 3389 return; 3390 case (APPLY_SHEAR | APPLY_SCALE): 3391 M00 = m00; M01 = m01; 3392 M10 = m10; M11 = m11; 3393 while (--numPts >= 0) { 3394 double x = srcPts[srcOff++]; 3395 double y = srcPts[srcOff++]; 3396 dstPts[dstOff++] = (float) (M00 * x + M01 * y); 3397 dstPts[dstOff++] = (float) (M10 * x + M11 * y); 3398 } 3399 return; 3400 case (APPLY_SHEAR | APPLY_TRANSLATE): 3401 M01 = m01; M02 = m02; 3402 M10 = m10; M12 = m12; 3403 while (--numPts >= 0) { 3404 double x = srcPts[srcOff++]; 3405 dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02); 3406 dstPts[dstOff++] = (float) (M10 * x + M12); 3407 } 3408 return; 3409 case (APPLY_SHEAR): 3410 M01 = m01; M10 = m10; 3411 while (--numPts >= 0) { 3412 double x = srcPts[srcOff++]; 3413 dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]); 3414 dstPts[dstOff++] = (float) (M10 * x); 3415 } 3416 return; 3417 case (APPLY_SCALE | APPLY_TRANSLATE): 3418 M00 = m00; M02 = m02; 3419 M11 = m11; M12 = m12; 3420 while (--numPts >= 0) { 3421 dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02); 3422 dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12); 3423 } 3424 return; 3425 case (APPLY_SCALE): 3426 M00 = m00; M11 = m11; 3427 while (--numPts >= 0) { 3428 dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]); 3429 dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]); 3430 } 3431 return; 3432 case (APPLY_TRANSLATE): 3433 M02 = m02; M12 = m12; 3434 while (--numPts >= 0) { 3435 dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02); 3436 dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12); 3437 } 3438 return; 3439 case (APPLY_IDENTITY): 3440 while (--numPts >= 0) { 3441 dstPts[dstOff++] = (float) (srcPts[srcOff++]); 3442 dstPts[dstOff++] = (float) (srcPts[srcOff++]); 3443 } 3444 return; 3445 } 3446 3447 /* NOTREACHED */ 3448 } 3449 3450 /** 3451 * Inverse transforms the specified {@code ptSrc} and stores the 3452 * result in {@code ptDst}. 3453 * If {@code ptDst} is {@code null}, a new 3454 * {@code Point2D} object is allocated and then the result of the 3455 * transform is stored in this object. 3456 * In either case, {@code ptDst}, which contains the transformed 3457 * point, is returned for convenience. 3458 * If {@code ptSrc} and {@code ptDst} are the same 3459 * object, the input point is correctly overwritten with the 3460 * transformed point. 3461 * @param ptSrc the point to be inverse transformed 3462 * @param ptDst the resulting transformed point 3463 * @return {@code ptDst}, which contains the result of the 3464 * inverse transform. 3465 * @exception NoninvertibleTransformException if the matrix cannot be 3466 * inverted. 3467 * @since 1.2 3468 */ 3469 @SuppressWarnings("fallthrough") 3470 public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst) 3471 throws NoninvertibleTransformException 3472 { 3473 if (ptDst == null) { 3474 if (ptSrc instanceof Point2D.Double) { 3475 ptDst = new Point2D.Double(); 3476 } else { 3477 ptDst = new Point2D.Float(); 3478 } 3479 } 3480 // Copy source coords into local variables in case src == dst 3481 double x = ptSrc.getX(); 3482 double y = ptSrc.getY(); 3483 switch (state) { 3484 default: 3485 stateError(); 3486 /* NOTREACHED */ 3487 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3488 x -= m02; 3489 y -= m12; 3490 /* NOBREAK */ 3491 case (APPLY_SHEAR | APPLY_SCALE): 3492 double det = m00 * m11 - m01 * m10; 3493 if (Math.abs(det) <= Double.MIN_VALUE) { 3494 throw new NoninvertibleTransformException("Determinant is "+ 3495 det); 3496 } 3497 ptDst.setLocation((x * m11 - y * m01) / det, 3498 (y * m00 - x * m10) / det); 3499 return ptDst; 3500 case (APPLY_SHEAR | APPLY_TRANSLATE): 3501 x -= m02; 3502 y -= m12; 3503 /* NOBREAK */ 3504 case (APPLY_SHEAR): 3505 if (m01 == 0.0 || m10 == 0.0) { 3506 throw new NoninvertibleTransformException("Determinant is 0"); 3507 } 3508 ptDst.setLocation(y / m10, x / m01); 3509 return ptDst; 3510 case (APPLY_SCALE | APPLY_TRANSLATE): 3511 x -= m02; 3512 y -= m12; 3513 /* NOBREAK */ 3514 case (APPLY_SCALE): 3515 if (m00 == 0.0 || m11 == 0.0) { 3516 throw new NoninvertibleTransformException("Determinant is 0"); 3517 } 3518 ptDst.setLocation(x / m00, y / m11); 3519 return ptDst; 3520 case (APPLY_TRANSLATE): 3521 ptDst.setLocation(x - m02, y - m12); 3522 return ptDst; 3523 case (APPLY_IDENTITY): 3524 ptDst.setLocation(x, y); 3525 return ptDst; 3526 } 3527 3528 /* NOTREACHED */ 3529 } 3530 3531 /** 3532 * Inverse transforms an array of double precision coordinates by 3533 * this transform. 3534 * The two coordinate array sections can be exactly the same or 3535 * can be overlapping sections of the same array without affecting the 3536 * validity of the results. 3537 * This method ensures that no source coordinates are 3538 * overwritten by a previous operation before they can be transformed. 3539 * The coordinates are stored in the arrays starting at the specified 3540 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3541 * @param srcPts the array containing the source point coordinates. 3542 * Each point is stored as a pair of x, y coordinates. 3543 * @param dstPts the array into which the transformed point 3544 * coordinates are returned. Each point is stored as a pair of 3545 * x, y coordinates. 3546 * @param srcOff the offset to the first point to be transformed 3547 * in the source array 3548 * @param dstOff the offset to the location of the first 3549 * transformed point that is stored in the destination array 3550 * @param numPts the number of point objects to be transformed 3551 * @exception NoninvertibleTransformException if the matrix cannot be 3552 * inverted. 3553 * @since 1.2 3554 */ 3555 public void inverseTransform(double[] srcPts, int srcOff, 3556 double[] dstPts, int dstOff, 3557 int numPts) 3558 throws NoninvertibleTransformException 3559 { 3560 double M00, M01, M02, M10, M11, M12; // For caching 3561 double det; 3562 if (dstPts == srcPts && 3563 dstOff > srcOff && dstOff < srcOff + numPts * 2) 3564 { 3565 // If the arrays overlap partially with the destination higher 3566 // than the source and we transform the coordinates normally 3567 // we would overwrite some of the later source coordinates 3568 // with results of previous transformations. 3569 // To get around this we use arraycopy to copy the points 3570 // to their final destination with correct overwrite 3571 // handling and then transform them in place in the new 3572 // safer location. 3573 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); 3574 // srcPts = dstPts; // They are known to be equal. 3575 srcOff = dstOff; 3576 } 3577 switch (state) { 3578 default: 3579 stateError(); 3580 /* NOTREACHED */ 3581 return; 3582 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3583 M00 = m00; M01 = m01; M02 = m02; 3584 M10 = m10; M11 = m11; M12 = m12; 3585 det = M00 * M11 - M01 * M10; 3586 if (Math.abs(det) <= Double.MIN_VALUE) { 3587 throw new NoninvertibleTransformException("Determinant is "+ 3588 det); 3589 } 3590 while (--numPts >= 0) { 3591 double x = srcPts[srcOff++] - M02; 3592 double y = srcPts[srcOff++] - M12; 3593 dstPts[dstOff++] = (x * M11 - y * M01) / det; 3594 dstPts[dstOff++] = (y * M00 - x * M10) / det; 3595 } 3596 return; 3597 case (APPLY_SHEAR | APPLY_SCALE): 3598 M00 = m00; M01 = m01; 3599 M10 = m10; M11 = m11; 3600 det = M00 * M11 - M01 * M10; 3601 if (Math.abs(det) <= Double.MIN_VALUE) { 3602 throw new NoninvertibleTransformException("Determinant is "+ 3603 det); 3604 } 3605 while (--numPts >= 0) { 3606 double x = srcPts[srcOff++]; 3607 double y = srcPts[srcOff++]; 3608 dstPts[dstOff++] = (x * M11 - y * M01) / det; 3609 dstPts[dstOff++] = (y * M00 - x * M10) / det; 3610 } 3611 return; 3612 case (APPLY_SHEAR | APPLY_TRANSLATE): 3613 M01 = m01; M02 = m02; 3614 M10 = m10; M12 = m12; 3615 if (M01 == 0.0 || M10 == 0.0) { 3616 throw new NoninvertibleTransformException("Determinant is 0"); 3617 } 3618 while (--numPts >= 0) { 3619 double x = srcPts[srcOff++] - M02; 3620 dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10; 3621 dstPts[dstOff++] = x / M01; 3622 } 3623 return; 3624 case (APPLY_SHEAR): 3625 M01 = m01; M10 = m10; 3626 if (M01 == 0.0 || M10 == 0.0) { 3627 throw new NoninvertibleTransformException("Determinant is 0"); 3628 } 3629 while (--numPts >= 0) { 3630 double x = srcPts[srcOff++]; 3631 dstPts[dstOff++] = srcPts[srcOff++] / M10; 3632 dstPts[dstOff++] = x / M01; 3633 } 3634 return; 3635 case (APPLY_SCALE | APPLY_TRANSLATE): 3636 M00 = m00; M02 = m02; 3637 M11 = m11; M12 = m12; 3638 if (M00 == 0.0 || M11 == 0.0) { 3639 throw new NoninvertibleTransformException("Determinant is 0"); 3640 } 3641 while (--numPts >= 0) { 3642 dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00; 3643 dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11; 3644 } 3645 return; 3646 case (APPLY_SCALE): 3647 M00 = m00; M11 = m11; 3648 if (M00 == 0.0 || M11 == 0.0) { 3649 throw new NoninvertibleTransformException("Determinant is 0"); 3650 } 3651 while (--numPts >= 0) { 3652 dstPts[dstOff++] = srcPts[srcOff++] / M00; 3653 dstPts[dstOff++] = srcPts[srcOff++] / M11; 3654 } 3655 return; 3656 case (APPLY_TRANSLATE): 3657 M02 = m02; M12 = m12; 3658 while (--numPts >= 0) { 3659 dstPts[dstOff++] = srcPts[srcOff++] - M02; 3660 dstPts[dstOff++] = srcPts[srcOff++] - M12; 3661 } 3662 return; 3663 case (APPLY_IDENTITY): 3664 if (srcPts != dstPts || srcOff != dstOff) { 3665 System.arraycopy(srcPts, srcOff, dstPts, dstOff, 3666 numPts * 2); 3667 } 3668 return; 3669 } 3670 3671 /* NOTREACHED */ 3672 } 3673 3674 /** 3675 * Transforms the relative distance vector specified by 3676 * {@code ptSrc} and stores the result in {@code ptDst}. 3677 * A relative distance vector is transformed without applying the 3678 * translation components of the affine transformation matrix 3679 * using the following equations: 3680 * <pre> 3681 * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] 3682 * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] 3683 * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] 3684 * </pre> 3685 * If {@code ptDst} is {@code null}, a new 3686 * {@code Point2D} object is allocated and then the result of the 3687 * transform is stored in this object. 3688 * In either case, {@code ptDst}, which contains the 3689 * transformed point, is returned for convenience. 3690 * If {@code ptSrc} and {@code ptDst} are the same object, 3691 * the input point is correctly overwritten with the transformed 3692 * point. 3693 * @param ptSrc the distance vector to be delta transformed 3694 * @param ptDst the resulting transformed distance vector 3695 * @return {@code ptDst}, which contains the result of the 3696 * transformation. 3697 * @since 1.2 3698 */ 3699 public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) { 3700 if (ptDst == null) { 3701 if (ptSrc instanceof Point2D.Double) { 3702 ptDst = new Point2D.Double(); 3703 } else { 3704 ptDst = new Point2D.Float(); 3705 } 3706 } 3707 // Copy source coords into local variables in case src == dst 3708 double x = ptSrc.getX(); 3709 double y = ptSrc.getY(); 3710 switch (state) { 3711 default: 3712 stateError(); 3713 /* NOTREACHED */ 3714 return null; 3715 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3716 case (APPLY_SHEAR | APPLY_SCALE): 3717 ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11); 3718 return ptDst; 3719 case (APPLY_SHEAR | APPLY_TRANSLATE): 3720 case (APPLY_SHEAR): 3721 ptDst.setLocation(y * m01, x * m10); 3722 return ptDst; 3723 case (APPLY_SCALE | APPLY_TRANSLATE): 3724 case (APPLY_SCALE): 3725 ptDst.setLocation(x * m00, y * m11); 3726 return ptDst; 3727 case (APPLY_TRANSLATE): 3728 case (APPLY_IDENTITY): 3729 ptDst.setLocation(x, y); 3730 return ptDst; 3731 } 3732 3733 /* NOTREACHED */ 3734 } 3735 3736 /** 3737 * Transforms an array of relative distance vectors by this 3738 * transform. 3739 * A relative distance vector is transformed without applying the 3740 * translation components of the affine transformation matrix 3741 * using the following equations: 3742 * <pre> 3743 * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] 3744 * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] 3745 * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] 3746 * </pre> 3747 * The two coordinate array sections can be exactly the same or 3748 * can be overlapping sections of the same array without affecting the 3749 * validity of the results. 3750 * This method ensures that no source coordinates are 3751 * overwritten by a previous operation before they can be transformed. 3752 * The coordinates are stored in the arrays starting at the indicated 3753 * offset in the order {@code [x0, y0, x1, y1, ..., xn, yn]}. 3754 * @param srcPts the array containing the source distance vectors. 3755 * Each vector is stored as a pair of relative x, y coordinates. 3756 * @param dstPts the array into which the transformed distance vectors 3757 * are returned. Each vector is stored as a pair of relative 3758 * x, y coordinates. 3759 * @param srcOff the offset to the first vector to be transformed 3760 * in the source array 3761 * @param dstOff the offset to the location of the first 3762 * transformed vector that is stored in the destination array 3763 * @param numPts the number of vector coordinate pairs to be 3764 * transformed 3765 * @since 1.2 3766 */ 3767 public void deltaTransform(double[] srcPts, int srcOff, 3768 double[] dstPts, int dstOff, 3769 int numPts) { 3770 double M00, M01, M10, M11; // For caching 3771 if (dstPts == srcPts && 3772 dstOff > srcOff && dstOff < srcOff + numPts * 2) 3773 { 3774 // If the arrays overlap partially with the destination higher 3775 // than the source and we transform the coordinates normally 3776 // we would overwrite some of the later source coordinates 3777 // with results of previous transformations. 3778 // To get around this we use arraycopy to copy the points 3779 // to their final destination with correct overwrite 3780 // handling and then transform them in place in the new 3781 // safer location. 3782 System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); 3783 // srcPts = dstPts; // They are known to be equal. 3784 srcOff = dstOff; 3785 } 3786 switch (state) { 3787 default: 3788 stateError(); 3789 /* NOTREACHED */ 3790 return; 3791 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): 3792 case (APPLY_SHEAR | APPLY_SCALE): 3793 M00 = m00; M01 = m01; 3794 M10 = m10; M11 = m11; 3795 while (--numPts >= 0) { 3796 double x = srcPts[srcOff++]; 3797 double y = srcPts[srcOff++]; 3798 dstPts[dstOff++] = x * M00 + y * M01; 3799 dstPts[dstOff++] = x * M10 + y * M11; 3800 } 3801 return; 3802 case (APPLY_SHEAR | APPLY_TRANSLATE): 3803 case (APPLY_SHEAR): 3804 M01 = m01; M10 = m10; 3805 while (--numPts >= 0) { 3806 double x = srcPts[srcOff++]; 3807 dstPts[dstOff++] = srcPts[srcOff++] * M01; 3808 dstPts[dstOff++] = x * M10; 3809 } 3810 return; 3811 case (APPLY_SCALE | APPLY_TRANSLATE): 3812 case (APPLY_SCALE): 3813 M00 = m00; M11 = m11; 3814 while (--numPts >= 0) { 3815 dstPts[dstOff++] = srcPts[srcOff++] * M00; 3816 dstPts[dstOff++] = srcPts[srcOff++] * M11; 3817 } 3818 return; 3819 case (APPLY_TRANSLATE): 3820 case (APPLY_IDENTITY): 3821 if (srcPts != dstPts || srcOff != dstOff) { 3822 System.arraycopy(srcPts, srcOff, dstPts, dstOff, 3823 numPts * 2); 3824 } 3825 return; 3826 } 3827 3828 /* NOTREACHED */ 3829 } 3830 3831 /** 3832 * Returns a new {@link Shape} object defined by the geometry of the 3833 * specified {@code Shape} after it has been transformed by 3834 * this transform. 3835 * @param pSrc the specified {@code Shape} object to be 3836 * transformed by this transform. 3837 * @return a new {@code Shape} object that defines the geometry 3838 * of the transformed {@code Shape}, or null if {@code pSrc} is null. 3839 * @since 1.2 3840 */ 3841 public Shape createTransformedShape(Shape pSrc) { 3842 if (pSrc == null) { 3843 return null; 3844 } 3845 return new Path2D.Double(pSrc, this); 3846 } 3847 3848 // Round values to sane precision for printing 3849 // Note that Math.sin(Math.PI) has an error of about 10^-16 3850 private static double _matround(double matval) { 3851 return Math.rint(matval * 1E15) / 1E15; 3852 } 3853 3854 /** 3855 * Returns a {@code String} that represents the value of this 3856 * {@link Object}. 3857 * @return a {@code String} representing the value of this 3858 * {@code Object}. 3859 * @since 1.2 3860 */ 3861 public String toString() { 3862 return ("AffineTransform[[" 3863 + _matround(m00) + ", " 3864 + _matround(m01) + ", " 3865 + _matround(m02) + "], [" 3866 + _matround(m10) + ", " 3867 + _matround(m11) + ", " 3868 + _matround(m12) + "]]"); 3869 } 3870 3871 /** 3872 * Returns {@code true} if this {@code AffineTransform} is 3873 * an identity transform. 3874 * @return {@code true} if this {@code AffineTransform} is 3875 * an identity transform; {@code false} otherwise. 3876 * @since 1.2 3877 */ 3878 public boolean isIdentity() { 3879 return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY)); 3880 } 3881 3882 /** 3883 * Returns a copy of this {@code AffineTransform} object. 3884 * @return an {@code Object} that is a copy of this 3885 * {@code AffineTransform} object. 3886 * @since 1.2 3887 */ 3888 public Object clone() { 3889 try { 3890 return super.clone(); 3891 } catch (CloneNotSupportedException e) { 3892 // this shouldn't happen, since we are Cloneable 3893 throw new InternalError(e); 3894 } 3895 } 3896 3897 /** 3898 * Returns the hashcode for this transform. 3899 * @return a hash code for this transform. 3900 * @since 1.2 3901 */ 3902 public int hashCode() { 3903 long bits = Double.doubleToLongBits(m00); 3904 bits = bits * 31 + Double.doubleToLongBits(m01); 3905 bits = bits * 31 + Double.doubleToLongBits(m02); 3906 bits = bits * 31 + Double.doubleToLongBits(m10); 3907 bits = bits * 31 + Double.doubleToLongBits(m11); 3908 bits = bits * 31 + Double.doubleToLongBits(m12); 3909 return (((int) bits) ^ ((int) (bits >> 32))); 3910 } 3911 3912 /** 3913 * Returns {@code true} if this {@code AffineTransform} 3914 * represents the same affine coordinate transform as the specified 3915 * argument. 3916 * @param obj the {@code Object} to test for equality with this 3917 * {@code AffineTransform} 3918 * @return {@code true} if {@code obj} equals this 3919 * {@code AffineTransform} object; {@code false} otherwise. 3920 * @since 1.2 3921 */ 3922 public boolean equals(Object obj) { 3923 if (!(obj instanceof AffineTransform)) { 3924 return false; 3925 } 3926 3927 AffineTransform a = (AffineTransform)obj; 3928 3929 return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) && 3930 (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12)); 3931 } 3932 3933 /* Serialization support. A readObject method is neccessary because 3934 * the state field is part of the implementation of this particular 3935 * AffineTransform and not part of the public specification. The 3936 * state variable's value needs to be recalculated on the fly by the 3937 * readObject method as it is in the 6-argument matrix constructor. 3938 */ 3939 3940 /* 3941 * JDK 1.2 serialVersionUID 3942 */ 3943 private static final long serialVersionUID = 1330973210523860834L; 3944 3945 private void writeObject(java.io.ObjectOutputStream s) 3946 throws java.io.IOException 3947 { 3948 s.defaultWriteObject(); 3949 } 3950 3951 private void readObject(java.io.ObjectInputStream s) 3952 throws java.lang.ClassNotFoundException, java.io.IOException 3953 { 3954 s.defaultReadObject(); 3955 updateState(); 3956 } 3957 }