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