/[classpath]/classpath/java/awt/geom/AffineTransform.java
ViewVC logotype

Diff of /classpath/java/awt/geom/AffineTransform.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.4

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26