Source for java.awt.geom.AffineTransform

   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