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