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.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