Frames | No Frames |
1: /* AffineTransform.java -- transform coordinates between two 2-D spaces 2: Copyright (C) 2000, 2001, 2002, 2004 Free Software Foundation 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package java.awt.geom; 40: 41: import java.awt.Shape; 42: import java.io.IOException; 43: import java.io.ObjectInputStream; 44: import java.io.Serializable; 45: 46: /** 47: * This class represents an affine transformation between two coordinate 48: * spaces in 2 dimensions. Such a transform preserves the "straightness" 49: * and "parallelness" of lines. The transform is built from a sequence of 50: * translations, scales, flips, rotations, and shears. 51: * 52: * <p>The transformation can be represented using matrix math on a 3x3 array. 53: * Given (x,y), the transformation (x',y') can be found by: 54: * <pre> 55: * [ x'] [ m00 m01 m02 ] [ x ] [ m00*x + m01*y + m02 ] 56: * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10*x + m11*y + m12 ] 57: * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] 58: * </pre> 59: * The bottom row of the matrix is constant, so a transform can be uniquely 60: * represented (as in {@link #toString()}) by 61: * "[[m00, m01, m02], [m10, m11, m12]]". 62: * 63: * @author Tom Tromey (tromey@cygnus.com) 64: * @author Eric Blake (ebb9@email.byu.edu) 65: * @since 1.2 66: * @status partially updated to 1.4, still has some problems 67: */ 68: public class AffineTransform implements Cloneable, Serializable 69: { 70: /** 71: * Compatible with JDK 1.2+. 72: */ 73: private static final long serialVersionUID = 1330973210523860834L; 74: 75: /** 76: * The transformation is the identity (x' = x, y' = y). All other transforms 77: * have either a combination of the appropriate transform flag bits for 78: * their type, or the type GENERAL_TRANSFORM. 79: * 80: * @see #TYPE_TRANSLATION 81: * @see #TYPE_UNIFORM_SCALE 82: * @see #TYPE_GENERAL_SCALE 83: * @see #TYPE_FLIP 84: * @see #TYPE_QUADRANT_ROTATION 85: * @see #TYPE_GENERAL_ROTATION 86: * @see #TYPE_GENERAL_TRANSFORM 87: * @see #getType() 88: */ 89: public static final int TYPE_IDENTITY = 0; 90: 91: /** 92: * The transformation includes a translation - shifting in the x or y 93: * direction without changing length or angles. 94: * 95: * @see #TYPE_IDENTITY 96: * @see #TYPE_UNIFORM_SCALE 97: * @see #TYPE_GENERAL_SCALE 98: * @see #TYPE_FLIP 99: * @see #TYPE_QUADRANT_ROTATION 100: * @see #TYPE_GENERAL_ROTATION 101: * @see #TYPE_GENERAL_TRANSFORM 102: * @see #getType() 103: */ 104: public static final int TYPE_TRANSLATION = 1; 105: 106: /** 107: * The transformation includes a uniform scale - length is scaled in both 108: * the x and y directions by the same amount, without affecting angles. 109: * This is mutually exclusive with TYPE_GENERAL_SCALE. 110: * 111: * @see #TYPE_IDENTITY 112: * @see #TYPE_TRANSLATION 113: * @see #TYPE_GENERAL_SCALE 114: * @see #TYPE_FLIP 115: * @see #TYPE_QUADRANT_ROTATION 116: * @see #TYPE_GENERAL_ROTATION 117: * @see #TYPE_GENERAL_TRANSFORM 118: * @see #TYPE_MASK_SCALE 119: * @see #getType() 120: */ 121: public static final int TYPE_UNIFORM_SCALE = 2; 122: 123: /** 124: * The transformation includes a general scale - length is scaled in either 125: * or both the x and y directions, but by different amounts; without 126: * affecting angles. This is mutually exclusive with TYPE_UNIFORM_SCALE. 127: * 128: * @see #TYPE_IDENTITY 129: * @see #TYPE_TRANSLATION 130: * @see #TYPE_UNIFORM_SCALE 131: * @see #TYPE_FLIP 132: * @see #TYPE_QUADRANT_ROTATION 133: * @see #TYPE_GENERAL_ROTATION 134: * @see #TYPE_GENERAL_TRANSFORM 135: * @see #TYPE_MASK_SCALE 136: * @see #getType() 137: */ 138: public static final int TYPE_GENERAL_SCALE = 4; 139: 140: /** 141: * This constant checks if either variety of scale transform is performed. 142: * 143: * @see #TYPE_UNIFORM_SCALE 144: * @see #TYPE_GENERAL_SCALE 145: */ 146: public static final int TYPE_MASK_SCALE = 6; 147: 148: /** 149: * The transformation includes a flip about an axis, swapping between 150: * right-handed and left-handed coordinate systems. In a right-handed 151: * system, the positive x-axis rotates counter-clockwise to the positive 152: * y-axis; in a left-handed system it rotates clockwise. 153: * 154: * @see #TYPE_IDENTITY 155: * @see #TYPE_TRANSLATION 156: * @see #TYPE_UNIFORM_SCALE 157: * @see #TYPE_GENERAL_SCALE 158: * @see #TYPE_QUADRANT_ROTATION 159: * @see #TYPE_GENERAL_ROTATION 160: * @see #TYPE_GENERAL_TRANSFORM 161: * @see #getType() 162: */ 163: public static final int TYPE_FLIP = 64; 164: 165: /** 166: * The transformation includes a rotation of a multiple of 90 degrees (PI/2 167: * radians). Angles are rotated, but length is preserved. This is mutually 168: * exclusive with TYPE_GENERAL_ROTATION. 169: * 170: * @see #TYPE_IDENTITY 171: * @see #TYPE_TRANSLATION 172: * @see #TYPE_UNIFORM_SCALE 173: * @see #TYPE_GENERAL_SCALE 174: * @see #TYPE_FLIP 175: * @see #TYPE_GENERAL_ROTATION 176: * @see #TYPE_GENERAL_TRANSFORM 177: * @see #TYPE_MASK_ROTATION 178: * @see #getType() 179: */ 180: public static final int TYPE_QUADRANT_ROTATION = 8; 181: 182: /** 183: * The transformation includes a rotation by an arbitrary angle. Angles are 184: * rotated, but length is preserved. This is mutually exclusive with 185: * TYPE_QUADRANT_ROTATION. 186: * 187: * @see #TYPE_IDENTITY 188: * @see #TYPE_TRANSLATION 189: * @see #TYPE_UNIFORM_SCALE 190: * @see #TYPE_GENERAL_SCALE 191: * @see #TYPE_FLIP 192: * @see #TYPE_QUADRANT_ROTATION 193: * @see #TYPE_GENERAL_TRANSFORM 194: * @see #TYPE_MASK_ROTATION 195: * @see #getType() 196: */ 197: public static final int TYPE_GENERAL_ROTATION = 16; 198: 199: /** 200: * This constant checks if either variety of rotation is performed. 201: * 202: * @see #TYPE_QUADRANT_ROTATION 203: * @see #TYPE_GENERAL_ROTATION 204: */ 205: public static final int TYPE_MASK_ROTATION = 24; 206: 207: /** 208: * The transformation is an arbitrary conversion of coordinates which 209: * could not be decomposed into the other TYPEs. 210: * 211: * @see #TYPE_IDENTITY 212: * @see #TYPE_TRANSLATION 213: * @see #TYPE_UNIFORM_SCALE 214: * @see #TYPE_GENERAL_SCALE 215: * @see #TYPE_FLIP 216: * @see #TYPE_QUADRANT_ROTATION 217: * @see #TYPE_GENERAL_ROTATION 218: * @see #getType() 219: */ 220: public static final int TYPE_GENERAL_TRANSFORM = 32; 221: 222: /** 223: * The X coordinate scaling element of the transform matrix. 224: * 225: * @serial matrix[0,0] 226: */ 227: private double m00; 228: 229: /** 230: * The Y coordinate shearing element of the transform matrix. 231: * 232: * @serial matrix[1,0] 233: */ 234: private double m10; 235: 236: /** 237: * The X coordinate shearing element of the transform matrix. 238: * 239: * @serial matrix[0,1] 240: */ 241: private double m01; 242: 243: /** 244: * The Y coordinate scaling element of the transform matrix. 245: * 246: * @serial matrix[1,1] 247: */ 248: private double m11; 249: 250: /** 251: * The X coordinate translation element of the transform matrix. 252: * 253: * @serial matrix[0,2] 254: */ 255: private double m02; 256: 257: /** 258: * The Y coordinate translation element of the transform matrix. 259: * 260: * @serial matrix[1,2] 261: */ 262: private double m12; 263: 264: /** The type of this transform. */ 265: private transient int type; 266: 267: /** 268: * Construct a new identity transform: 269: * <pre> 270: * [ 1 0 0 ] 271: * [ 0 1 0 ] 272: * [ 0 0 1 ] 273: * </pre> 274: */ 275: public AffineTransform() 276: { 277: m00 = m11 = 1; 278: } 279: 280: /** 281: * Create a new transform which copies the given one. 282: * 283: * @param tx the transform to copy 284: * @throws NullPointerException if tx is null 285: */ 286: public AffineTransform(AffineTransform tx) 287: { 288: setTransform(tx); 289: } 290: 291: /** 292: * Construct a transform with the given matrix entries: 293: * <pre> 294: * [ m00 m01 m02 ] 295: * [ m10 m11 m12 ] 296: * [ 0 0 1 ] 297: * </pre> 298: * 299: * @param m00 the x scaling component 300: * @param m10 the y shearing component 301: * @param m01 the x shearing component 302: * @param m11 the y scaling component 303: * @param m02 the x translation component 304: * @param m12 the y translation component 305: */ 306: public AffineTransform(float m00, float m10, 307: float m01, float m11, 308: float m02, float m12) 309: { 310: this.m00 = m00; 311: this.m10 = m10; 312: this.m01 = m01; 313: this.m11 = m11; 314: this.m02 = m02; 315: this.m12 = m12; 316: updateType(); 317: } 318: 319: /** 320: * Construct a transform from a sequence of float entries. The array must 321: * have at least 4 entries, which has a translation factor of 0; or 6 322: * entries, for specifying all parameters: 323: * <pre> 324: * [ f[0] f[2] (f[4]) ] 325: * [ f[1] f[3] (f[5]) ] 326: * [ 0 0 1 ] 327: * </pre> 328: * 329: * @param f the matrix to copy from, with at least 4 (6) entries 330: * @throws NullPointerException if f is null 331: * @throws ArrayIndexOutOfBoundsException if f is too small 332: */ 333: public AffineTransform(float[] f) 334: { 335: m00 = f[0]; 336: m10 = f[1]; 337: m01 = f[2]; 338: m11 = f[3]; 339: if (f.length >= 6) 340: { 341: m02 = f[4]; 342: m12 = f[5]; 343: } 344: updateType(); 345: } 346: 347: /** 348: * Construct a transform with the given matrix entries: 349: * <pre> 350: * [ m00 m01 m02 ] 351: * [ m10 m11 m12 ] 352: * [ 0 0 1 ] 353: * </pre> 354: * 355: * @param m00 the x scaling component 356: * @param m10 the y shearing component 357: * @param m01 the x shearing component 358: * @param m11 the y scaling component 359: * @param m02 the x translation component 360: * @param m12 the y translation component 361: */ 362: public AffineTransform(double m00, double m10, double m01, 363: double m11, double m02, double m12) 364: { 365: this.m00 = m00; 366: this.m10 = m10; 367: this.m01 = m01; 368: this.m11 = m11; 369: this.m02 = m02; 370: this.m12 = m12; 371: updateType(); 372: } 373: 374: /** 375: * Construct a transform from a sequence of double entries. The array must 376: * have at least 4 entries, which has a translation factor of 0; or 6 377: * entries, for specifying all parameters: 378: * <pre> 379: * [ d[0] d[2] (d[4]) ] 380: * [ d[1] d[3] (d[5]) ] 381: * [ 0 0 1 ] 382: * </pre> 383: * 384: * @param d the matrix to copy from, with at least 4 (6) entries 385: * @throws NullPointerException if d is null 386: * @throws ArrayIndexOutOfBoundsException if d is too small 387: */ 388: public AffineTransform(double[] d) 389: { 390: m00 = d[0]; 391: m10 = d[1]; 392: m01 = d[2]; 393: m11 = d[3]; 394: if (d.length >= 6) 395: { 396: m02 = d[4]; 397: m12 = d[5]; 398: } 399: updateType(); 400: } 401: 402: /** 403: * Returns a translation transform: 404: * <pre> 405: * [ 1 0 tx ] 406: * [ 0 1 ty ] 407: * [ 0 0 1 ] 408: * </pre> 409: * 410: * @param tx the x translation distance 411: * @param ty the y translation distance 412: * @return the translating transform 413: */ 414: public static AffineTransform getTranslateInstance(double tx, double ty) 415: { 416: AffineTransform t = new AffineTransform(); 417: t.m02 = tx; 418: t.m12 = ty; 419: t.type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; 420: return t; 421: } 422: 423: /** 424: * Returns a rotation transform. A positive angle (in radians) rotates 425: * the positive x-axis to the positive y-axis: 426: * <pre> 427: * [ cos(theta) -sin(theta) 0 ] 428: * [ sin(theta) cos(theta) 0 ] 429: * [ 0 0 1 ] 430: * </pre> 431: * 432: * @param theta the rotation angle 433: * @return the rotating transform 434: */ 435: public static AffineTransform getRotateInstance(double theta) 436: { 437: AffineTransform t = new AffineTransform(); 438: t.setToRotation(theta); 439: return t; 440: } 441: 442: /** 443: * Returns a rotation transform about a point. A positive angle (in radians) 444: * rotates the positive x-axis to the positive y-axis. This is the same 445: * as calling: 446: * <pre> 447: * AffineTransform tx = new AffineTransform(); 448: * tx.setToTranslation(x, y); 449: * tx.rotate(theta); 450: * tx.translate(-x, -y); 451: * </pre> 452: * 453: * <p>The resulting matrix is: 454: * <pre> 455: * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 456: * [ sin(theta) cos(theta) y-x*sin-y*cos ] 457: * [ 0 0 1 ] 458: * </pre> 459: * 460: * @param theta the rotation angle 461: * @param x the x coordinate of the pivot point 462: * @param y the y coordinate of the pivot point 463: * @return the rotating transform 464: */ 465: public static AffineTransform getRotateInstance(double theta, 466: double x, double y) 467: { 468: AffineTransform t = new AffineTransform(); 469: t.setToTranslation(x, y); 470: t.rotate(theta); 471: t.translate(-x, -y); 472: return t; 473: } 474: 475: /** 476: * Returns a scaling transform: 477: * <pre> 478: * [ sx 0 0 ] 479: * [ 0 sy 0 ] 480: * [ 0 0 1 ] 481: * </pre> 482: * 483: * @param sx the x scaling factor 484: * @param sy the y scaling factor 485: * @return the scaling transform 486: */ 487: public static AffineTransform getScaleInstance(double sx, double sy) 488: { 489: AffineTransform t = new AffineTransform(); 490: t.setToScale(sx, sy); 491: return t; 492: } 493: 494: /** 495: * Returns a shearing transform (points are shifted in the x direction based 496: * on a factor of their y coordinate, and in the y direction as a factor of 497: * their x coordinate): 498: * <pre> 499: * [ 1 shx 0 ] 500: * [ shy 1 0 ] 501: * [ 0 0 1 ] 502: * </pre> 503: * 504: * @param shx the x shearing factor 505: * @param shy the y shearing factor 506: * @return the shearing transform 507: */ 508: public static AffineTransform getShearInstance(double shx, double shy) 509: { 510: AffineTransform t = new AffineTransform(); 511: t.setToShear(shx, shy); 512: return t; 513: } 514: 515: /** 516: * Returns the type of this transform. The result is always valid, although 517: * it may not be the simplest interpretation (in other words, there are 518: * sequences of transforms which reduce to something simpler, which this 519: * does not always detect). The result is either TYPE_GENERAL_TRANSFORM, 520: * or a bit-wise combination of TYPE_TRANSLATION, the mutually exclusive 521: * TYPE_*_ROTATIONs, and the mutually exclusive TYPE_*_SCALEs. 522: * 523: * @return The type. 524: * 525: * @see #TYPE_IDENTITY 526: * @see #TYPE_TRANSLATION 527: * @see #TYPE_UNIFORM_SCALE 528: * @see #TYPE_GENERAL_SCALE 529: * @see #TYPE_QUADRANT_ROTATION 530: * @see #TYPE_GENERAL_ROTATION 531: * @see #TYPE_GENERAL_TRANSFORM 532: */ 533: public int getType() 534: { 535: return type; 536: } 537: 538: /** 539: * Return the determinant of this transform matrix. If the determinant is 540: * non-zero, the transform is invertible; otherwise operations which require 541: * an inverse throw a NoninvertibleTransformException. A result very near 542: * zero, due to rounding errors, may indicate that inversion results do not 543: * carry enough precision to be meaningful. 544: * 545: * <p>If this is a uniform scale transformation, the determinant also 546: * represents the squared value of the scale. Otherwise, it carries little 547: * additional meaning. The determinant is calculated as: 548: * <pre> 549: * | m00 m01 m02 | 550: * | m10 m11 m12 | = m00 * m11 - m01 * m10 551: * | 0 0 1 | 552: * </pre> 553: * 554: * @return the determinant 555: * @see #createInverse() 556: */ 557: public double getDeterminant() 558: { 559: return m00 * m11 - m01 * m10; 560: } 561: 562: /** 563: * Return the matrix of values used in this transform. If the matrix has 564: * fewer than 6 entries, only the scale and shear factors are returned; 565: * otherwise the translation factors are copied as well. The resulting 566: * values are: 567: * <pre> 568: * [ d[0] d[2] (d[4]) ] 569: * [ d[1] d[3] (d[5]) ] 570: * [ 0 0 1 ] 571: * </pre> 572: * 573: * @param d the matrix to store the results into; with 4 (6) entries 574: * @throws NullPointerException if d is null 575: * @throws ArrayIndexOutOfBoundsException if d is too small 576: */ 577: public void getMatrix(double[] d) 578: { 579: d[0] = m00; 580: d[1] = m10; 581: d[2] = m01; 582: d[3] = m11; 583: if (d.length >= 6) 584: { 585: d[4] = m02; 586: d[5] = m12; 587: } 588: } 589: 590: /** 591: * Returns the X coordinate scaling factor of the matrix. 592: * 593: * @return m00 594: * @see #getMatrix(double[]) 595: */ 596: public double getScaleX() 597: { 598: return m00; 599: } 600: 601: /** 602: * Returns the Y coordinate scaling factor of the matrix. 603: * 604: * @return m11 605: * @see #getMatrix(double[]) 606: */ 607: public double getScaleY() 608: { 609: return m11; 610: } 611: 612: /** 613: * Returns the X coordinate shearing factor of the matrix. 614: * 615: * @return m01 616: * @see #getMatrix(double[]) 617: */ 618: public double getShearX() 619: { 620: return m01; 621: } 622: 623: /** 624: * Returns the Y coordinate shearing factor of the matrix. 625: * 626: * @return m10 627: * @see #getMatrix(double[]) 628: */ 629: public double getShearY() 630: { 631: return m10; 632: } 633: 634: /** 635: * Returns the X coordinate translation factor of the matrix. 636: * 637: * @return m02 638: * @see #getMatrix(double[]) 639: */ 640: public double getTranslateX() 641: { 642: return m02; 643: } 644: 645: /** 646: * Returns the Y coordinate translation factor of the matrix. 647: * 648: * @return m12 649: * @see #getMatrix(double[]) 650: */ 651: public double getTranslateY() 652: { 653: return m12; 654: } 655: 656: /** 657: * Concatenate a translation onto this transform. This is equivalent, but 658: * more efficient than 659: * <code>concatenate(AffineTransform.getTranslateInstance(tx, ty))</code>. 660: * 661: * @param tx the x translation distance 662: * @param ty the y translation distance 663: * @see #getTranslateInstance(double, double) 664: * @see #concatenate(AffineTransform) 665: */ 666: public void translate(double tx, double ty) 667: { 668: m02 += tx * m00 + ty * m01; 669: m12 += tx * m10 + ty * m11; 670: updateType(); 671: } 672: 673: /** 674: * Concatenate a rotation onto this transform. This is equivalent, but 675: * more efficient than 676: * <code>concatenate(AffineTransform.getRotateInstance(theta))</code>. 677: * 678: * @param theta the rotation angle 679: * @see #getRotateInstance(double) 680: * @see #concatenate(AffineTransform) 681: */ 682: public void rotate(double theta) 683: { 684: double c = Math.cos(theta); 685: double s = Math.sin(theta); 686: double n00 = m00 * c + m01 * s; 687: double n01 = m00 * -s + m01 * c; 688: double n10 = m10 * c + m11 * s; 689: double n11 = m10 * -s + m11 * c; 690: m00 = n00; 691: m01 = n01; 692: m10 = n10; 693: m11 = n11; 694: updateType(); 695: } 696: 697: /** 698: * Concatenate a rotation about a point onto this transform. This is 699: * equivalent, but more efficient than 700: * <code>concatenate(AffineTransform.getRotateInstance(theta, x, y))</code>. 701: * 702: * @param theta the rotation angle 703: * @param x the x coordinate of the pivot point 704: * @param y the y coordinate of the pivot point 705: * @see #getRotateInstance(double, double, double) 706: * @see #concatenate(AffineTransform) 707: */ 708: public void rotate(double theta, double x, double y) 709: { 710: translate(x, y); 711: rotate(theta); 712: translate(-x, -y); 713: } 714: 715: /** 716: * Concatenate a scale onto this transform. This is equivalent, but more 717: * efficient than 718: * <code>concatenate(AffineTransform.getScaleInstance(sx, sy))</code>. 719: * 720: * @param sx the x scaling factor 721: * @param sy the y scaling factor 722: * @see #getScaleInstance(double, double) 723: * @see #concatenate(AffineTransform) 724: */ 725: public void scale(double sx, double sy) 726: { 727: m00 *= sx; 728: m01 *= sy; 729: m10 *= sx; 730: m11 *= sy; 731: updateType(); 732: } 733: 734: /** 735: * Concatenate a shearing onto this transform. This is equivalent, but more 736: * efficient than 737: * <code>concatenate(AffineTransform.getShearInstance(sx, sy))</code>. 738: * 739: * @param shx the x shearing factor 740: * @param shy the y shearing factor 741: * @see #getShearInstance(double, double) 742: * @see #concatenate(AffineTransform) 743: */ 744: public void shear(double shx, double shy) 745: { 746: double n00 = m00 + (shy * m01); 747: double n01 = m01 + (shx * m00); 748: double n10 = m10 + (shy * m11); 749: double n11 = m11 + (shx * m10); 750: m00 = n00; 751: m01 = n01; 752: m10 = n10; 753: m11 = n11; 754: updateType(); 755: } 756: 757: /** 758: * Reset this transform to the identity (no transformation): 759: * <pre> 760: * [ 1 0 0 ] 761: * [ 0 1 0 ] 762: * [ 0 0 1 ] 763: * </pre> 764: */ 765: public void setToIdentity() 766: { 767: m00 = m11 = 1; 768: m01 = m02 = m10 = m12 = 0; 769: type = TYPE_IDENTITY; 770: } 771: 772: /** 773: * Set this transform to a translation: 774: * <pre> 775: * [ 1 0 tx ] 776: * [ 0 1 ty ] 777: * [ 0 0 1 ] 778: * </pre> 779: * 780: * @param tx the x translation distance 781: * @param ty the y translation distance 782: */ 783: public void setToTranslation(double tx, double ty) 784: { 785: m00 = m11 = 1; 786: m01 = m10 = 0; 787: m02 = tx; 788: m12 = ty; 789: type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; 790: } 791: 792: /** 793: * Set this transform to a rotation. A positive angle (in radians) rotates 794: * the positive x-axis to the positive y-axis: 795: * <pre> 796: * [ cos(theta) -sin(theta) 0 ] 797: * [ sin(theta) cos(theta) 0 ] 798: * [ 0 0 1 ] 799: * </pre> 800: * 801: * @param theta the rotation angle 802: */ 803: public void setToRotation(double theta) 804: { 805: double c = Math.cos(theta); 806: double s = Math.sin(theta); 807: m00 = c; 808: m01 = -s; 809: m02 = 0; 810: m10 = s; 811: m11 = c; 812: m12 = 0; 813: type = (c == 1 ? TYPE_IDENTITY 814: : c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION 815: : TYPE_GENERAL_ROTATION); 816: } 817: 818: /** 819: * Set this transform to a rotation about a point. A positive angle (in 820: * radians) rotates the positive x-axis to the positive y-axis. This is the 821: * same as calling: 822: * <pre> 823: * tx.setToTranslation(x, y); 824: * tx.rotate(theta); 825: * tx.translate(-x, -y); 826: * </pre> 827: * 828: * <p>The resulting matrix is: 829: * <pre> 830: * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 831: * [ sin(theta) cos(theta) y-x*sin-y*cos ] 832: * [ 0 0 1 ] 833: * </pre> 834: * 835: * @param theta the rotation angle 836: * @param x the x coordinate of the pivot point 837: * @param y the y coordinate of the pivot point 838: */ 839: public void setToRotation(double theta, double x, double y) 840: { 841: double c = Math.cos(theta); 842: double s = Math.sin(theta); 843: m00 = c; 844: m01 = -s; 845: m02 = x - x * c + y * s; 846: m10 = s; 847: m11 = c; 848: m12 = y - x * s - y * c; 849: updateType(); 850: } 851: 852: /** 853: * Set this transform to a scale: 854: * <pre> 855: * [ sx 0 0 ] 856: * [ 0 sy 0 ] 857: * [ 0 0 1 ] 858: * </pre> 859: * 860: * @param sx the x scaling factor 861: * @param sy the y scaling factor 862: */ 863: public void setToScale(double sx, double sy) 864: { 865: m00 = sx; 866: m01 = m02 = m10 = m12 = 0; 867: m11 = sy; 868: type = (sx != sy ? TYPE_GENERAL_SCALE 869: : sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE); 870: } 871: 872: /** 873: * Set this transform to a shear (points are shifted in the x direction based 874: * on a factor of their y coordinate, and in the y direction as a factor of 875: * their x coordinate): 876: * <pre> 877: * [ 1 shx 0 ] 878: * [ shy 1 0 ] 879: * [ 0 0 1 ] 880: * </pre> 881: * 882: * @param shx the x shearing factor 883: * @param shy the y shearing factor 884: */ 885: public void setToShear(double shx, double shy) 886: { 887: m00 = m11 = 1; 888: m01 = shx; 889: m10 = shy; 890: m02 = m12 = 0; 891: updateType(); 892: } 893: 894: /** 895: * Set this transform to a copy of the given one. 896: * 897: * @param tx the transform to copy 898: * @throws NullPointerException if tx is null 899: */ 900: public void setTransform(AffineTransform tx) 901: { 902: m00 = tx.m00; 903: m01 = tx.m01; 904: m02 = tx.m02; 905: m10 = tx.m10; 906: m11 = tx.m11; 907: m12 = tx.m12; 908: type = tx.type; 909: } 910: 911: /** 912: * Set this transform to the given values: 913: * <pre> 914: * [ m00 m01 m02 ] 915: * [ m10 m11 m12 ] 916: * [ 0 0 1 ] 917: * </pre> 918: * 919: * @param m00 the x scaling component 920: * @param m10 the y shearing component 921: * @param m01 the x shearing component 922: * @param m11 the y scaling component 923: * @param m02 the x translation component 924: * @param m12 the y translation component 925: */ 926: public void setTransform(double m00, double m10, double m01, 927: double m11, double m02, double m12) 928: { 929: this.m00 = m00; 930: this.m10 = m10; 931: this.m01 = m01; 932: this.m11 = m11; 933: this.m02 = m02; 934: this.m12 = m12; 935: updateType(); 936: } 937: 938: /** 939: * Set this transform to the result of performing the original version of 940: * this followed by tx. This is commonly used when chaining transformations 941: * from one space to another. In matrix form: 942: * <pre> 943: * [ this ] = [ this ] x [ tx ] 944: * </pre> 945: * 946: * @param tx the transform to concatenate 947: * @throws NullPointerException if tx is null 948: * @see #preConcatenate(AffineTransform) 949: */ 950: public void concatenate(AffineTransform tx) 951: { 952: double n00 = m00 * tx.m00 + m01 * tx.m10; 953: double n01 = m00 * tx.m01 + m01 * tx.m11; 954: double n02 = m00 * tx.m02 + m01 * tx.m12 + m02; 955: double n10 = m10 * tx.m00 + m11 * tx.m10; 956: double n11 = m10 * tx.m01 + m11 * tx.m11; 957: double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; 958: m00 = n00; 959: m01 = n01; 960: m02 = n02; 961: m10 = n10; 962: m11 = n11; 963: m12 = n12; 964: updateType(); 965: } 966: 967: /** 968: * Set this transform to the result of performing tx followed by the 969: * original version of this. This is less common than normal concatenation, 970: * but can still be used to chain transformations from one space to another. 971: * In matrix form: 972: * <pre> 973: * [ this ] = [ tx ] x [ this ] 974: * </pre> 975: * 976: * @param tx the transform to concatenate 977: * @throws NullPointerException if tx is null 978: * @see #concatenate(AffineTransform) 979: */ 980: public void preConcatenate(AffineTransform tx) 981: { 982: double n00 = tx.m00 * m00 + tx.m01 * m10; 983: double n01 = tx.m00 * m01 + tx.m01 * m11; 984: double n02 = tx.m00 * m02 + tx.m01 * m12 + tx.m02; 985: double n10 = tx.m10 * m00 + tx.m11 * m10; 986: double n11 = tx.m10 * m01 + tx.m11 * m11; 987: double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; 988: m00 = n00; 989: m01 = n01; 990: m02 = n02; 991: m10 = n10; 992: m11 = n11; 993: m12 = n12; 994: updateType(); 995: } 996: 997: /** 998: * Returns a transform, which if concatenated to this one, will result in 999: * the identity transform. This is useful for undoing transformations, but 1000: * is only possible if the original transform has an inverse (ie. does not 1001: * map multiple points to the same line or point). A transform exists only 1002: * if getDeterminant() has a non-zero value. 1003: * 1004: * The inverse is calculated as: 1005: * 1006: * <pre> 1007: * 1008: * Let A be the matrix for which we want to find the inverse: 1009: * 1010: * A = [ m00 m01 m02 ] 1011: * [ m10 m11 m12 ] 1012: * [ 0 0 1 ] 1013: * 1014: * 1015: * 1 1016: * inverse (A) = --- x adjoint(A) 1017: * det 1018: * 1019: * 1020: * 1021: * = 1 [ m11 -m01 m01*m12-m02*m11 ] 1022: * --- x [ -m10 m00 -m00*m12+m10*m02 ] 1023: * det [ 0 0 m00*m11-m10*m01 ] 1024: * 1025: * 1026: * 1027: * = [ m11/det -m01/det m01*m12-m02*m11/det ] 1028: * [ -m10/det m00/det -m00*m12+m10*m02/det ] 1029: * [ 0 0 1 ] 1030: * 1031: * 1032: * </pre> 1033: * 1034: * 1035: * 1036: * @return a new inverse transform 1037: * @throws NoninvertibleTransformException if inversion is not possible 1038: * @see #getDeterminant() 1039: */ 1040: public AffineTransform createInverse() 1041: throws NoninvertibleTransformException 1042: { 1043: double det = getDeterminant(); 1044: if (det == 0) 1045: throw new NoninvertibleTransformException("can't invert transform"); 1046: 1047: double im00 = m11 / det; 1048: double im10 = -m10 / det; 1049: double im01 = -m01 / det; 1050: double im11 = m00 / det; 1051: double im02 = (m01 * m12 - m02 * m11) / det; 1052: double im12 = (-m00 * m12 + m10 * m02) / det; 1053: 1054: return new AffineTransform (im00, im10, im01, im11, im02, im12); 1055: } 1056: 1057: /** 1058: * Perform this transformation on the given source point, and store the 1059: * result in the destination (creating it if necessary). It is safe for 1060: * src and dst to be the same. 1061: * 1062: * @param src the source point 1063: * @param dst the destination, or null 1064: * @return the transformation of src, in dst if it was non-null 1065: * @throws NullPointerException if src is null 1066: */ 1067: public Point2D transform(Point2D src, Point2D dst) 1068: { 1069: if (dst == null) 1070: dst = new Point2D.Double(); 1071: double x = src.getX(); 1072: double y = src.getY(); 1073: double nx = m00 * x + m01 * y + m02; 1074: double ny = m10 * x + m11 * y + m12; 1075: dst.setLocation(nx, ny); 1076: return dst; 1077: } 1078: 1079: /** 1080: * Perform this transformation on an array of points, storing the results 1081: * in another (possibly same) array. This will not create a destination 1082: * array, but will create points for the null entries of the destination. 1083: * The transformation is done sequentially. While having a single source 1084: * and destination point be the same is safe, you should be aware that 1085: * duplicate references to the same point in the source, and having the 1086: * source overlap the destination, may result in your source points changing 1087: * from a previous transform before it is their turn to be evaluated. 1088: * 1089: * @param src the array of source points 1090: * @param srcOff the starting offset into src 1091: * @param dst the array of destination points (may have null entries) 1092: * @param dstOff the starting offset into dst 1093: * @param num the number of points to transform 1094: * @throws NullPointerException if src or dst is null, or src has null 1095: * entries 1096: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1097: * @throws ArrayStoreException if new points are incompatible with dst 1098: */ 1099: public void transform(Point2D[] src, int srcOff, 1100: Point2D[] dst, int dstOff, int num) 1101: { 1102: while (--num >= 0) 1103: dst[dstOff] = transform(src[srcOff++], dst[dstOff++]); 1104: } 1105: 1106: /** 1107: * Perform this transformation on an array of points, in (x,y) pairs, 1108: * storing the results in another (possibly same) array. This will not 1109: * create a destination array. All sources are copied before the 1110: * transformation, so that no result will overwrite a point that has not yet 1111: * been evaluated. 1112: * 1113: * @param srcPts the array of source points 1114: * @param srcOff the starting offset into src 1115: * @param dstPts the array of destination points 1116: * @param dstOff the starting offset into dst 1117: * @param num the number of points to transform 1118: * @throws NullPointerException if src or dst is null 1119: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1120: */ 1121: public void transform(float[] srcPts, int srcOff, 1122: float[] dstPts, int dstOff, int num) 1123: { 1124: if (srcPts == dstPts && dstOff > srcOff 1125: && num > 1 && srcOff + 2 * num > dstOff) 1126: { 1127: float[] f = new float[2 * num]; 1128: System.arraycopy(srcPts, srcOff, f, 0, 2 * num); 1129: srcPts = f; 1130: } 1131: while (--num >= 0) 1132: { 1133: float x = srcPts[srcOff++]; 1134: float y = srcPts[srcOff++]; 1135: dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); 1136: dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); 1137: } 1138: } 1139: 1140: /** 1141: * Perform this transformation on an array of points, in (x,y) pairs, 1142: * storing the results in another (possibly same) array. This will not 1143: * create a destination array. All sources are copied before the 1144: * transformation, so that no result will overwrite a point that has not yet 1145: * been evaluated. 1146: * 1147: * @param srcPts the array of source points 1148: * @param srcOff the starting offset into src 1149: * @param dstPts the array of destination points 1150: * @param dstOff the starting offset into dst 1151: * @param num the number of points to transform 1152: * @throws NullPointerException if src or dst is null 1153: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1154: */ 1155: public void transform(double[] srcPts, int srcOff, 1156: double[] dstPts, int dstOff, int num) 1157: { 1158: if (srcPts == dstPts && dstOff > srcOff 1159: && num > 1 && srcOff + 2 * num > dstOff) 1160: { 1161: double[] d = new double[2 * num]; 1162: System.arraycopy(srcPts, srcOff, d, 0, 2 * num); 1163: srcPts = d; 1164: } 1165: while (--num >= 0) 1166: { 1167: double x = srcPts[srcOff++]; 1168: double y = srcPts[srcOff++]; 1169: dstPts[dstOff++] = m00 * x + m01 * y + m02; 1170: dstPts[dstOff++] = m10 * x + m11 * y + m12; 1171: } 1172: } 1173: 1174: /** 1175: * Perform this transformation on an array of points, in (x,y) pairs, 1176: * storing the results in another array. This will not create a destination 1177: * array. 1178: * 1179: * @param srcPts the array of source points 1180: * @param srcOff the starting offset into src 1181: * @param dstPts the array of destination points 1182: * @param dstOff the starting offset into dst 1183: * @param num the number of points to transform 1184: * @throws NullPointerException if src or dst is null 1185: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1186: */ 1187: public void transform(float[] srcPts, int srcOff, 1188: double[] dstPts, int dstOff, int num) 1189: { 1190: while (--num >= 0) 1191: { 1192: float x = srcPts[srcOff++]; 1193: float y = srcPts[srcOff++]; 1194: dstPts[dstOff++] = m00 * x + m01 * y + m02; 1195: dstPts[dstOff++] = m10 * x + m11 * y + m12; 1196: } 1197: } 1198: 1199: /** 1200: * Perform this transformation on an array of points, in (x,y) pairs, 1201: * storing the results in another array. This will not create a destination 1202: * array. 1203: * 1204: * @param srcPts the array of source points 1205: * @param srcOff the starting offset into src 1206: * @param dstPts the array of destination points 1207: * @param dstOff the starting offset into dst 1208: * @param num the number of points to transform 1209: * @throws NullPointerException if src or dst is null 1210: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1211: */ 1212: public void transform(double[] srcPts, int srcOff, 1213: float[] dstPts, int dstOff, int num) 1214: { 1215: while (--num >= 0) 1216: { 1217: double x = srcPts[srcOff++]; 1218: double y = srcPts[srcOff++]; 1219: dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); 1220: dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); 1221: } 1222: } 1223: 1224: /** 1225: * Perform the inverse of this transformation on the given source point, 1226: * and store the result in the destination (creating it if necessary). It 1227: * is safe for src and dst to be the same. 1228: * 1229: * @param src the source point 1230: * @param dst the destination, or null 1231: * @return the inverse transformation of src, in dst if it was non-null 1232: * @throws NullPointerException if src is null 1233: * @throws NoninvertibleTransformException if the inverse does not exist 1234: * @see #getDeterminant() 1235: */ 1236: public Point2D inverseTransform(Point2D src, Point2D dst) 1237: throws NoninvertibleTransformException 1238: { 1239: return createInverse().transform(src, dst); 1240: } 1241: 1242: /** 1243: * Perform the inverse of this transformation on an array of points, in 1244: * (x,y) pairs, storing the results in another (possibly same) array. This 1245: * will not create a destination array. All sources are copied before the 1246: * transformation, so that no result will overwrite a point that has not yet 1247: * been evaluated. 1248: * 1249: * @param srcPts the array of source points 1250: * @param srcOff the starting offset into src 1251: * @param dstPts the array of destination points 1252: * @param dstOff the starting offset into dst 1253: * @param num the number of points to transform 1254: * @throws NullPointerException if src or dst is null 1255: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1256: * @throws NoninvertibleTransformException if the inverse does not exist 1257: * @see #getDeterminant() 1258: */ 1259: public void inverseTransform(double[] srcPts, int srcOff, 1260: double[] dstPts, int dstOff, int num) 1261: throws NoninvertibleTransformException 1262: { 1263: createInverse().transform(srcPts, srcOff, dstPts, dstOff, num); 1264: } 1265: 1266: /** 1267: * Perform this transformation, less any translation, on the given source 1268: * point, and store the result in the destination (creating it if 1269: * necessary). It is safe for src and dst to be the same. The reduced 1270: * transform is equivalent to: 1271: * <pre> 1272: * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] 1273: * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] 1274: * </pre> 1275: * 1276: * @param src the source point 1277: * @param dst the destination, or null 1278: * @return the delta transformation of src, in dst if it was non-null 1279: * @throws NullPointerException if src is null 1280: */ 1281: public Point2D deltaTransform(Point2D src, Point2D dst) 1282: { 1283: if (dst == null) 1284: dst = new Point2D.Double(); 1285: double x = src.getX(); 1286: double y = src.getY(); 1287: double nx = m00 * x + m01 * y; 1288: double ny = m10 * x + m11 * y; 1289: dst.setLocation(nx, ny); 1290: return dst; 1291: } 1292: 1293: /** 1294: * Perform this transformation, less any translation, on an array of points, 1295: * in (x,y) pairs, storing the results in another (possibly same) array. 1296: * This will not create a destination array. All sources are copied before 1297: * the transformation, so that no result will overwrite a point that has 1298: * not yet been evaluated. The reduced transform is equivalent to: 1299: * <pre> 1300: * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] 1301: * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] 1302: * </pre> 1303: * 1304: * @param srcPts the array of source points 1305: * @param srcOff the starting offset into src 1306: * @param dstPts the array of destination points 1307: * @param dstOff the starting offset into dst 1308: * @param num the number of points to transform 1309: * @throws NullPointerException if src or dst is null 1310: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1311: */ 1312: public void deltaTransform(double[] srcPts, int srcOff, 1313: double[] dstPts, int dstOff, 1314: int num) 1315: { 1316: if (srcPts == dstPts && dstOff > srcOff 1317: && num > 1 && srcOff + 2 * num > dstOff) 1318: { 1319: double[] d = new double[2 * num]; 1320: System.arraycopy(srcPts, srcOff, d, 0, 2 * num); 1321: srcPts = d; 1322: } 1323: while (--num >= 0) 1324: { 1325: double x = srcPts[srcOff++]; 1326: double y = srcPts[srcOff++]; 1327: dstPts[dstOff++] = m00 * x + m01 * y; 1328: dstPts[dstOff++] = m10 * x + m11 * y; 1329: } 1330: } 1331: 1332: /** 1333: * Return a new Shape, based on the given one, where the path of the shape 1334: * has been transformed by this transform. Notice that this uses GeneralPath, 1335: * which only stores points in float precision. 1336: * 1337: * @param src the shape source to transform 1338: * @return the shape, transformed by this, <code>null</code> if src is 1339: * <code>null</code>. 1340: * @see GeneralPath#transform(AffineTransform) 1341: */ 1342: public Shape createTransformedShape(Shape src) 1343: { 1344: if(src == null) 1345: return null; 1346: GeneralPath p = new GeneralPath(src); 1347: p.transform(this); 1348: return p; 1349: } 1350: 1351: /** 1352: * Returns a string representation of the transform, in the format: 1353: * <code>"AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" 1354: * + m10 + ", " + m11 + ", " + m12 + "]]"</code>. 1355: * 1356: * @return the string representation 1357: */ 1358: public String toString() 1359: { 1360: return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" 1361: + m10 + ", " + m11 + ", " + m12 + "]]"; 1362: } 1363: 1364: /** 1365: * Tests if this transformation is the identity: 1366: * <pre> 1367: * [ 1 0 0 ] 1368: * [ 0 1 0 ] 1369: * [ 0 0 1 ] 1370: * </pre> 1371: * 1372: * @return true if this is the identity transform 1373: */ 1374: public boolean isIdentity() 1375: { 1376: // Rather than rely on type, check explicitly. 1377: return (m00 == 1 && m01 == 0 && m02 == 0 1378: && m10 == 0 && m11 == 1 && m12 == 0); 1379: } 1380: 1381: /** 1382: * Create a new transform of the same run-time type, with the same 1383: * transforming properties as this one. 1384: * 1385: * @return the clone 1386: */ 1387: public Object clone() 1388: { 1389: try 1390: { 1391: return super.clone(); 1392: } 1393: catch (CloneNotSupportedException e) 1394: { 1395: throw (Error) new InternalError().initCause(e); // Impossible 1396: } 1397: } 1398: 1399: /** 1400: * Return the hashcode for this transformation. The formula is not 1401: * documented, but appears to be the same as: 1402: * <pre> 1403: * long l = Double.doubleToLongBits(getScaleX()); 1404: * l = l * 31 + Double.doubleToLongBits(getShearX()); 1405: * l = l * 31 + Double.doubleToLongBits(getTranslateX()); 1406: * l = l * 31 + Double.doubleToLongBits(getShearY()); 1407: * l = l * 31 + Double.doubleToLongBits(getScaleY()); 1408: * l = l * 31 + Double.doubleToLongBits(getTranslateY()); 1409: * return (int) ((l >> 32) ^ l); 1410: * </pre> 1411: * 1412: * @return the hashcode 1413: */ 1414: public int hashCode() 1415: { 1416: long l = Double.doubleToLongBits(m00); 1417: l = l * 31 + Double.doubleToLongBits(m01); 1418: l = l * 31 + Double.doubleToLongBits(m02); 1419: l = l * 31 + Double.doubleToLongBits(m10); 1420: l = l * 31 + Double.doubleToLongBits(m11); 1421: l = l * 31 + Double.doubleToLongBits(m12); 1422: return (int) ((l >> 32) ^ l); 1423: } 1424: 1425: /** 1426: * Compares two transforms for equality. This returns true if they have the 1427: * same matrix values. 1428: * 1429: * @param obj the transform to compare 1430: * @return true if it is equal 1431: */ 1432: public boolean equals(Object obj) 1433: { 1434: if (! (obj instanceof AffineTransform)) 1435: return false; 1436: AffineTransform t = (AffineTransform) obj; 1437: return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02 1438: && m10 == t.m10 && m11 == t.m11 && m12 == t.m12); 1439: } 1440: 1441: /** 1442: * Helper to decode the type from the matrix. This is not guaranteed 1443: * to find the optimal type, but at least it will be valid. 1444: */ 1445: private void updateType() 1446: { 1447: double det = getDeterminant(); 1448: if (det == 0) 1449: { 1450: type = TYPE_GENERAL_TRANSFORM; 1451: return; 1452: } 1453: // Scale (includes rotation by PI) or translation. 1454: if (m01 == 0 && m10 == 0) 1455: { 1456: if (m00 == m11) 1457: type = m00 == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE; 1458: else 1459: type = TYPE_GENERAL_SCALE; 1460: if (m02 != 0 || m12 != 0) 1461: type |= TYPE_TRANSLATION; 1462: } 1463: // Rotation. 1464: else if (m00 == m11 && m01 == -m10) 1465: { 1466: type = m00 == 0 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION; 1467: if (det != 1) 1468: type |= TYPE_UNIFORM_SCALE; 1469: if (m02 != 0 || m12 != 0) 1470: type |= TYPE_TRANSLATION; 1471: } 1472: else 1473: type = TYPE_GENERAL_TRANSFORM; 1474: } 1475: 1476: /** 1477: * Reads a transform from an object stream. 1478: * 1479: * @param s the stream to read from 1480: * @throws ClassNotFoundException if there is a problem deserializing 1481: * @throws IOException if there is a problem deserializing 1482: */ 1483: private void readObject(ObjectInputStream s) 1484: throws ClassNotFoundException, IOException 1485: { 1486: s.defaultReadObject(); 1487: updateType(); 1488: } 1489: } // class AffineTransform