summaryrefslogtreecommitdiffstats
path: root/libjava/java/awt/geom/AffineTransform.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/java/awt/geom/AffineTransform.java')
-rw-r--r--libjava/java/awt/geom/AffineTransform.java1461
1 files changed, 1118 insertions, 343 deletions
diff --git a/libjava/java/awt/geom/AffineTransform.java b/libjava/java/awt/geom/AffineTransform.java
index 436a842c205..3c9486e6875 100644
--- a/libjava/java/awt/geom/AffineTransform.java
+++ b/libjava/java/awt/geom/AffineTransform.java
@@ -1,4 +1,5 @@
-/* Copyright (C) 2000, 2001, 2002 Free Software Foundation
+/* AffineTransform.java -- transform coordinates between two 2-D spaces
+ Copyright (C) 2000, 2001, 2002 Free Software Foundation
This file is part of GNU Classpath.
@@ -34,44 +35,276 @@ this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
+
package java.awt.geom;
-import java.awt.*;
+
+import java.awt.Shape;
+import java.io.IOException;
+import java.io.ObjectInputStream;
import java.io.Serializable;
/**
+ * This class represents an affine transformation between two coordinate
+ * spaces in 2 dimensions. Such a transform preserves the "straightness"
+ * and "parallelness" of lines. The transform is built from a sequence of
+ * translations, scales, flips, rotations, and shears.
+ *
+ * <p>The transformation can be represented using matrix math on a 3x3 array.
+ * Given (x,y), the transformation (x',y') can be found by:
+ * <pre>
+ * [ x'] [ m00 m01 m02 ] [ x ] [ m00*x + m01*y + m02 ]
+ * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10*x + m11*y + m12 ]
+ * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
+ * </pre>
+ * The bottom row of the matrix is constant, so a transform can be uniquely
+ * represented (as in toString) by "[[m00, m01, m02], [m10, m11, m12]]".
+ *
* @author Tom Tromey <tromey@cygnus.com>
- * @date April 16, 2000
+ * @author Eric Blake <ebb9@email.byu.edu>
+ * @since 1.2
+ * @status partially updated to 1.4, still has some problems
*/
-
-/* Status: mostly complete. Search for fixme to see problems.
- Also, TYPE_ returns are not handled correctly. */
-
public class AffineTransform implements Cloneable, Serializable
{
+ /**
+ * Compatible with JDK 1.2+.
+ */
+ private static final long serialVersionUID = 1330973210523860834L;
+
+ /**
+ * The transformation is the identity (x' = x, y' = y). All other transforms
+ * have either a combination of the appropriate transform flag bits for
+ * their type, or the type GENERAL_TRANSFORM.
+ *
+ * @see #TYPE_TRANSLATION
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_GENERAL_SCALE
+ * @see #TYPE_FLIP
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_ROTATION
+ * @see #TYPE_GENERAL_TRANSFORM
+ * @see #getType()
+ */
public static final int TYPE_IDENTITY = 0;
- public static final int TYPE_FLIP = 64;
- public static final int TYPE_GENERAL_ROTATION = 16;
+
+ /**
+ * The transformation includes a translation - shifting in the x or y
+ * direction without changing length or angles.
+ *
+ * @see #TYPE_IDENTITY
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_GENERAL_SCALE
+ * @see #TYPE_FLIP
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_ROTATION
+ * @see #TYPE_GENERAL_TRANSFORM
+ * @see #getType()
+ */
+ public static final int TYPE_TRANSLATION = 1;
+
+ /**
+ * The transformation includes a uniform scale - length is scaled in both
+ * the x and y directions by the same amount, without affecting angles.
+ * This is mutually exclusive with TYPE_GENERAL_SCALE.
+ *
+ * @see #TYPE_IDENTITY
+ * @see #TYPE_TRANSLATION
+ * @see #TYPE_GENERAL_SCALE
+ * @see #TYPE_FLIP
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_ROTATION
+ * @see #TYPE_GENERAL_TRANSFORM
+ * @see #TYPE_MASK_SCALE
+ * @see #getType()
+ */
+ public static final int TYPE_UNIFORM_SCALE = 2;
+
+ /**
+ * The transformation includes a general scale - length is scaled in either
+ * or both the x and y directions, but by different amounts; without
+ * affecting angles. This is mutually exclusive with TYPE_UNIFORM_SCALE.
+ *
+ * @see #TYPE_IDENTITY
+ * @see #TYPE_TRANSLATION
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_FLIP
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_ROTATION
+ * @see #TYPE_GENERAL_TRANSFORM
+ * @see #TYPE_MASK_SCALE
+ * @see #getType()
+ */
public static final int TYPE_GENERAL_SCALE = 4;
- public static final int TYPE_GENERAL_TRANSFORM = 32;
- public static final int TYPE_MASK_ROTATION = 24;
+
+ /**
+ * This constant checks if either variety of scale transform is performed.
+ *
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_GENERAL_SCALE
+ */
public static final int TYPE_MASK_SCALE = 6;
+
+ /**
+ * The transformation includes a flip about an axis, swapping between
+ * right-handed and left-handed coordinate systems. In a right-handed
+ * system, the positive x-axis rotates counter-clockwise to the positive
+ * y-axis; in a left-handed system it rotates clockwise.
+ *
+ * @see #TYPE_IDENTITY
+ * @see #TYPE_TRANSLATION
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_GENERAL_SCALE
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_ROTATION
+ * @see #TYPE_GENERAL_TRANSFORM
+ * @see #getType()
+ */
+ public static final int TYPE_FLIP = 64;
+
+ /**
+ * The transformation includes a rotation of a multiple of 90 degrees (PI/2
+ * radians). Angles are rotated, but length is preserved. This is mutually
+ * exclusive with TYPE_GENERAL_ROTATION.
+ *
+ * @see #TYPE_IDENTITY
+ * @see #TYPE_TRANSLATION
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_GENERAL_SCALE
+ * @see #TYPE_FLIP
+ * @see #TYPE_GENERAL_ROTATION
+ * @see #TYPE_GENERAL_TRANSFORM
+ * @see #TYPE_MASK_ROTATION
+ * @see #getType()
+ */
public static final int TYPE_QUADRANT_ROTATION = 8;
- public static final int TYPE_TRANSLATION = 1;
- public static final int TYPE_UNIFORM_SCALE = 2;
- public AffineTransform ()
+ /**
+ * The transformation includes a rotation by an arbitrary angle. Angles are
+ * rotated, but length is preserved. This is mutually exclusive with
+ * TYPE_QUADRANT_ROTATION.
+ *
+ * @see #TYPE_IDENTITY
+ * @see #TYPE_TRANSLATION
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_GENERAL_SCALE
+ * @see #TYPE_FLIP
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_TRANSFORM
+ * @see #TYPE_MASK_ROTATION
+ * @see #getType()
+ */
+ public static final int TYPE_GENERAL_ROTATION = 16;
+
+ /**
+ * This constant checks if either variety of rotation is performed.
+ *
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_ROTATION
+ */
+ public static final int TYPE_MASK_ROTATION = 24;
+
+ /**
+ * The transformation is an arbitrary conversion of coordinates which
+ * could not be decomposed into the other TYPEs.
+ *
+ * @see #TYPE_IDENTITY
+ * @see #TYPE_TRANSLATION
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_GENERAL_SCALE
+ * @see #TYPE_FLIP
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_ROTATION
+ * @see #getType()
+ */
+ public static final int TYPE_GENERAL_TRANSFORM = 32;
+
+ /**
+ * The X coordinate scaling element of the transform matrix.
+ *
+ * @serial matrix[0,0]
+ */
+ private double m00;
+
+ /**
+ * The Y coordinate scaling element of the transform matrix.
+ *
+ * @serial matrix[1,0]
+ */
+ private double m10;
+
+ /**
+ * The X coordinate shearing element of the transform matrix.
+ *
+ * @serial matrix[0,1]
+ */
+ private double m01;
+
+ /**
+ * The Y coordinate shearing element of the transform matrix.
+ *
+ * @serial matrix[1,1]
+ */
+ private double m11;
+
+ /**
+ * The X coordinate translation element of the transform matrix.
+ *
+ * @serial matrix[0,2]
+ */
+ private double m02;
+
+ /**
+ * The Y coordinate translation element of the transform matrix.
+ *
+ * @serial matrix[1,2]
+ */
+ private double m12;
+
+ /** The type of this transform. */
+ private transient int type;
+
+ /**
+ * Construct a new identity transform:
+ * <pre>
+ * [ 1 0 0 ]
+ * [ 0 1 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ */
+ public AffineTransform()
{
- setToIdentity ();
+ m00 = m11 = 1;
}
- public AffineTransform (AffineTransform tx)
+ /**
+ * Create a new transform which copies the given one.
+ *
+ * @param tx the transform to copy
+ * @throws NullPointerException if tx is null
+ */
+ public AffineTransform(AffineTransform tx)
{
- setTransform (tx);
+ setTransform(tx);
}
- public AffineTransform (float m00, float m10,
- float m01, float m11,
- float m02, float m12)
+ /**
+ * Construct a transform with the given matrix entries:
+ * <pre>
+ * [ m00 m01 m02 ]
+ * [ m10 m11 m12 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param m00 the x scaling component
+ * @param m10 the y shearing component
+ * @param m01 the x shearing component
+ * @param m11 the y scaling component
+ * @param m02 the x translation component
+ * @param m12 the y translation component
+ */
+ public AffineTransform(float m00, float m10,
+ float m01, float m11,
+ float m02, float m12)
{
this.m00 = m00;
this.m10 = m10;
@@ -79,24 +312,54 @@ public class AffineTransform implements Cloneable, Serializable
this.m11 = m11;
this.m02 = m02;
this.m12 = m12;
- this.type = TYPE_GENERAL_TRANSFORM;
+ updateType();
}
- public AffineTransform (float[] flatmatrix)
+ /**
+ * Construct a transform from a sequence of float entries. The array must
+ * have at least 4 entries, which has a translation factor of 0; or 6
+ * entries, for specifying all parameters:
+ * <pre>
+ * [ f[0] f[2] (f[4]) ]
+ * [ f[1] f[3] (f[5]) ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param f the matrix to copy from, with at least 4 (6) entries
+ * @throws NullPointerException if f is null
+ * @throws ArrayIndexOutOfBoundsException if f is too small
+ */
+ public AffineTransform(float[] f)
{
- m00 = flatmatrix[0];
- m10 = flatmatrix[1];
- m01 = flatmatrix[2];
- m11 = flatmatrix[3];
- if (flatmatrix.length >= 6)
+ m00 = f[0];
+ m10 = f[1];
+ m01 = f[2];
+ m11 = f[3];
+ if (f.length >= 6)
{
- m02 = flatmatrix[4];
- m12 = flatmatrix[5];
+ m02 = f[4];
+ m12 = f[5];
}
+ updateType();
}
- public AffineTransform (double m00, double m10, double m01,
- double m11, double m02, double m12)
+ /**
+ * Construct a transform with the given matrix entries:
+ * <pre>
+ * [ m00 m01 m02 ]
+ * [ m10 m11 m12 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param m00 the x scaling component
+ * @param m10 the y shearing component
+ * @param m01 the x shearing component
+ * @param m11 the y scaling component
+ * @param m02 the x translation component
+ * @param m12 the y translation component
+ */
+ public AffineTransform(double m00, double m10, double m01,
+ double m11, double m02, double m12)
{
this.m00 = m00;
this.m10 = m10;
@@ -104,222 +367,532 @@ public class AffineTransform implements Cloneable, Serializable
this.m11 = m11;
this.m02 = m02;
this.m12 = m12;
- this.type = TYPE_GENERAL_TRANSFORM;
+ updateType();
}
- public AffineTransform (double[] flatmatrix)
+ /**
+ * Construct a transform from a sequence of double entries. The array must
+ * have at least 4 entries, which has a translation factor of 0; or 6
+ * entries, for specifying all parameters:
+ * <pre>
+ * [ d[0] d[2] (d[4]) ]
+ * [ d[1] d[3] (d[5]) ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param d the matrix to copy from, with at least 4 (6) entries
+ * @throws NullPointerException if d is null
+ * @throws ArrayIndexOutOfBoundsException if d is too small
+ */
+ public AffineTransform(double[] d)
{
- m00 = flatmatrix[0];
- m10 = flatmatrix[1];
- m01 = flatmatrix[2];
- m11 = flatmatrix[3];
- if (flatmatrix.length >= 6)
+ m00 = d[0];
+ m10 = d[1];
+ m01 = d[2];
+ m11 = d[3];
+ if (d.length >= 6)
{
- m02 = flatmatrix[4];
- m12 = flatmatrix[5];
+ m02 = d[4];
+ m12 = d[5];
}
+ updateType();
}
- public static AffineTransform getTranslateInstance (double tx, double ty)
+ /**
+ * Returns a translation transform:
+ * <pre>
+ * [ 1 0 tx ]
+ * [ 0 1 ty ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param tx the x translation distance
+ * @param ty the y translation distance
+ * @return the translating transform
+ */
+ public static AffineTransform getTranslateInstance(double tx, double ty)
{
- AffineTransform t = new AffineTransform ();
- t.setToTranslation (tx, ty);
+ AffineTransform t = new AffineTransform();
+ t.setToTranslation(tx, ty);
return t;
}
- public static AffineTransform getRotateInstance (double theta)
+ /**
+ * Returns a rotation transform. A positive angle (in radians) rotates
+ * the positive x-axis to the positive y-axis:
+ * <pre>
+ * [ cos(theta) -sin(theta) 0 ]
+ * [ sin(theta) cos(theta) 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param theta the rotation angle
+ * @return the rotating transform
+ */
+ public static AffineTransform getRotateInstance(double theta)
{
- AffineTransform t = new AffineTransform ();
- t.setToRotation (theta);
+ AffineTransform t = new AffineTransform();
+ t.setToRotation(theta);
return t;
}
- public static AffineTransform getRotateInstance (double theta,
- double x, double y)
+ /**
+ * Returns a rotation transform about a point. A positive angle (in radians)
+ * rotates the positive x-axis to the positive y-axis. This is the same
+ * as calling:
+ * <pre>
+ * AffineTransform tx = new AffineTransform();
+ * tx.setToTranslation(x, y);
+ * tx.rotate(theta);
+ * tx.translate(-x, -y);
+ * </pre>
+ *
+ * <p>The resulting matrix is:
+ * <pre>
+ * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
+ * [ sin(theta) cos(theta) y-x*sin-y*cos ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param theta the rotation angle
+ * @param x the x coordinate of the pivot point
+ * @param y the y coordinate of the pivot point
+ * @return the rotating transform
+ */
+ public static AffineTransform getRotateInstance(double theta,
+ double x, double y)
{
- AffineTransform t = new AffineTransform ();
- t.rotate (theta, x, y);
+ AffineTransform t = new AffineTransform();
+ t.setToTranslation(x, y);
+ t.rotate(theta);
+ t.translate(-x, -y);
return t;
}
- public static AffineTransform getScaleInstance (double sx, double sy)
+ /**
+ * Returns a scaling transform:
+ * <pre>
+ * [ sx 0 0 ]
+ * [ 0 sy 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param sx the x scaling factor
+ * @param sy the y scaling factor
+ * @return the scaling transform
+ */
+ public static AffineTransform getScaleInstance(double sx, double sy)
{
- AffineTransform t = new AffineTransform ();
- t.setToScale (sx, sy);
+ AffineTransform t = new AffineTransform();
+ t.setToScale(sx, sy);
return t;
}
- public static AffineTransform getShearInstance (double shx, double shy)
+ /**
+ * Returns a shearing transform (points are shifted in the x direction based
+ * on a factor of their y coordinate, and in the y direction as a factor of
+ * their x coordinate):
+ * <pre>
+ * [ 1 shx 0 ]
+ * [ shy 1 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param shx the x shearing factor
+ * @param shy the y shearing factor
+ * @return the shearing transform
+ */
+ public static AffineTransform getShearInstance(double shx, double shy)
{
- AffineTransform t = new AffineTransform ();
- t.setToShear (shx, shy);
+ AffineTransform t = new AffineTransform();
+ t.setToShear(shx, shy);
return t;
}
- public int getType ()
+ /**
+ * Returns the type of this transform. The result is always valid, although
+ * it may not be the simplest interpretation (in other words, there are
+ * sequences of transforms which reduce to something simpler, which this
+ * does not always detect). The result is either TYPE_GENERAL_TRANSFORM,
+ * or a bit-wise combination of TYPE_TRANSLATION, the mutually exclusive
+ * TYPE_*_ROTATIONs, and the mutually exclusive TYPE_*_SCALEs.
+ *
+ * @see #TYPE_IDENTITY
+ * @see #TYPE_TRANSLATION
+ * @see #TYPE_UNIFORM_SCALE
+ * @see #TYPE_GENERAL_SCALE
+ * @see #TYPE_QUADRANT_ROTATION
+ * @see #TYPE_GENERAL_ROTATION
+ * @see #TYPE_GENERAL_TRANSFORM
+ */
+ public int getType()
{
return type;
}
- public double getDeterminant ()
+ /**
+ * Return the determinant of this transform matrix. If the determinant is
+ * non-zero, the transform is invertible; otherwise operations which require
+ * an inverse throw a NoninvertibleTransformException. A result very near
+ * zero, due to rounding errors, may indicate that inversion results do not
+ * carry enough precision to be meaningful.
+ *
+ * <p>If this is a uniform scale transformation, the determinant also
+ * represents the squared value of the scale. Otherwise, it carries little
+ * additional meaning. The determinant is calculated as:
+ * <pre>
+ * | m00 m01 m02 |
+ * | m10 m11 m12 | = m00 * m11 - m01 * m10
+ * | 0 0 1 |
+ * </pre>
+ *
+ * @return the determinant
+ * @see #createInverse()
+ */
+ public double getDeterminant()
{
return m00 * m11 - m01 * m10;
}
- public void getMatrix (double[] flatmatrix)
+ /**
+ * Return the matrix of values used in this transform. If the matrix has
+ * fewer than 6 entries, only the scale and shear factors are returned;
+ * otherwise the translation factors are copied as well. The resulting
+ * values are:
+ * <pre>
+ * [ d[0] d[2] (d[4]) ]
+ * [ d[1] d[3] (d[5]) ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param d the matrix to store the results into; with 4 (6) entries
+ * @throws NullPointerException if d is null
+ * @throws ArrayIndexOutOfBoundsException if d is too small
+ */
+ public void getMatrix(double[] d)
{
- flatmatrix[0] = m00;
- flatmatrix[1] = m10;
- flatmatrix[2] = m01;
- flatmatrix[3] = m11;
- if (flatmatrix.length >= 6)
+ d[0] = m00;
+ d[1] = m10;
+ d[2] = m01;
+ d[3] = m11;
+ if (d.length >= 6)
{
- flatmatrix[4] = m02;
- flatmatrix[5] = m12;
+ d[4] = m02;
+ d[5] = m12;
}
}
- public double getScaleX ()
+ /**
+ * Returns the X coordinate scaling factor of the matrix.
+ *
+ * @return m00
+ * @see #getMatrix(double[])
+ */
+ public double getScaleX()
{
return m00;
}
- public double getScaleY ()
+ /**
+ * Returns the Y coordinate scaling factor of the matrix.
+ *
+ * @return m11
+ * @see #getMatrix(double[])
+ */
+ public double getScaleY()
{
return m11;
}
- public double getShearX ()
+ /**
+ * Returns the X coordinate shearing factor of the matrix.
+ *
+ * @return m01
+ * @see #getMatrix(double[])
+ */
+ public double getShearX()
{
return m01;
}
- public double getShearY ()
+ /**
+ * Returns the Y coordinate shearing factor of the matrix.
+ *
+ * @return m10
+ * @see #getMatrix(double[])
+ */
+ public double getShearY()
{
return m10;
}
- public double getTranslateX ()
+ /**
+ * Returns the X coordinate translation factor of the matrix.
+ *
+ * @return m02
+ * @see #getMatrix(double[])
+ */
+ public double getTranslateX()
{
return m02;
}
- public double getTranslateY ()
+ /**
+ * Returns the Y coordinate translation factor of the matrix.
+ *
+ * @return m12
+ * @see #getMatrix(double[])
+ */
+ public double getTranslateY()
{
return m12;
}
- public void translate (double tx, double ty)
+ /**
+ * Concatenate a translation onto this transform. This is equivalent, but
+ * more efficient than
+ * <code>concatenate(AffineTransform.getTranslateInstance(tx, ty))</code>.
+ *
+ * @param tx the x translation distance
+ * @param ty the y translation distance
+ * @see #getTranslateInstance(double, double)
+ * @see #concatenate(AffineTransform)
+ */
+ public void translate(double tx, double ty)
{
m02 += tx * m00 + ty * m01;
m12 += tx * m10 + ty * m11;
+ updateType();
}
- public void rotate (double theta)
+ /**
+ * Concatenate a rotation onto this transform. This is equivalent, but
+ * more efficient than
+ * <code>concatenate(AffineTransform.getRotateInstance(theta))</code>.
+ *
+ * @param theta the rotation angle
+ * @see #getRotateInstance(double)
+ * @see #concatenate(AffineTransform)
+ */
+ public void rotate(double theta)
{
- double c = Math.cos (theta);
- double s = Math.sin (theta);
+ double c = Math.cos(theta);
+ double s = Math.sin(theta);
double n00 = m00 * c + m01 * s;
double n01 = m00 * -s + m01 * c;
double n10 = m10 * c + m11 * s;
double n11 = m10 * -s + m11 * c;
-
m00 = n00;
m01 = n01;
m10 = n10;
m11 = n11;
+ updateType();
}
- public void rotate (double theta, double x, double y)
+ /**
+ * Concatenate a rotation about a point onto this transform. This is
+ * equivalent, but more efficient than
+ * <code>concatenate(AffineTransform.getRotateInstance(theta, x, y))</code>.
+ *
+ * @param theta the rotation angle
+ * @param x the x coordinate of the pivot point
+ * @param y the y coordinate of the pivot point
+ * @see #getRotateInstance(double, double, double)
+ * @see #concatenate(AffineTransform)
+ */
+ public void rotate(double theta, double x, double y)
{
- translate (x, y);
- rotate (theta);
- translate (-x, -y);
+ translate(x, y);
+ rotate(theta);
+ translate(-x, -y);
}
- public void scale (double sx, double sy)
+ /**
+ * Concatenate a scale onto this transform. This is equivalent, but more
+ * efficient than
+ * <code>concatenate(AffineTransform.getScaleInstance(sx, sy))</code>.
+ *
+ * @param sx the x scaling factor
+ * @param sy the y scaling factor
+ * @see #getScaleInstance(double, double)
+ * @see #concatenate(AffineTransform)
+ */
+ public void scale(double sx, double sy)
{
m00 *= sx;
m01 *= sy;
m10 *= sx;
m11 *= sy;
+ updateType();
}
- public void shear (double shx, double shy)
+ /**
+ * Concatenate a shearing onto this transform. This is equivalent, but more
+ * efficient than
+ * <code>concatenate(AffineTransform.getShearInstance(sx, sy))</code>.
+ *
+ * @param shx the x shearing factor
+ * @param shy the y shearing factor
+ * @see #getShearInstance(double, double)
+ * @see #concatenate(AffineTransform)
+ */
+ public void shear(double shx, double shy)
{
double n00 = m00 + shx * m01;
double n01 = shx * m00 + m01;
double n10 = m10 * shy + m11;
double n11 = shx * m10 + m11;
-
m00 = n00;
m01 = n01;
m10 = n10;
m11 = n11;
+ updateType();
}
- public void setToIdentity ()
+ /**
+ * Reset this transform to the identity (no transformation):
+ * <pre>
+ * [ 1 0 0 ]
+ * [ 0 1 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ */
+ public void setToIdentity()
{
m00 = m11 = 1;
m01 = m02 = m10 = m12 = 0;
type = TYPE_IDENTITY;
}
- public void setToTranslation (double tx, double ty)
+ /**
+ * Set this transform to a translation:
+ * <pre>
+ * [ 1 0 tx ]
+ * [ 0 1 ty ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param tx the x translation distance
+ * @param ty the y translation distance
+ */
+ public void setToTranslation(double tx, double ty)
{
m00 = m11 = 1;
m01 = m10 = 0;
m02 = tx;
m12 = ty;
- type = TYPE_TRANSLATION;
+ type = (tx == 0 && ty == 0) ? TYPE_UNIFORM_SCALE : TYPE_TRANSLATION;
}
- public void setToRotation (double theta)
+ /**
+ * Set this transform to a rotation. A positive angle (in radians) rotates
+ * the positive x-axis to the positive y-axis:
+ * <pre>
+ * [ cos(theta) -sin(theta) 0 ]
+ * [ sin(theta) cos(theta) 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param theta the rotation angle
+ */
+ public void setToRotation(double theta)
{
- double c = Math.cos (theta);
- double s = Math.sin (theta);
-
+ double c = Math.cos(theta);
+ double s = Math.sin(theta);
m00 = c;
m01 = -s;
m02 = 0;
m10 = s;
m11 = c;
m12 = 0;
- type = TYPE_GENERAL_ROTATION;
+ type = (c == 1 ? TYPE_IDENTITY
+ : c == 0 || c == -1 ? TYPE_QUADRANT_ROTATION
+ : TYPE_GENERAL_ROTATION);
}
- public void setToRotation (double theta, double x, double y)
+ /**
+ * Set this transform to a rotation about a point. A positive angle (in
+ * radians) rotates the positive x-axis to the positive y-axis. This is the
+ * same as calling:
+ * <pre>
+ * tx.setToTranslation(x, y);
+ * tx.rotate(theta);
+ * tx.translate(-x, -y);
+ * </pre>
+ *
+ * <p>The resulting matrix is:
+ * <pre>
+ * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
+ * [ sin(theta) cos(theta) y-x*sin-y*cos ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param theta the rotation angle
+ * @param x the x coordinate of the pivot point
+ * @param y the y coordinate of the pivot point
+ */
+ public void setToRotation(double theta, double x, double y)
{
- double c = Math.cos (theta);
- double s = Math.sin (theta);
-
+ double c = Math.cos(theta);
+ double s = Math.sin(theta);
m00 = c;
m01 = -s;
m02 = x - x * c + y * s;
m10 = s;
m11 = c;
m12 = y - x * s - y * c;
- type = TYPE_GENERAL_TRANSFORM;
+ updateType();
}
- public void setToScale (double sx, double sy)
+ /**
+ * Set this transform to a scale:
+ * <pre>
+ * [ sx 0 0 ]
+ * [ 0 sy 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param sx the x scaling factor
+ * @param sy the y scaling factor
+ */
+ public void setToScale(double sx, double sy)
{
m00 = sx;
m01 = m02 = m10 = m12 = 0;
m11 = sy;
- type = (sx == sy) ? TYPE_UNIFORM_SCALE : TYPE_GENERAL_SCALE;
+ type = (sx != sy ? TYPE_GENERAL_SCALE
+ : sx == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE);
}
- public void setToShear (double shx, double shy)
+ /**
+ * Set this transform to a shear (points are shifted in the x direction based
+ * on a factor of their y coordinate, and in the y direction as a factor of
+ * their x coordinate):
+ * <pre>
+ * [ 1 shx 0 ]
+ * [ shy 1 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param shx the x shearing factor
+ * @param shy the y shearing factor
+ */
+ public void setToShear(double shx, double shy)
{
m00 = m11 = 1;
m01 = shx;
m10 = shy;
m02 = m12 = 0;
- type = TYPE_GENERAL_TRANSFORM;
+ updateType();
}
- public void setTransform (AffineTransform tx)
+ /**
+ * Set this transform to a copy of the given one.
+ *
+ * @param tx the transform to copy
+ * @throws NullPointerException if tx is null
+ */
+ public void setTransform(AffineTransform tx)
{
m00 = tx.m00;
m01 = tx.m01;
@@ -330,8 +903,23 @@ public class AffineTransform implements Cloneable, Serializable
type = tx.type;
}
- public void setTransform (double m00, double m10, double m01,
- double m11, double m02, double m12)
+ /**
+ * Set this transform to the given values:
+ * <pre>
+ * [ m00 m01 m02 ]
+ * [ m10 m11 m12 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @param m00 the x scaling component
+ * @param m10 the y shearing component
+ * @param m01 the x shearing component
+ * @param m11 the y scaling component
+ * @param m02 the x translation component
+ * @param m12 the y translation component
+ */
+ public void setTransform(double m00, double m10, double m01,
+ double m11, double m02, double m12)
{
this.m00 = m00;
this.m10 = m10;
@@ -339,10 +927,22 @@ public class AffineTransform implements Cloneable, Serializable
this.m11 = m11;
this.m02 = m02;
this.m12 = m12;
- this.type = 0; // FIXME
+ updateType();
}
- public void concatenate (AffineTransform tx)
+ /**
+ * Set this transform to the result of performing the original version of
+ * this followed by tx. This is commonly used when chaining transformations
+ * from one space to another. In matrix form:
+ * <pre>
+ * [ this ] = [ this ] x [ tx ]
+ * </pre>
+ *
+ * @param tx the transform to concatenate
+ * @throws NullPointerException if tx is null
+ * @see #preConcatenate(AffineTransform)
+ */
+ public void concatenate(AffineTransform tx)
{
double n00 = m00 * tx.m00 + m01 * tx.m10;
double n01 = m00 * tx.m01 + m01 * tx.m11;
@@ -350,16 +950,29 @@ public class AffineTransform implements Cloneable, Serializable
double n10 = m10 * tx.m00 + m11 * tx.m10;
double n11 = m10 * tx.m01 + m11 * tx.m11;
double n12 = m10 * tx.m02 + m11 * tx.m12 + m12;
-
m00 = n00;
m01 = n01;
m02 = n02;
m10 = n10;
m11 = n11;
m12 = n12;
+ updateType();
}
- public void preConcatenate (AffineTransform tx)
+ /**
+ * Set this transform to the result of performing tx followed by the
+ * original version of this. This is less common than normal concatenation,
+ * but can still be used to chain transformations from one space to another.
+ * In matrix form:
+ * <pre>
+ * [ this ] = [ tx ] x [ this ]
+ * </pre>
+ *
+ * @param tx the transform to concatenate
+ * @throws NullPointerException if tx is null
+ * @see #concatenate(AffineTransform)
+ */
+ public void preConcatenate(AffineTransform tx)
{
double n00 = tx.m00 * m00 + tx.m01 * m10;
double n01 = tx.m00 * m01 + tx.m01 * m11;
@@ -367,328 +980,490 @@ public class AffineTransform implements Cloneable, Serializable
double n10 = tx.m10 * m00 + tx.m11 * m10;
double n11 = tx.m10 * m01 + tx.m11 * m11;
double n12 = tx.m10 * m02 + tx.m11 * m12 + tx.m12;
-
m00 = n00;
m01 = n01;
m02 = n02;
m10 = n10;
m11 = n11;
m12 = n12;
+ updateType();
}
- public AffineTransform createInverse ()
+ /**
+ * Returns a transform, which if concatenated to this one, will result in
+ * the identity transform. This is useful for undoing transformations, but
+ * is only possible if the original transform has an inverse (ie. does not
+ * map multiple points to the same line or point). A transform exists only
+ * if getDeterminant() has a non-zero value.
+ *
+ * @return a new inverse transform
+ * @throws NoninvertibleTransformException if inversion is not possible
+ * @see #getDeterminant()
+ */
+ public AffineTransform createInverse()
throws NoninvertibleTransformException
{
- double det = getDeterminant ();
+ double det = getDeterminant();
if (det == 0)
- throw new NoninvertibleTransformException ("can't invert transform");
-
- double i00 = m11 / det;
- double i01 = -m10 / det;
- double i02 = 0;
- double i10 = m01 / det;
- double i11 = -m00 / det;
- double i12 = 0;
-
- return new AffineTransform (i00, i01, i02,
- i10, i11, i12);
+ throw new NoninvertibleTransformException("can't invert transform");
+ return new AffineTransform(m11 / det, -m10 / det, m01 / det, -m00 / det,
+ -m02, -m12);
}
- public Point2D transform (Point2D src, Point2D dst)
+ /**
+ * Perform this transformation on the given source point, and store the
+ * result in the destination (creating it if necessary). It is safe for
+ * src and dst to be the same.
+ *
+ * @param src the source point
+ * @param dst the destination, or null
+ * @return the transformation of src, in dst if it was non-null
+ * @throws NullPointerException if src is null
+ */
+ public Point2D transform(Point2D src, Point2D dst)
{
if (dst == null)
- dst = new Point2D.Double ();
-
- // We compute and set separately to correctly overwrite if
- // src==dst.
- double x = src.getX ();
- double y = src.getY ();
+ dst = new Point2D.Double();
+ double x = src.getX();
+ double y = src.getY();
double nx = m00 * x + m01 * y + m02;
double ny = m10 * x + m11 * y + m12;
-
- dst.setLocation (nx, ny);
-
+ dst.setLocation(nx, ny);
return dst;
}
- public void transform (Point2D[] src, int srcOff,
- Point2D[] dst, int dstOff,
- int num)
+ /**
+ * Perform this transformation on an array of points, storing the results
+ * in another (possibly same) array. This will not create a destination
+ * array, but will create points for the null entries of the destination.
+ * The transformation is done sequentially. While having a single source
+ * and destination point be the same is safe, you should be aware that
+ * duplicate references to the same point in the source, and having the
+ * source overlap the destination, may result in your source points changing
+ * from a previous transform before it is their turn to be evaluated.
+ *
+ * @param src the array of source points
+ * @param srcOff the starting offset into src
+ * @param dst the array of destination points (may have null entries)
+ * @param dstOff the starting offset into dst
+ * @param num the number of points to transform
+ * @throws NullPointerException if src or dst is null, or src has null
+ * entries
+ * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded
+ * @throws ArrayStoreException if new points are incompatible with dst
+ */
+ public void transform(Point2D[] src, int srcOff,
+ Point2D[] dst, int dstOff, int num)
{
- while (num-- > 0)
- {
- dst[dstOff] = transform (src[srcOff], dst[dstOff]);
- ++srcOff;
- ++dstOff;
- }
+ while (--num >= 0)
+ dst[dstOff] = transform(src[srcOff++], dst[dstOff++]);
}
- public void transform (float[] srcPts, int srcOff,
- float[] dstPts, int dstOff,
- int num)
+ /**
+ * Perform this transformation on an array of points, in (x,y) pairs,
+ * storing the results in another (possibly same) array. This will not
+ * create a destination array. All sources are copied before the
+ * transformation, so that no result will overwrite a point that has not yet
+ * been evaluated.
+ *
+ * @param src the array of source points
+ * @param srcOff the starting offset into src
+ * @param dst the array of destination points
+ * @param dstOff the starting offset into dst
+ * @param num the number of points to transform
+ * @throws NullPointerException if src or dst is null
+ * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded
+ */
+ public void transform(float[] srcPts, int srcOff,
+ float[] dstPts, int dstOff, int num)
{
- while (num-- > 0)
+ if (srcPts == dstPts && dstOff > srcOff
+ && num > 1 && srcOff + 2 * num > dstOff)
{
- float x = srcPts[srcOff];
- float y = srcPts[srcOff + 1];
- srcOff += 2;
- float nx = (float) (m00 * x + m01 * y + m02);
- float ny = (float) (m10 * x + m10 * y + m12);
- dstPts[dstOff] = nx;
- dstPts[dstOff + 1] = ny;
- dstOff += 2;
+ float[] f = new float[2 * num];
+ System.arraycopy(srcPts, srcOff, f, 0, 2 * num);
+ srcPts = f;
+ }
+ while (--num >= 0)
+ {
+ float x = srcPts[srcOff++];
+ float y = srcPts[srcOff++];
+ dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02);
+ dstPts[dstOff++] = (float) (m10 * x + m10 * y + m12);
}
}
- public void transform (double[] srcPts, int srcOff,
- double[] dstPts, int dstOff,
- int num)
+ /**
+ * Perform this transformation on an array of points, in (x,y) pairs,
+ * storing the results in another (possibly same) array. This will not
+ * create a destination array. All sources are copied before the
+ * transformation, so that no result will overwrite a point that has not yet
+ * been evaluated.
+ *
+ * @param src the array of source points
+ * @param srcOff the starting offset into src
+ * @param dst the array of destination points
+ * @param dstOff the starting offset into dst
+ * @param num the number of points to transform
+ * @throws NullPointerException if src or dst is null
+ * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded
+ */
+ public void transform(double[] srcPts, int srcOff,
+ double[] dstPts, int dstOff, int num)
{
- while (num-- > 0)
+ if (srcPts == dstPts && dstOff > srcOff
+ && num > 1 && srcOff + 2 * num > dstOff)
+ {
+ double[] d = new double[2 * num];
+ System.arraycopy(srcPts, srcOff, d, 0, 2 * num);
+ srcPts = d;
+ }
+ while (--num >= 0)
{
- double x = srcPts[srcOff];
- double y = srcPts[srcOff + 1];
- srcOff += 2;
- double nx = m00 * x + m01 * y + m02;
- double ny = m10 * x + m10 * y + m12;
- dstPts[dstOff] = nx;
- dstPts[dstOff + 1] = ny;
- dstOff += 2;
+ double x = srcPts[srcOff++];
+ double y = srcPts[srcOff++];
+ dstPts[dstOff++] = m00 * x + m01 * y + m02;
+ dstPts[dstOff++] = m10 * x + m10 * y + m12;
}
}
- public void transform (float[] srcPts, int srcOff,
- double[] dstPts, int dstOff,
- int num)
+ /**
+ * Perform this transformation on an array of points, in (x,y) pairs,
+ * storing the results in another array. This will not create a destination
+ * array.
+ *
+ * @param src the array of source points
+ * @param srcOff the starting offset into src
+ * @param dst the array of destination points
+ * @param dstOff the starting offset into dst
+ * @param num the number of points to transform
+ * @throws NullPointerException if src or dst is null
+ * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded
+ */
+ public void transform(float[] srcPts, int srcOff,
+ double[] dstPts, int dstOff, int num)
{
- while (num-- > 0)
+ while (--num >= 0)
{
- float x = srcPts[srcOff];
- float y = srcPts[srcOff + 1];
- srcOff += 2;
- double nx = m00 * x + m01 * y + m02;
- double ny = m10 * x + m10 * y + m12;
- dstPts[dstOff] = nx;
- dstPts[dstOff + 1] = ny;
- dstOff += 2;
+ float x = srcPts[srcOff++];
+ float y = srcPts[srcOff++];
+ dstPts[dstOff++] = m00 * x + m01 * y + m02;
+ dstPts[dstOff++] = m10 * x + m10 * y + m12;
}
}
- public void transform (double[] srcPts, int srcOff,
- float[] dstPts, int dstOff,
- int num)
+ /**
+ * Perform this transformation on an array of points, in (x,y) pairs,
+ * storing the results in another array. This will not create a destination
+ * array.
+ *
+ * @param src the array of source points
+ * @param srcOff the starting offset into src
+ * @param dst the array of destination points
+ * @param dstOff the starting offset into dst
+ * @param num the number of points to transform
+ * @throws NullPointerException if src or dst is null
+ * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded
+ */
+ public void transform(double[] srcPts, int srcOff,
+ float[] dstPts, int dstOff, int num)
{
- while (num-- > 0)
+ while (--num >= 0)
{
- double x = srcPts[srcOff];
- double y = srcPts[srcOff + 1];
- srcOff += 2;
- float nx = (float) (m00 * x + m01 * y + m02);
- float ny = (float) (m10 * x + m10 * y + m12);
- dstPts[dstOff] = nx;
- dstPts[dstOff + 1] = ny;
- dstOff += 2;
+ double x = srcPts[srcOff++];
+ double y = srcPts[srcOff++];
+ dstPts[dstOff++] = (float) (m00 * x + m01 * y + m02);
+ dstPts[dstOff++] = (float) (m10 * x + m10 * y + m12);
}
}
- public Point2D inverseTransform (Point2D src, Point2D dst)
+ /**
+ * Perform the inverse of this transformation on the given source point,
+ * and store the result in the destination (creating it if necessary). It
+ * is safe for src and dst to be the same.
+ *
+ * @param src the source point
+ * @param dst the destination, or null
+ * @return the inverse transformation of src, in dst if it was non-null
+ * @throws NullPointerException if src is null
+ * @throws NoninvertibleTransformException if the inverse does not exist
+ * @see #getDeterminant()
+ */
+ public Point2D inverseTransform(Point2D src, Point2D dst)
throws NoninvertibleTransformException
{
- double det = getDeterminant ();
+ double det = getDeterminant();
if (det == 0)
- throw new NoninvertibleTransformException ("couldn't invert transform");
-
+ throw new NoninvertibleTransformException("couldn't invert transform");
if (dst == null)
- dst = new Point2D.Double ();
- double x = src.getX ();
- double y = src.getY ();
- double nx = (m11 * x + - m10 * y) / det;
- double ny = (m01 * x + - m00 * y) / det;
- dst.setLocation (nx, ny);
+ dst = new Point2D.Double();
+ double x = src.getX();
+ double y = src.getY();
+ double nx = (m11 * x + -m10 * y) / det - m02;
+ double ny = (m01 * x + -m00 * y) / det - m12;
+ dst.setLocation(nx, ny);
return dst;
}
- public void inverseTransform (double[] srcPts, int srcOff,
- double[] dstPts, int dstOff,
- int num)
+ /**
+ * Perform the inverse of this transformation on an array of points, in
+ * (x,y) pairs, storing the results in another (possibly same) array. This
+ * will not create a destination array. All sources are copied before the
+ * transformation, so that no result will overwrite a point that has not yet
+ * been evaluated.
+ *
+ * @param src the array of source points
+ * @param srcOff the starting offset into src
+ * @param dst the array of destination points
+ * @param dstOff the starting offset into dst
+ * @param num the number of points to transform
+ * @throws NullPointerException if src or dst is null
+ * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded
+ * @throws NoninvertibleTransformException if the inverse does not exist
+ * @see #getDeterminant()
+ */
+ public void inverseTransform(double[] srcPts, int srcOff,
+ double[] dstPts, int dstOff, int num)
throws NoninvertibleTransformException
{
- double det = getDeterminant ();
+ double det = getDeterminant();
if (det == 0)
- throw new NoninvertibleTransformException ("couldn't invert transform");
-
- while (num-- > 0)
+ throw new NoninvertibleTransformException("couldn't invert transform");
+ if (srcPts == dstPts && dstOff > srcOff
+ && num > 1 && srcOff + 2 * num > dstOff)
+ {
+ double[] d = new double[2 * num];
+ System.arraycopy(srcPts, srcOff, d, 0, 2 * num);
+ srcPts = d;
+ }
+ while (--num >= 0)
{
- double x = srcPts[srcOff];
- double y = srcPts[srcOff + 1];
- double nx = (m11 * x + - m10 * y) / det;
- double ny = (m01 * x + - m00 * y) / det;
- dstPts[dstOff] = nx;
- dstPts[dstOff + 1] = ny;
- dstOff += 2;
- srcOff += 2;
+ double x = srcPts[srcOff++];
+ double y = srcPts[srcOff++];
+ dstPts[dstOff++] = (m11 * x + -m10 * y) / det - m02;
+ dstPts[dstOff++] = (m01 * x + -m00 * y) / det - m12;
}
}
- public Point2D deltaTransform (Point2D src, Point2D dst)
+ /**
+ * Perform this transformation, less any translation, on the given source
+ * point, and store the result in the destination (creating it if
+ * necessary). It is safe for src and dst to be the same. The reduced
+ * transform is equivalent to:
+ * <pre>
+ * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ]
+ * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ]
+ * </pre>
+ *
+ * @param src the source point
+ * @param dst the destination, or null
+ * @return the delta transformation of src, in dst if it was non-null
+ * @throws NullPointerException if src is null
+ */
+ public Point2D deltaTransform(Point2D src, Point2D dst)
{
if (dst == null)
- dst = new Point2D.Double ();
- double x = src.getX ();
- double y = src.getY ();
+ dst = new Point2D.Double();
+ double x = src.getX();
+ double y = src.getY();
double nx = m00 * x + m01 * y;
double ny = m10 * x + m11 * y;
- dst.setLocation (nx, ny);
+ dst.setLocation(nx, ny);
return dst;
}
- public void deltaTransform (double[] srcPts, int srcOff,
- double[] dstPts, int dstOff,
- int num)
+ /**
+ * Perform this transformation, less any translation, on an array of points,
+ * in (x,y) pairs, storing the results in another (possibly same) array.
+ * This will not create a destination array. All sources are copied before
+ * the transformation, so that no result will overwrite a point that has
+ * not yet been evaluated. The reduced transform is equivalent to:
+ * <pre>
+ * [ x' ] = [ m00 m01 ] [ x ] = [ m00 * x + m01 * y ]
+ * [ y' ] [ m10 m11 ] [ y ] = [ m10 * x + m11 * y ]
+ * </pre>
+ *
+ * @param src the array of source points
+ * @param srcOff the starting offset into src
+ * @param dst the array of destination points
+ * @param dstOff the starting offset into dst
+ * @param num the number of points to transform
+ * @throws NullPointerException if src or dst is null
+ * @throws ArrayIndexOutOfBoundsException if array bounds are exceeded
+ */
+ public void deltaTransform(double[] srcPts, int srcOff,
+ double[] dstPts, int dstOff,
+ int num)
{
- while (num-- > 0)
+ if (srcPts == dstPts && dstOff > srcOff
+ && num > 1 && srcOff + 2 * num > dstOff)
{
- double x = srcPts[srcOff];
- double y = srcPts[srcOff + 1];
- double nx = m00 * x + m01 * y;
- double ny = m10 * x + m11 * y;
- dstPts[dstOff] = nx;
- dstPts[dstOff + 1] = ny;
- dstOff += 2;
- srcOff += 2;
+ double[] d = new double[2 * num];
+ System.arraycopy(srcPts, srcOff, d, 0, 2 * num);
+ srcPts = d;
+ }
+ while (--num >= 0)
+ {
+ double x = srcPts[srcOff++];
+ double y = srcPts[srcOff++];
+ dstPts[dstOff++] = m00 * x + m01 * y;
+ dstPts[dstOff++] = m10 * x + m11 * y;
}
}
- public Shape createTransformedShape (Shape pSrc)
+ /**
+ * Return a new Shape, based on the given one, where the path of the shape
+ * has been transformed by this transform. Notice that this uses GeneralPath,
+ * which only stores points in float precision.
+ *
+ * @param src the shape source to transform
+ * @return the shape, transformed by this
+ * @throws NullPointerException if src is null
+ * @see GeneralPath#transform(AffineTransform)
+ */
+ public Shape createTransformedShape(Shape src)
{
- // FIXME
- return null;
+ GeneralPath p = new GeneralPath(src);
+ p.transform(this);
+ return p;
}
- public String toString ()
+ /**
+ * Returns a string representation of the transform, in the format:
+ * <code>"AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], ["
+ * + m10 + ", " + m11 + ", " + m12 + "]]"</code>.
+ *
+ * @return the string representation
+ */
+ public String toString()
{
- // FIXME
- return null;
+ return "AffineTransform[[" + m00 + ", " + m01 + ", " + m02 + "], ["
+ + m10 + ", " + m11 + ", " + m12 + "]]";
}
- public boolean isIdentity ()
+ /**
+ * Tests if this transformation is the identity:
+ * <pre>
+ * [ 1 0 0 ]
+ * [ 0 1 0 ]
+ * [ 0 0 1 ]
+ * </pre>
+ *
+ * @return true if this is the identity transform
+ */
+ public boolean isIdentity()
{
+ // Rather than rely on type, check explicitly.
return (m00 == 1 && m01 == 0 && m02 == 0
- && m10 == 0 && m11 == 1 && m12 == 0);
+ && m10 == 0 && m11 == 1 && m12 == 0);
}
- public Object clone ()
+ /**
+ * Create a new transform of the same run-time type, with the same
+ * transforming properties as this one.
+ *
+ * @return the clone
+ */
+ public Object clone()
{
- return new AffineTransform (this);
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw (Error) new InternalError().initCause(e); // Impossible
+ }
}
- public int hashCode ()
+ /**
+ * Return the hashcode for this transformation. The formula is not
+ * documented, but appears to be the same as:
+ * <pre>
+ * long l = Double.doubleToLongBits(getScaleX());
+ * l = l * 31 + Double.doubleToLongBits(getShearY());
+ * l = l * 31 + Double.doubleToLongBits(getShearX());
+ * l = l * 31 + Double.doubleToLongBits(getScaleY());
+ * l = l * 31 + Double.doubleToLongBits(getTranslateX());
+ * l = l * 31 + Double.doubleToLongBits(getTranslateY());
+ * return (int) ((l >> 32) ^ l);
+ * </pre>
+ *
+ * @return the hashcode
+ */
+ public int hashCode()
{
- // FIXME
- return 23;
+ long l = Double.doubleToLongBits(m00);
+ l = l * 31 + Double.doubleToLongBits(m10);
+ l = l * 31 + Double.doubleToLongBits(m01);
+ l = l * 31 + Double.doubleToLongBits(m11);
+ l = l * 31 + Double.doubleToLongBits(m02);
+ l = l * 31 + Double.doubleToLongBits(m12);
+ return (int) ((l >> 32) ^ l);
}
- public boolean equals (Object obj)
+ /**
+ * Compares two transforms for equality. This returns true if they have the
+ * same matrix values.
+ *
+ * @param o the transform to compare
+ * @return true if it is equal
+ */
+ public boolean equals(Object obj)
{
if (! (obj instanceof AffineTransform))
return false;
AffineTransform t = (AffineTransform) obj;
return (m00 == t.m00 && m01 == t.m01 && m02 == t.m02
- && m10 == t.m10 && m11 == t.m11 && m12 == t.m12);
- }
-
- // This iterator is used to apply an AffineTransform to some other
- // iterator. It is not private because we want to be able to access
- // it from the rest of this package.
- class Iterator implements PathIterator
- {
- // The iterator we are applied to.
- private PathIterator subIterator;
-
- public Iterator (PathIterator subIterator)
- {
- this.subIterator = subIterator;
- }
-
- public int currentSegment (double[] coords)
- {
- int r = subIterator.currentSegment (coords);
- int count = 0;
-
- switch (r)
- {
- case SEG_CUBICTO:
- count = 3;
- break;
-
- case SEG_QUADTO:
- count = 2;
- break;
-
- case SEG_LINETO:
- case SEG_MOVETO:
- count = 1;
- break;
-
- default:
- // Error. But how to report?
- case SEG_CLOSE:
- break;
- }
-
- transform (coords, 0, coords, 0, count);
-
- return r;
- }
-
- public int currentSegment (float[] coords)
- {
- int r = subIterator.currentSegment (coords);
- int count = 0;
-
- switch (r)
- {
- case SEG_CUBICTO:
- count = 3;
- break;
-
- case SEG_QUADTO:
- count = 2;
- break;
-
- case SEG_LINETO:
- case SEG_MOVETO:
- count = 1;
- break;
-
- default:
- // Error. But how to report?
- case SEG_CLOSE:
- break;
- }
-
- transform (coords, 0, coords, 0, count);
-
- return r;
- }
-
- public int getWindingRule ()
- {
- return subIterator.getWindingRule ();
- }
-
- public boolean isDone ()
- {
- return subIterator.isDone ();
- }
-
- public void next ()
- {
- subIterator.next ();
- }
- }
-
- private double m00, m01, m02;
- private double m10, m11, m12;
- private int type;
-}
+ && m10 == t.m10 && m11 == t.m11 && m12 == t.m12);
+ }
+
+ /**
+ * Helper to decode the type from the matrix. This is not guaranteed
+ * to find the optimal type, but at least it will be valid.
+ */
+ private void updateType()
+ {
+ double det = getDeterminant();
+ if (det == 0)
+ {
+ type = TYPE_GENERAL_TRANSFORM;
+ return;
+ }
+ // Scale (includes rotation by PI) or translation.
+ if (m01 == 0 && m10 == 0)
+ {
+ if (m00 == m11)
+ type = m00 == 1 ? TYPE_IDENTITY : TYPE_UNIFORM_SCALE;
+ else
+ type = TYPE_GENERAL_SCALE;
+ if (m02 != 0 || m12 != 0)
+ type |= TYPE_TRANSLATION;
+ }
+ // Rotation.
+ else if (m00 == m11 && m01 == -m10)
+ {
+ type = m00 == 0 ? TYPE_QUADRANT_ROTATION : TYPE_GENERAL_ROTATION;
+ if (det != 1)
+ type |= TYPE_UNIFORM_SCALE;
+ if (m02 != 0 || m12 != 0)
+ type |= TYPE_TRANSLATION;
+ }
+ else
+ type = TYPE_GENERAL_TRANSFORM;
+ }
+
+ /**
+ * Reads a transform from an object stream.
+ *
+ * @param s the stream to read from
+ * @throws ClassNotFoundException if there is a problem deserializing
+ * @throws IOException if there is a problem deserializing
+ */
+ private void readObject(ObjectInputStream s)
+ throws ClassNotFoundException, IOException
+ {
+ s.defaultReadObject();
+ updateType();
+ }
+} // class AffineTransform
OpenPOWER on IntegriCloud