GNU Classpath (0.20) | |
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.setToTranslation(tx, ty); 418: return t; 419: } 420: 421: /** 422: * Returns a rotation transform. A positive angle (in radians) rotates 423: * the positive x-axis to the positive y-axis: 424: * <pre> 425: * [ cos(theta) -sin(theta) 0 ] 426: * [ sin(theta) cos(theta) 0 ] 427: * [ 0 0 1 ] 428: * </pre> 429: * 430: * @param theta the rotation angle 431: * @return the rotating transform 432: */ 433: public static AffineTransform getRotateInstance(double theta) 434: { 435: AffineTransform t = new AffineTransform(); 436: t.setToRotation(theta); 437: return t; 438: } 439: 440: /** 441: * Returns a rotation transform about a point. A positive angle (in radians) 442: * rotates the positive x-axis to the positive y-axis. This is the same 443: * as calling: 444: * <pre> 445: * AffineTransform tx = new AffineTransform(); 446: * tx.setToTranslation(x, y); 447: * tx.rotate(theta); 448: * tx.translate(-x, -y); 449: * </pre> 450: * 451: * <p>The resulting matrix is: 452: * <pre> 453: * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 454: * [ sin(theta) cos(theta) y-x*sin-y*cos ] 455: * [ 0 0 1 ] 456: * </pre> 457: * 458: * @param theta the rotation angle 459: * @param x the x coordinate of the pivot point 460: * @param y the y coordinate of the pivot point 461: * @return the rotating transform 462: */ 463: public static AffineTransform getRotateInstance(double theta, 464: double x, double y) 465: { 466: AffineTransform t = new AffineTransform(); 467: t.setToTranslation(x, y); 468: t.rotate(theta); 469: t.translate(-x, -y); 470: return t; 471: } 472: 473: /** 474: * Returns a scaling transform: 475: * <pre> 476: * [ sx 0 0 ] 477: * [ 0 sy 0 ] 478: * [ 0 0 1 ] 479: * </pre> 480: * 481: * @param sx the x scaling factor 482: * @param sy the y scaling factor 483: * @return the scaling transform 484: */ 485: public static AffineTransform getScaleInstance(double sx, double sy) 486: { 487: AffineTransform t = new AffineTransform(); 488: t.setToScale(sx, sy); 489: return t; 490: } 491: 492: /** 493: * Returns a shearing transform (points are shifted in the x direction based 494: * on a factor of their y coordinate, and in the y direction as a factor of 495: * their x coordinate): 496: * <pre> 497: * [ 1 shx 0 ] 498: * [ shy 1 0 ] 499: * [ 0 0 1 ] 500: * </pre> 501: * 502: * @param shx the x shearing factor 503: * @param shy the y shearing factor 504: * @return the shearing transform 505: */ 506: public static AffineTransform getShearInstance(double shx, double shy) 507: { 508: AffineTransform t = new AffineTransform(); 509: t.setToShear(shx, shy); 510: return t; 511: } 512: 513: /** 514: * Returns the type of this transform. The result is always valid, although 515: * it may not be the simplest interpretation (in other words, there are 516: * sequences of transforms which reduce to something simpler, which this 517: * does not always detect). The result is either TYPE_GENERAL_TRANSFORM, 518: * or a bit-wise combination of TYPE_TRANSLATION, the mutually exclusive 519: * TYPE_*_ROTATIONs, and the mutually exclusive TYPE_*_SCALEs. 520: * 521: * @return The type. 522: * 523: * @see #TYPE_IDENTITY 524: * @see #TYPE_TRANSLATION 525: * @see #TYPE_UNIFORM_SCALE 526: * @see #TYPE_GENERAL_SCALE 527: * @see #TYPE_QUADRANT_ROTATION 528: * @see #TYPE_GENERAL_ROTATION 529: * @see #TYPE_GENERAL_TRANSFORM 530: */ 531: public int getType() 532: { 533: return type; 534: } 535: 536: /** 537: * Return the determinant of this transform matrix. If the determinant is 538: * non-zero, the transform is invertible; otherwise operations which require 539: * an inverse throw a NoninvertibleTransformException. A result very near 540: * zero, due to rounding errors, may indicate that inversion results do not 541: * carry enough precision to be meaningful. 542: * 543: * <p>If this is a uniform scale transformation, the determinant also 544: * represents the squared value of the scale. Otherwise, it carries little 545: * additional meaning. The determinant is calculated as: 546: * <pre> 547: * | m00 m01 m02 | 548: * | m10 m11 m12 | = m00 * m11 - m01 * m10 549: * | 0 0 1 | 550: * </pre> 551: * 552: * @return the determinant 553: * @see #createInverse() 554: */ 555: public double getDeterminant() 556: { 557: return m00 * m11 - m01 * m10; 558: } 559: 560: /** 561: * Return the matrix of values used in this transform. If the matrix has 562: * fewer than 6 entries, only the scale and shear factors are returned; 563: * otherwise the translation factors are copied as well. The resulting 564: * values are: 565: * <pre> 566: * [ d[0] d[2] (d[4]) ] 567: * [ d[1] d[3] (d[5]) ] 568: * [ 0 0 1 ] 569: * </pre> 570: * 571: * @param d the matrix to store the results into; with 4 (6) entries 572: * @throws NullPointerException if d is null 573: * @throws ArrayIndexOutOfBoundsException if d is too small 574: */ 575: public void getMatrix(double[] d) 576: { 577: d[0] = m00; 578: d[1] = m10; 579: d[2] = m01; 580: d[3] = m11; 581: if (d.length >= 6) 582: { 583: d[4] = m02; 584: d[5] = m12; 585: } 586: } 587: 588: /** 589: * Returns the X coordinate scaling factor of the matrix. 590: * 591: * @return m00 592: * @see #getMatrix(double[]) 593: */ 594: public double getScaleX() 595: { 596: return m00; 597: } 598: 599: /** 600: * Returns the Y coordinate scaling factor of the matrix. 601: * 602: * @return m11 603: * @see #getMatrix(double[]) 604: */ 605: public double getScaleY() 606: { 607: return m11; 608: } 609: 610: /** 611: * Returns the X coordinate shearing factor of the matrix. 612: * 613: * @return m01 614: * @see #getMatrix(double[]) 615: */ 616: public double getShearX() 617: { 618: return m01; 619: } 620: 621: /** 622: * Returns the Y coordinate shearing factor of the matrix. 623: * 624: * @return m10 625: * @see #getMatrix(double[]) 626: */ 627: public double getShearY() 628: { 629: return m10; 630: } 631: 632: /** 633: * Returns the X coordinate translation factor of the matrix. 634: * 635: * @return m02 636: * @see #getMatrix(double[]) 637: */ 638: public double getTranslateX() 639: { 640: return m02; 641: } 642: 643: /** 644: * Returns the Y coordinate translation factor of the matrix. 645: * 646: * @return m12 647: * @see #getMatrix(double[]) 648: */ 649: public double getTranslateY() 650: { 651: return m12; 652: } 653: 654: /** 655: * Concatenate a translation onto this transform. This is equivalent, but 656: * more efficient than 657: * <code>concatenate(AffineTransform.getTranslateInstance(tx, ty))</code>. 658: * 659: * @param tx the x translation distance 660: * @param ty the y translation distance 661: * @see #getTranslateInstance(double, double) 662: * @see #concatenate(AffineTransform) 663: */ 664: public void translate(double tx, double ty) 665: { 666: m02 += tx * m00 + ty * m01; 667: m12 += tx * m10 + ty * m11; 668: updateType(); 669: } 670: 671: /** 672: * Concatenate a rotation onto this transform. This is equivalent, but 673: * more efficient than 674: * <code>concatenate(AffineTransform.getRotateInstance(theta))</code>. 675: * 676: * @param theta the rotation angle 677: * @see #getRotateInstance(double) 678: * @see #concatenate(AffineTransform) 679: */ 680: public void rotate(double theta) 681: { 682: double c = Math.cos(theta); 683: double s = Math.sin(theta); 684: double n00 = m00 * c + m01 * s; 685: double n01 = m00 * -s + m01 * c; 686: double n10 = m10 * c + m11 * s; 687: double n11 = m10 * -s + m11 * c; 688: m00 = n00; 689: m01 = n01; 690: m10 = n10; 691: m11 = n11; 692: updateType(); 693: } 694: 695: /** 696: * Concatenate a rotation about a point onto this transform. This is 697: * equivalent, but more efficient than 698: * <code>concatenate(AffineTransform.getRotateInstance(theta, x, y))</code>. 699: * 700: * @param theta the rotation angle 701: * @param x the x coordinate of the pivot point 702: * @param y the y coordinate of the pivot point 703: * @see #getRotateInstance(double, double, double) 704: * @see #concatenate(AffineTransform) 705: */ 706: public void rotate(double theta, double x, double y) 707: { 708: translate(x, y); 709: rotate(theta); 710: translate(-x, -y); 711: } 712: 713: /** 714: * Concatenate a scale onto this transform. This is equivalent, but more 715: * efficient than 716: * <code>concatenate(AffineTransform.getScaleInstance(sx, sy))</code>. 717: * 718: * @param sx the x scaling factor 719: * @param sy the y scaling factor 720: * @see #getScaleInstance(double, double) 721: * @see #concatenate(AffineTransform) 722: */ 723: public void scale(double sx, double sy) 724: { 725: m00 *= sx; 726: m01 *= sy; 727: m10 *= sx; 728: m11 *= sy; 729: updateType(); 730: } 731: 732: /** 733: * Concatenate a shearing onto this transform. This is equivalent, but more 734: * efficient than 735: * <code>concatenate(AffineTransform.getShearInstance(sx, sy))</code>. 736: * 737: * @param shx the x shearing factor 738: * @param shy the y shearing factor 739: * @see #getShearInstance(double, double) 740: * @see #concatenate(AffineTransform) 741: */ 742: public void shear(double shx, double shy) 743: { 744: double n00 = m00 + (shy * m01); 745: double n01 = m01 + (shx * m00); 746: double n10 = m10 + (shy * m11); 747: double n11 = m11 + (shx * m10); 748: m00 = n00; 749: m01 = n01; 750: m10 = n10; 751: m11 = n11; 752: updateType(); 753: } 754: 755: /** 756: * Reset this transform to the identity (no transformation): 757: * <pre> 758: * [ 1 0 0 ] 759: * [ 0 1 0 ] 760: * [ 0 0 1 ] 761: * </pre> 762: */ 763: public void setToIdentity() 764: { 765: m00 = m11 = 1; 766: m01 = m02 = m10 = m12 = 0; 767: type = TYPE_IDENTITY; 768: } 769: 770: /** 771: * Set this transform to a translation: 772: * <pre> 773: * [ 1 0 tx ] 774: * [ 0 1 ty ] 775: * [ 0 0 1 ] 776: * </pre> 777: * 778: * @param tx the x translation distance 779: * @param ty the y translation distance 780: */ 781: public void setToTranslation(double tx, double ty) 782: { 783: m00 = m11 = 1; 784: m01 = m10 = 0; 785: m02 = tx; 786: m12 = ty; 787: type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION; 788: } 789: 790: /** 791: * Set this transform to a rotation. A positive angle (in radians) rotates 792: * the positive x-axis to the positive y-axis: 793: * <pre> 794: * [ cos(theta) -sin(theta) 0 ] 795: * [ sin(theta) cos(theta) 0 ] 796: * [ 0 0 1 ] 797: * </pre> 798: * 799: * @param theta the rotation angle 800: */ 801: public void setToRotation(double theta) 802: { 803: double c = Math.cos(theta); 804: double s = Math.sin(theta); 805: m00 = c; 806: m01 = -s; 807: m02 = 0; 808: m10 = s; 809: m11 = c; 810: m12 = 0; 811: type = (c == 1 ? TYPE_IDENTITY 812: : c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION 813: : TYPE_GENERAL_ROTATION); 814: } 815: 816: /** 817: * Set this transform to a rotation about a point. A positive angle (in 818: * radians) rotates the positive x-axis to the positive y-axis. This is the 819: * same as calling: 820: * <pre> 821: * tx.setToTranslation(x, y); 822: * tx.rotate(theta); 823: * tx.translate(-x, -y); 824: * </pre> 825: * 826: * <p>The resulting matrix is: 827: * <pre> 828: * [ cos(theta) -sin(theta) x-x*cos+y*sin ] 829: * [ sin(theta) cos(theta) y-x*sin-y*cos ] 830: * [ 0 0 1 ] 831: * </pre> 832: * 833: * @param theta the rotation angle 834: * @param x the x coordinate of the pivot point 835: * @param y the y coordinate of the pivot point 836: */ 837: public void setToRotation(double theta, double x, double y) 838: { 839: double c = Math.cos(theta); 840: double s = Math.sin(theta); 841: m00 = c; 842: m01 = -s; 843: m02 = x - x * c + y * s; 844: m10 = s; 845: m11 = c; 846: m12 = y - x * s - y * c; 847: updateType(); 848: } 849: 850: /** 851: * Set this transform to a scale: 852: * <pre> 853: * [ sx 0 0 ] 854: * [ 0 sy 0 ] 855: * [ 0 0 1 ] 856: * </pre> 857: * 858: * @param sx the x scaling factor 859: * @param sy the y scaling factor 860: */ 861: public void setToScale(double sx, double sy) 862: { 863: m00 = sx; 864: m01 = m02 = m10 = m12 = 0; 865: m11 = sy; 866: type = (sx != sy ? TYPE_GENERAL_SCALE 867: : sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE); 868: } 869: 870: /** 871: * Set this transform to a shear (points are shifted in the x direction based 872: * on a factor of their y coordinate, and in the y direction as a factor of 873: * their x coordinate): 874: * <pre> 875: * [ 1 shx 0 ] 876: * [ shy 1 0 ] 877: * [ 0 0 1 ] 878: * </pre> 879: * 880: * @param shx the x shearing factor 881: * @param shy the y shearing factor 882: */ 883: public void setToShear(double shx, double shy) 884: { 885: m00 = m11 = 1; 886: m01 = shx; 887: m10 = shy; 888: m02 = m12 = 0; 889: updateType(); 890: } 891: 892: /** 893: * Set this transform to a copy of the given one. 894: * 895: * @param tx the transform to copy 896: * @throws NullPointerException if tx is null 897: */ 898: public void setTransform(AffineTransform tx) 899: { 900: m00 = tx.m00; 901: m01 = tx.m01; 902: m02 = tx.m02; 903: m10 = tx.m10; 904: m11 = tx.m11; 905: m12 = tx.m12; 906: type = tx.type; 907: } 908: 909: /** 910: * Set this transform to the given values: 911: * <pre> 912: * [ m00 m01 m02 ] 913: * [ m10 m11 m12 ] 914: * [ 0 0 1 ] 915: * </pre> 916: * 917: * @param m00 the x scaling component 918: * @param m10 the y shearing component 919: * @param m01 the x shearing component 920: * @param m11 the y scaling component 921: * @param m02 the x translation component 922: * @param m12 the y translation component 923: */ 924: public void setTransform(double m00, double m10, double m01, 925: double m11, double m02, double m12) 926: { 927: this.m00 = m00; 928: this.m10 = m10; 929: this.m01 = m01; 930: this.m11 = m11; 931: this.m02 = m02; 932: this.m12 = m12; 933: updateType(); 934: } 935: 936: /** 937: * Set this transform to the result of performing the original version of 938: * this followed by tx. This is commonly used when chaining transformations 939: * from one space to another. In matrix form: 940: * <pre> 941: * [ this ] = [ this ] x [ tx ] 942: * </pre> 943: * 944: * @param tx the transform to concatenate 945: * @throws NullPointerException if tx is null 946: * @see #preConcatenate(AffineTransform) 947: */ 948: public void concatenate(AffineTransform tx) 949: { 950: double n00 = m00 * tx.m00 + m01 * tx.m10; 951: double n01 = m00 * tx.m01 + m01 * tx.m11; 952: double n02 = m00 * tx.m02 + m01 * tx.m12 + m02; 953: double n10 = m10 * tx.m00 + m11 * tx.m10; 954: double n11 = m10 * tx.m01 + m11 * tx.m11; 955: double n12 = m10 * tx.m02 + m11 * tx.m12 + m12; 956: m00 = n00; 957: m01 = n01; 958: m02 = n02; 959: m10 = n10; 960: m11 = n11; 961: m12 = n12; 962: updateType(); 963: } 964: 965: /** 966: * Set this transform to the result of performing tx followed by the 967: * original version of this. This is less common than normal concatenation, 968: * but can still be used to chain transformations from one space to another. 969: * In matrix form: 970: * <pre> 971: * [ this ] = [ tx ] x [ this ] 972: * </pre> 973: * 974: * @param tx the transform to concatenate 975: * @throws NullPointerException if tx is null 976: * @see #concatenate(AffineTransform) 977: */ 978: public void preConcatenate(AffineTransform tx) 979: { 980: double n00 = tx.m00 * m00 + tx.m01 * m10; 981: double n01 = tx.m00 * m01 + tx.m01 * m11; 982: double n02 = tx.m00 * m02 + tx.m01 * m12 + tx.m02; 983: double n10 = tx.m10 * m00 + tx.m11 * m10; 984: double n11 = tx.m10 * m01 + tx.m11 * m11; 985: double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12; 986: m00 = n00; 987: m01 = n01; 988: m02 = n02; 989: m10 = n10; 990: m11 = n11; 991: m12 = n12; 992: updateType(); 993: } 994: 995: /** 996: * Returns a transform, which if concatenated to this one, will result in 997: * the identity transform. This is useful for undoing transformations, but 998: * is only possible if the original transform has an inverse (ie. does not 999: * map multiple points to the same line or point). A transform exists only 1000: * if getDeterminant() has a non-zero value. 1001: * 1002: * The inverse is calculated as: 1003: * 1004: * <pre> 1005: * 1006: * Let A be the matrix for which we want to find the inverse: 1007: * 1008: * A = [ m00 m01 m02 ] 1009: * [ m10 m11 m12 ] 1010: * [ 0 0 1 ] 1011: * 1012: * 1013: * 1 1014: * inverse (A) = --- x adjoint(A) 1015: * det 1016: * 1017: * 1018: * 1019: * = 1 [ m11 -m01 m01*m12-m02*m11 ] 1020: * --- x [ -m10 m00 -m00*m12+m10*m02 ] 1021: * det [ 0 0 m00*m11-m10*m01 ] 1022: * 1023: * 1024: * 1025: * = [ m11/det -m01/det m01*m12-m02*m11/det ] 1026: * [ -m10/det m00/det -m00*m12+m10*m02/det ] 1027: * [ 0 0 1 ] 1028: * 1029: * 1030: * </pre> 1031: * 1032: * 1033: * 1034: * @return a new inverse transform 1035: * @throws NoninvertibleTransformException if inversion is not possible 1036: * @see #getDeterminant() 1037: */ 1038: public AffineTransform createInverse() 1039: throws NoninvertibleTransformException 1040: { 1041: double det = getDeterminant(); 1042: if (det == 0) 1043: throw new NoninvertibleTransformException("can't invert transform"); 1044: 1045: double im00 = m11 / det; 1046: double im10 = -m10 / det; 1047: double im01 = -m01 / det; 1048: double im11 = m00 / det; 1049: double im02 = (m01 * m12 - m02 * m11) / det; 1050: double im12 = (-m00 * m12 + m10 * m02) / det; 1051: 1052: return new AffineTransform (im00, im10, im01, im11, im02, im12); 1053: } 1054: 1055: /** 1056: * Perform this transformation on the given source point, and store the 1057: * result in the destination (creating it if necessary). It is safe for 1058: * src and dst to be the same. 1059: * 1060: * @param src the source point 1061: * @param dst the destination, or null 1062: * @return the transformation of src, in dst if it was non-null 1063: * @throws NullPointerException if src is null 1064: */ 1065: public Point2D transform(Point2D src, Point2D dst) 1066: { 1067: if (dst == null) 1068: dst = new Point2D.Double(); 1069: double x = src.getX(); 1070: double y = src.getY(); 1071: double nx = m00 * x + m01 * y + m02; 1072: double ny = m10 * x + m11 * y + m12; 1073: dst.setLocation(nx, ny); 1074: return dst; 1075: } 1076: 1077: /** 1078: * Perform this transformation on an array of points, storing the results 1079: * in another (possibly same) array. This will not create a destination 1080: * array, but will create points for the null entries of the destination. 1081: * The transformation is done sequentially. While having a single source 1082: * and destination point be the same is safe, you should be aware that 1083: * duplicate references to the same point in the source, and having the 1084: * source overlap the destination, may result in your source points changing 1085: * from a previous transform before it is their turn to be evaluated. 1086: * 1087: * @param src the array of source points 1088: * @param srcOff the starting offset into src 1089: * @param dst the array of destination points (may have null entries) 1090: * @param dstOff the starting offset into dst 1091: * @param num the number of points to transform 1092: * @throws NullPointerException if src or dst is null, or src has null 1093: * entries 1094: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1095: * @throws ArrayStoreException if new points are incompatible with dst 1096: */ 1097: public void transform(Point2D[] src, int srcOff, 1098: Point2D[] dst, int dstOff, int num) 1099: { 1100: while (--num >= 0) 1101: dst[dstOff] = transform(src[srcOff++], dst[dstOff++]); 1102: } 1103: 1104: /** 1105: * Perform this transformation on an array of points, in (x,y) pairs, 1106: * storing the results in another (possibly same) array. This will not 1107: * create a destination array. All sources are copied before the 1108: * transformation, so that no result will overwrite a point that has not yet 1109: * been evaluated. 1110: * 1111: * @param srcPts the array of source points 1112: * @param srcOff the starting offset into src 1113: * @param dstPts the array of destination points 1114: * @param dstOff the starting offset into dst 1115: * @param num the number of points to transform 1116: * @throws NullPointerException if src or dst is null 1117: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1118: */ 1119: public void transform(float[] srcPts, int srcOff, 1120: float[] dstPts, int dstOff, int num) 1121: { 1122: if (srcPts == dstPts && dstOff > srcOff 1123: && num > 1 && srcOff + 2 * num > dstOff) 1124: { 1125: float[] f = new float[2 * num]; 1126: System.arraycopy(srcPts, srcOff, f, 0, 2 * num); 1127: srcPts = f; 1128: } 1129: while (--num >= 0) 1130: { 1131: float x = srcPts[srcOff++]; 1132: float y = srcPts[srcOff++]; 1133: dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); 1134: dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); 1135: } 1136: } 1137: 1138: /** 1139: * Perform this transformation on an array of points, in (x,y) pairs, 1140: * storing the results in another (possibly same) array. This will not 1141: * create a destination array. All sources are copied before the 1142: * transformation, so that no result will overwrite a point that has not yet 1143: * been evaluated. 1144: * 1145: * @param srcPts the array of source points 1146: * @param srcOff the starting offset into src 1147: * @param dstPts the array of destination points 1148: * @param dstOff the starting offset into dst 1149: * @param num the number of points to transform 1150: * @throws NullPointerException if src or dst is null 1151: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1152: */ 1153: public void transform(double[] srcPts, int srcOff, 1154: double[] dstPts, int dstOff, int num) 1155: { 1156: if (srcPts == dstPts && dstOff > srcOff 1157: && num > 1 && srcOff + 2 * num > dstOff) 1158: { 1159: double[] d = new double[2 * num]; 1160: System.arraycopy(srcPts, srcOff, d, 0, 2 * num); 1161: srcPts = d; 1162: } 1163: while (--num >= 0) 1164: { 1165: double x = srcPts[srcOff++]; 1166: double y = srcPts[srcOff++]; 1167: dstPts[dstOff++] = m00 * x + m01 * y + m02; 1168: dstPts[dstOff++] = m10 * x + m11 * y + m12; 1169: } 1170: } 1171: 1172: /** 1173: * Perform this transformation on an array of points, in (x,y) pairs, 1174: * storing the results in another array. This will not create a destination 1175: * array. 1176: * 1177: * @param srcPts the array of source points 1178: * @param srcOff the starting offset into src 1179: * @param dstPts the array of destination points 1180: * @param dstOff the starting offset into dst 1181: * @param num the number of points to transform 1182: * @throws NullPointerException if src or dst is null 1183: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1184: */ 1185: public void transform(float[] srcPts, int srcOff, 1186: double[] dstPts, int dstOff, int num) 1187: { 1188: while (--num >= 0) 1189: { 1190: float x = srcPts[srcOff++]; 1191: float y = srcPts[srcOff++]; 1192: dstPts[dstOff++] = m00 * x + m01 * y + m02; 1193: dstPts[dstOff++] = m10 * x + m11 * y + m12; 1194: } 1195: } 1196: 1197: /** 1198: * Perform this transformation on an array of points, in (x,y) pairs, 1199: * storing the results in another array. This will not create a destination 1200: * array. 1201: * 1202: * @param srcPts the array of source points 1203: * @param srcOff the starting offset into src 1204: * @param dstPts the array of destination points 1205: * @param dstOff the starting offset into dst 1206: * @param num the number of points to transform 1207: * @throws NullPointerException if src or dst is null 1208: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1209: */ 1210: public void transform(double[] srcPts, int srcOff, 1211: float[] dstPts, int dstOff, int num) 1212: { 1213: while (--num >= 0) 1214: { 1215: double x = srcPts[srcOff++]; 1216: double y = srcPts[srcOff++]; 1217: dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02); 1218: dstPts[dstOff++] = (float) (m10 * x + m11 * y + m12); 1219: } 1220: } 1221: 1222: /** 1223: * Perform the inverse of this transformation on the given source point, 1224: * and store the result in the destination (creating it if necessary). It 1225: * is safe for src and dst to be the same. 1226: * 1227: * @param src the source point 1228: * @param dst the destination, or null 1229: * @return the inverse transformation of src, in dst if it was non-null 1230: * @throws NullPointerException if src is null 1231: * @throws NoninvertibleTransformException if the inverse does not exist 1232: * @see #getDeterminant() 1233: */ 1234: public Point2D inverseTransform(Point2D src, Point2D dst) 1235: throws NoninvertibleTransformException 1236: { 1237: return createInverse().transform(src, dst); 1238: } 1239: 1240: /** 1241: * Perform the inverse of this transformation on an array of points, in 1242: * (x,y) pairs, storing the results in another (possibly same) array. This 1243: * will not create a destination array. All sources are copied before the 1244: * transformation, so that no result will overwrite a point that has not yet 1245: * been evaluated. 1246: * 1247: * @param srcPts the array of source points 1248: * @param srcOff the starting offset into src 1249: * @param dstPts the array of destination points 1250: * @param dstOff the starting offset into dst 1251: * @param num the number of points to transform 1252: * @throws NullPointerException if src or dst is null 1253: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1254: * @throws NoninvertibleTransformException if the inverse does not exist 1255: * @see #getDeterminant() 1256: */ 1257: public void inverseTransform(double[] srcPts, int srcOff, 1258: double[] dstPts, int dstOff, int num) 1259: throws NoninvertibleTransformException 1260: { 1261: createInverse().transform(srcPts, srcOff, dstPts, dstOff, num); 1262: } 1263: 1264: /** 1265: * Perform this transformation, less any translation, on the given source 1266: * point, and store the result in the destination (creating it if 1267: * necessary). It is safe for src and dst to be the same. The reduced 1268: * transform is equivalent to: 1269: * <pre> 1270: * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] 1271: * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] 1272: * </pre> 1273: * 1274: * @param src the source point 1275: * @param dst the destination, or null 1276: * @return the delta transformation of src, in dst if it was non-null 1277: * @throws NullPointerException if src is null 1278: */ 1279: public Point2D deltaTransform(Point2D src, Point2D dst) 1280: { 1281: if (dst == null) 1282: dst = new Point2D.Double(); 1283: double x = src.getX(); 1284: double y = src.getY(); 1285: double nx = m00 * x + m01 * y; 1286: double ny = m10 * x + m11 * y; 1287: dst.setLocation(nx, ny); 1288: return dst; 1289: } 1290: 1291: /** 1292: * Perform this transformation, less any translation, on an array of points, 1293: * in (x,y) pairs, storing the results in another (possibly same) array. 1294: * This will not create a destination array. All sources are copied before 1295: * the transformation, so that no result will overwrite a point that has 1296: * not yet been evaluated. The reduced transform is equivalent to: 1297: * <pre> 1298: * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ] 1299: * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ] 1300: * </pre> 1301: * 1302: * @param srcPts the array of source points 1303: * @param srcOff the starting offset into src 1304: * @param dstPts the array of destination points 1305: * @param dstOff the starting offset into dst 1306: * @param num the number of points to transform 1307: * @throws NullPointerException if src or dst is null 1308: * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded 1309: */ 1310: public void deltaTransform(double[] srcPts, int srcOff, 1311: double[] dstPts, int dstOff, 1312: int num) 1313: { 1314: if (srcPts == dstPts && dstOff > srcOff 1315: && num > 1 && srcOff + 2 * num > dstOff) 1316: { 1317: double[] d = new double[2 * num]; 1318: System.arraycopy(srcPts, srcOff, d, 0, 2 * num); 1319: srcPts = d; 1320: } 1321: while (--num >= 0) 1322: { 1323: double x = srcPts[srcOff++]; 1324: double y = srcPts[srcOff++]; 1325: dstPts[dstOff++] = m00 * x + m01 * y; 1326: dstPts[dstOff++] = m10 * x + m11 * y; 1327: } 1328: } 1329: 1330: /** 1331: * Return a new Shape, based on the given one, where the path of the shape 1332: * has been transformed by this transform. Notice that this uses GeneralPath, 1333: * which only stores points in float precision. 1334: * 1335: * @param src the shape source to transform 1336: * @return the shape, transformed by this, <code>null</code> if src is 1337: * <code>null</code>. 1338: * @see GeneralPath#transform(AffineTransform) 1339: */ 1340: public Shape createTransformedShape(Shape src) 1341: { 1342: if(src == null) 1343: return null; 1344: GeneralPath p = new GeneralPath(src); 1345: p.transform(this); 1346: return p; 1347: } 1348: 1349: /** 1350: * Returns a string representation of the transform, in the format: 1351: * <code>"AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" 1352: * + m10 + ", " + m11 + ", " + m12 + "]]"</code>. 1353: * 1354: * @return the string representation 1355: */ 1356: public String toString() 1357: { 1358: return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], [" 1359: + m10 + ", " + m11 + ", " + m12 + "]]"; 1360: } 1361: 1362: /** 1363: * Tests if this transformation is the identity: 1364: * <pre> 1365: * [ 1 0 0 ] 1366: * [ 0 1 0 ] 1367: * [ 0 0 1 ] 1368: * </pre> 1369: * 1370: * @return true if this is the identity transform 1371: */ 1372: public boolean isIdentity() 1373: { 1374: // Rather than rely on type, check explicitly. 1375: return (m00 == 1 && m01 == 0 && m02 == 0 1376: && m10 == 0 && m11 == 1 && m12 == 0); 1377: } 1378: 1379: /** 1380: * Create a new transform of the same run-time type, with the same 1381: * transforming properties as this one. 1382: * 1383: * @return the clone 1384: */ 1385: public Object clone() 1386: { 1387: try 1388: { 1389: return super.clone(); 1390: } 1391: catch (CloneNotSupportedException e) 1392: { 1393: throw (Error) new InternalError().initCause(e); // Impossible 1394: } 1395: } 1396: 1397: /** 1398: * Return the hashcode for this transformation. The formula is not 1399: * documented, but appears to be the same as: 1400: * <pre> 1401: * long l = Double.doubleToLongBits(getScaleX()); 1402: * l = l * 31 + Double.doubleToLongBits(getShearY()); 1403: * l = l * 31 + Double.doubleToLongBits(getShearX()); 1404: * l = l * 31 + Double.doubleToLongBits(getScaleY()); 1405: * l = l * 31 + Double.doubleToLongBits(getTranslateX()); 1406: * l = l * 31 + Double.doubleToLongBits(getTranslateY()); 1407: * return (int) ((l >> 32) ^ l); 1408: * </pre> 1409: * 1410: * @return the hashcode 1411: */ 1412: public int hashCode() 1413: { 1414: long l = Double.doubleToLongBits(m00); 1415: l = l * 31 + Double.doubleToLongBits(m10); 1416: l = l * 31 + Double.doubleToLongBits(m01); 1417: l = l * 31 + Double.doubleToLongBits(m11); 1418: l = l * 31 + Double.doubleToLongBits(m02); 1419: l = l * 31 + Double.doubleToLongBits(m12); 1420: return (int) ((l >> 32) ^ l); 1421: } 1422: 1423: /** 1424: * Compares two transforms for equality. This returns true if they have the 1425: * same matrix values. 1426: * 1427: * @param obj the transform to compare 1428: * @return true if it is equal 1429: */ 1430: public boolean equals(Object obj) 1431: { 1432: if (! (obj instanceof AffineTransform)) 1433: return false; 1434: AffineTransform t = (AffineTransform) obj; 1435: return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02 1436: && m10 == t.m10 && m11 == t.m11 && m12 == t.m12); 1437: } 1438: 1439: /** 1440: * Helper to decode the type from the matrix. This is not guaranteed 1441: * to find the optimal type, but at least it will be valid. 1442: */ 1443: private void updateType() 1444: { 1445: double det = getDeterminant(); 1446: if (det == 0) 1447: { 1448: type = TYPE_GENERAL_TRANSFORM; 1449: return; 1450: } 1451: // Scale (includes rotation by PI) or translation. 1452: if (m01 == 0 && m10 == 0) 1453: { 1454: if (m00 == m11) 1455: type = m00 == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE; 1456: else 1457: type = TYPE_GENERAL_SCALE; 1458: if (m02 != 0 || m12 != 0) 1459: type |= TYPE_TRANSLATION; 1460: } 1461: // Rotation. 1462: else if (m00 == m11 && m01 == -m10) 1463: { 1464: type = m00 == 0 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION; 1465: if (det != 1) 1466: type |= TYPE_UNIFORM_SCALE; 1467: if (m02 != 0 || m12 != 0) 1468: type |= TYPE_TRANSLATION; 1469: } 1470: else 1471: type = TYPE_GENERAL_TRANSFORM; 1472: } 1473: 1474: /** 1475: * Reads a transform from an object stream. 1476: * 1477: * @param s the stream to read from 1478: * @throws ClassNotFoundException if there is a problem deserializing 1479: * @throws IOException if there is a problem deserializing 1480: */ 1481: private void readObject(ObjectInputStream s) 1482: throws ClassNotFoundException, IOException 1483: { 1484: s.defaultReadObject(); 1485: updateType(); 1486: } 1487: } // class AffineTransform
GNU Classpath (0.20) |