summaryrefslogtreecommitdiffstats
path: root/awt/java/awt/geom/AffineTransform.java
diff options
context:
space:
mode:
Diffstat (limited to 'awt/java/awt/geom/AffineTransform.java')
-rw-r--r--awt/java/awt/geom/AffineTransform.java1158
1 files changed, 1158 insertions, 0 deletions
diff --git a/awt/java/awt/geom/AffineTransform.java b/awt/java/awt/geom/AffineTransform.java
new file mode 100644
index 0000000..5fd3934
--- /dev/null
+++ b/awt/java/awt/geom/AffineTransform.java
@@ -0,0 +1,1158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author Denis M. Kishenko
+ * @version $Revision$
+ */
+package java.awt.geom;
+
+import java.awt.Shape;
+import java.io.IOException;
+import java.io.Serializable;
+
+import org.apache.harmony.awt.internal.nls.Messages;
+import org.apache.harmony.misc.HashCode;
+
+/**
+ * The Class AffineTransform represents a linear transformation
+ * (rotation, scaling, or shear) followed by a translation that
+ * acts on a coordinate space. It preserves colinearity of points
+ * and ratios of distances between collinear points: so if A, B,
+ * and C are on a line, then after the space has been transformed
+ * via the affine transform, the images of the three points will
+ * still be on a line, and the ratio of the distance from A to B
+ * with the distance from B to C will be the same as the corresponding
+ * ratio in the image space.
+ */
+public class AffineTransform implements Cloneable, Serializable {
+
+ /** The Constant serialVersionUID. */
+ private static final long serialVersionUID = 1330973210523860834L;
+
+ /** The Constant TYPE_IDENTITY. */
+ public static final int TYPE_IDENTITY = 0;
+
+ /** The Constant TYPE_TRANSLATION. */
+ public static final int TYPE_TRANSLATION = 1;
+
+ /** The Constant TYPE_UNIFORM_SCALE. */
+ public static final int TYPE_UNIFORM_SCALE = 2;
+
+ /** The Constant TYPE_GENERAL_SCALE. */
+ public static final int TYPE_GENERAL_SCALE = 4;
+
+ /** The Constant TYPE_QUADRANT_ROTATION. */
+ public static final int TYPE_QUADRANT_ROTATION = 8;
+
+ /** The Constant TYPE_GENERAL_ROTATION. */
+ public static final int TYPE_GENERAL_ROTATION = 16;
+
+ /** The Constant TYPE_GENERAL_TRANSFORM. */
+ public static final int TYPE_GENERAL_TRANSFORM = 32;
+
+ /** The Constant TYPE_FLIP. */
+ public static final int TYPE_FLIP = 64;
+
+ /** The Constant TYPE_MASK_SCALE. */
+ public static final int TYPE_MASK_SCALE = TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE;
+
+ /** The Constant TYPE_MASK_ROTATION. */
+ public static final int TYPE_MASK_ROTATION = TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION;
+
+ /** The <code>TYPE_UNKNOWN</code> is an initial type value. */
+ static final int TYPE_UNKNOWN = -1;
+
+ /** The min value equivalent to zero. If absolute value less then ZERO it considered as zero. */
+ static final double ZERO = 1E-10;
+
+ /** The values of transformation matrix. */
+ double m00;
+
+ /** The m10. */
+ double m10;
+
+ /** The m01. */
+ double m01;
+
+ /** The m11. */
+ double m11;
+
+ /** The m02. */
+ double m02;
+
+ /** The m12. */
+ double m12;
+
+ /** The transformation <code>type</code>. */
+ transient int type;
+
+ /**
+ * Instantiates a new affine transform of type <code>TYPE_IDENTITY</code>
+ * (which leaves coordinates unchanged).
+ */
+ public AffineTransform() {
+ type = TYPE_IDENTITY;
+ m00 = m11 = 1.0;
+ m10 = m01 = m02 = m12 = 0.0;
+ }
+
+ /**
+ * Instantiates a new affine transform that has the same data as
+ * the given AffineTransform.
+ *
+ * @param t the transform to copy.
+ */
+ public AffineTransform(AffineTransform t) {
+ this.type = t.type;
+ this.m00 = t.m00;
+ this.m10 = t.m10;
+ this.m01 = t.m01;
+ this.m11 = t.m11;
+ this.m02 = t.m02;
+ this.m12 = t.m12;
+ }
+
+ /**
+ * Instantiates a new affine transform by specifying the values of the 2x3
+ * transformation matrix as floats. The type is set to the default
+ * type: <code>TYPE_UNKNOWN</code>
+ *
+ * @param m00 the m00 entry in the transformation matrix.
+ * @param m10 the m10 entry in the transformation matrix.
+ * @param m01 the m01 entry in the transformation matrix.
+ * @param m11 the m11 entry in the transformation matrix.
+ * @param m02 the m02 entry in the transformation matrix.
+ * @param m12 the m12 entry in the transformation matrix.
+ */
+ public AffineTransform(float m00, float m10, float m01, float m11, float m02, float m12) {
+ this.type = TYPE_UNKNOWN;
+ this.m00 = m00;
+ this.m10 = m10;
+ this.m01 = m01;
+ this.m11 = m11;
+ this.m02 = m02;
+ this.m12 = m12;
+ }
+
+ /**
+ * Instantiates a new affine transform by specifying the values of the 2x3
+ * transformation matrix as doubles. The type is set to the default
+ * type: <code>TYPE_UNKNOWN</code>
+ *
+ * @param m00 the m00 entry in the transformation matrix.
+ * @param m10 the m10 entry in the transformation matrix.
+ * @param m01 the m01 entry in the transformation matrix.
+ * @param m11 the m11 entry in the transformation matrix.
+ * @param m02 the m02 entry in the transformation matrix.
+ * @param m12 the m12 entry in the transformation matrix.
+ */
+ public AffineTransform(double m00, double m10, double m01, double m11, double m02, double m12) {
+ this.type = TYPE_UNKNOWN;
+ this.m00 = m00;
+ this.m10 = m10;
+ this.m01 = m01;
+ this.m11 = m11;
+ this.m02 = m02;
+ this.m12 = m12;
+ }
+
+ /**
+ * Instantiates a new affine transform by reading the values of the
+ * transformation matrix from an array of floats. The mapping from the
+ * array to the matrix starts with <code>matrix[0]</code> giving the
+ * top-left entry of the matrix and
+ * proceeds with the usual left-to-right and top-down ordering.
+ * <p>
+ * If the array has only four entries, then the two entries of the
+ * last row of the transformation matrix default to zero.
+ *
+ * @param matrix the array of four or six floats giving the values
+ * of the matrix.
+ *
+ * @throws ArrayIndexOutOfBoundsException if the size of the array
+ * is 0, 1, 2, 3, or 5.
+ */
+ public AffineTransform(float[] matrix) {
+ this.type = TYPE_UNKNOWN;
+ m00 = matrix[0];
+ m10 = matrix[1];
+ m01 = matrix[2];
+ m11 = matrix[3];
+ if (matrix.length > 4) {
+ m02 = matrix[4];
+ m12 = matrix[5];
+ }
+ }
+
+ /**
+ * Instantiates a new affine transform by reading the values of the
+ * transformation matrix from an array of doubles. The mapping from the
+ * array to the matrix starts with <code>matrix[0]</code> giving the
+ * top-left entry of the matrix and
+ * proceeds with the usual left-to-right and top-down ordering.
+ * <p>
+ * If the array has only four entries, then the two entries of the
+ * last row of the transformation matrix default to zero.
+ *
+ * @param matrix the array of four or six doubles giving the values
+ * of the matrix.
+ *
+ * @throws ArrayIndexOutOfBoundsException if the size of the array
+ * is 0, 1, 2, 3, or 5.
+ */
+ public AffineTransform(double[] matrix) {
+ this.type = TYPE_UNKNOWN;
+ m00 = matrix[0];
+ m10 = matrix[1];
+ m01 = matrix[2];
+ m11 = matrix[3];
+ if (matrix.length > 4) {
+ m02 = matrix[4];
+ m12 = matrix[5];
+ }
+ }
+
+
+ /**
+ * Returns type of the affine transformation.
+ * <p>
+ * The type is computed as follows: Label the entries of the
+ * transformation matrix as three rows (m00, m01), (m10, m11), and
+ * (m02, m12). Then if the original basis vectors are (1, 0) and (0, 1),
+ * the new basis vectors after transformation are given by (m00, m01)
+ * and (m10, m11), and the translation vector is (m02, m12).
+ * <p>
+ * The types are classified as follows: <br/>
+ * TYPE_IDENTITY - no change<br/>
+ * TYPE_TRANSLATION - The translation vector isn't zero<br/>
+ * TYPE_UNIFORM_SCALE - The new basis vectors have equal length<br/>
+ * TYPE_GENERAL_SCALE - The new basis vectors dont' have equal length<br/>
+ * TYPE_FLIP - The new basis vector orientation differs from the original one<br/>
+ * TYPE_QUADRANT_ROTATION - The new basis is a rotation of the original by 90, 180, 270, or 360 degrees<br/>
+ * TYPE_GENERAL_ROTATION - The new basis is a rotation of the original by an arbitrary angle<br/>
+ * TYPE_GENERAL_TRANSFORM - The transformation can't be inverted.<br/>
+ * <p>
+ * Note that multiple types are possible, thus the types can be combined
+ * using bitwise combinations.
+ *
+ * @return the type of the Affine Transform.
+ */
+ public int getType() {
+ if (type != TYPE_UNKNOWN) {
+ return type;
+ }
+
+ int type = 0;
+
+ if (m00 * m01 + m10 * m11 != 0.0) {
+ type |= TYPE_GENERAL_TRANSFORM;
+ return type;
+ }
+
+ if (m02 != 0.0 || m12 != 0.0) {
+ type |= TYPE_TRANSLATION;
+ } else
+ if (m00 == 1.0 && m11 == 1.0 && m01 == 0.0 && m10 == 0.0) {
+ type = TYPE_IDENTITY;
+ return type;
+ }
+
+ if (m00 * m11 - m01 * m10 < 0.0) {
+ type |= TYPE_FLIP;
+ }
+
+ double dx = m00 * m00 + m10 * m10;
+ double dy = m01 * m01 + m11 * m11;
+ if (dx != dy) {
+ type |= TYPE_GENERAL_SCALE;
+ } else
+ if (dx != 1.0) {
+ type |= TYPE_UNIFORM_SCALE;
+ }
+
+ if ((m00 == 0.0 && m11 == 0.0) ||
+ (m10 == 0.0 && m01 == 0.0 && (m00 < 0.0 || m11 < 0.0)))
+ {
+ type |= TYPE_QUADRANT_ROTATION;
+ } else
+ if (m01 != 0.0 || m10 != 0.0) {
+ type |= TYPE_GENERAL_ROTATION;
+ }
+
+ return type;
+ }
+
+ /**
+ * Gets the scale x entry of the transformation matrix
+ * (the upper left matrix entry).
+ *
+ * @return the scale x value.
+ */
+ public double getScaleX() {
+ return m00;
+ }
+
+ /**
+ * Gets the scale y entry of the transformation matrix
+ * (the lower right entry of the linear transformation).
+ *
+ * @return the scale y value.
+ */
+ public double getScaleY() {
+ return m11;
+ }
+
+ /**
+ * Gets the shear x entry of the transformation matrix
+ * (the upper right entry of the linear transformation).
+ *
+ * @return the shear x value.
+ */
+ public double getShearX() {
+ return m01;
+ }
+
+ /**
+ * Gets the shear y entry of the transformation matrix
+ * (the lower left entry of the linear transformation).
+ *
+ * @return the shear y value.
+ */
+ public double getShearY() {
+ return m10;
+ }
+
+ /**
+ * Gets the x coordinate of the translation vector.
+ *
+ * @return the x coordinate of the translation vector.
+ */
+ public double getTranslateX() {
+ return m02;
+ }
+
+ /**
+ * Gets the y coordinate of the translation vector.
+ *
+ * @return the y coordinate of the translation vector.
+ */
+ public double getTranslateY() {
+ return m12;
+ }
+
+ /**
+ * Checks if the AffineTransformation is the identity.
+ *
+ * @return true, if the AffineTransformation is the identity.
+ */
+ public boolean isIdentity() {
+ return getType() == TYPE_IDENTITY;
+ }
+
+ /**
+ * Writes the values of the transformation matrix into the given
+ * array of doubles. If the array has length 4, only the linear
+ * transformation part will be written into it. If it has length
+ * greater than 4, the translation vector will be included as well.
+ *
+ * @param matrix the array to fill with the values of the matrix.
+ *
+ * @throws ArrayIndexOutOfBoundsException if the size of the array
+ * is 0, 1, 2, 3, or 5.
+ */
+ public void getMatrix(double[] matrix) {
+ matrix[0] = m00;
+ matrix[1] = m10;
+ matrix[2] = m01;
+ matrix[3] = m11;
+ if (matrix.length > 4) {
+ matrix[4] = m02;
+ matrix[5] = m12;
+ }
+ }
+
+ /**
+ * Gets the determinant of the linear transformation matrix.
+ *
+ * @return the determinant of the linear transformation matrix.
+ */
+ public double getDeterminant() {
+ return m00 * m11 - m01 * m10;
+ }
+
+ /**
+ * Sets the transform in terms of a list of double values.
+ *
+ * @param m00 the m00 coordinate of the transformation matrix.
+ * @param m10 the m10 coordinate of the transformation matrix.
+ * @param m01 the m01 coordinate of the transformation matrix.
+ * @param m11 the m11 coordinate of the transformation matrix.
+ * @param m02 the m02 coordinate of the transformation matrix.
+ * @param m12 the m12 coordinate of the transformation matrix.
+ */
+ public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12) {
+ this.type = TYPE_UNKNOWN;
+ this.m00 = m00;
+ this.m10 = m10;
+ this.m01 = m01;
+ this.m11 = m11;
+ this.m02 = m02;
+ this.m12 = m12;
+ }
+
+ /**
+ * Sets the transform's data to match the data of the transform
+ * sent as a parameter.
+ *
+ * @param t the transform that gives the new values.
+ */
+ public void setTransform(AffineTransform t) {
+ type = t.type;
+ setTransform(t.m00, t.m10, t.m01, t.m11, t.m02, t.m12);
+ }
+
+ /**
+ * Sets the transform to the identity transform.
+ */
+ public void setToIdentity() {
+ type = TYPE_IDENTITY;
+ m00 = m11 = 1.0;
+ m10 = m01 = m02 = m12 = 0.0;
+ }
+
+ /**
+ * Sets the transformation to a translation alone.
+ * Sets the linear part of the transformation to identity
+ * and the translation vector to the values sent as parameters.
+ * Sets the type to <code>TYPE_IDENTITY</code>
+ * if the resulting AffineTransformation is the identity
+ * transformation, otherwise sets it to <code>TYPE_TRANSLATION</code>.
+ *
+ * @param mx the distance to translate in the x direction.
+ * @param my the distance to translate in the y direction.
+ */
+ public void setToTranslation(double mx, double my) {
+ m00 = m11 = 1.0;
+ m01 = m10 = 0.0;
+ m02 = mx;
+ m12 = my;
+ if (mx == 0.0 && my == 0.0) {
+ type = TYPE_IDENTITY;
+ } else {
+ type = TYPE_TRANSLATION;
+ }
+ }
+
+ /**
+ * Sets the transformation to being a scale alone, eliminating
+ * rotation, shear, and translation elements.
+ * Sets the type to <code>TYPE_IDENTITY</code>
+ * if the resulting AffineTransformation is the identity
+ * transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>.
+ *
+ * @param scx the scaling factor in the x direction.
+ * @param scy the scaling factor in the y direction.
+ */
+ public void setToScale(double scx, double scy) {
+ m00 = scx;
+ m11 = scy;
+ m10 = m01 = m02 = m12 = 0.0;
+ if (scx != 1.0 || scy != 1.0) {
+ type = TYPE_UNKNOWN;
+ } else {
+ type = TYPE_IDENTITY;
+ }
+ }
+
+ /**
+ * Sets the transformation to being a shear alone, eliminating
+ * rotation, scaling, and translation elements.
+ * Sets the type to <code>TYPE_IDENTITY</code>
+ * if the resulting AffineTransformation is the identity
+ * transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>.
+ *
+ * @param shx the shearing factor in the x direction.
+ * @param shy the shearing factor in the y direction.
+ */
+ public void setToShear(double shx, double shy) {
+ m00 = m11 = 1.0;
+ m02 = m12 = 0.0;
+ m01 = shx;
+ m10 = shy;
+ if (shx != 0.0 || shy != 0.0) {
+ type = TYPE_UNKNOWN;
+ } else {
+ type = TYPE_IDENTITY;
+ }
+ }
+
+ /**
+ * Sets the transformation to being a rotation alone, eliminating
+ * shearing, scaling, and translation elements.
+ * Sets the type to <code>TYPE_IDENTITY</code>
+ * if the resulting AffineTransformation is the identity
+ * transformation, otherwise sets it to <code>TYPE_UNKNOWN</code>.
+ *
+ * @param angle the angle of rotation in radians.
+ */
+ public void setToRotation(double angle) {
+ double sin = Math.sin(angle);
+ double cos = Math.cos(angle);
+ if (Math.abs(cos) < ZERO) {
+ cos = 0.0;
+ sin = sin > 0.0 ? 1.0 : -1.0;
+ } else
+ if (Math.abs(sin) < ZERO) {
+ sin = 0.0;
+ cos = cos > 0.0 ? 1.0 : -1.0;
+ }
+ m00 = m11 = cos;
+ m01 = -sin;
+ m10 = sin;
+ m02 = m12 = 0.0;
+ type = TYPE_UNKNOWN;
+ }
+
+ /**
+ * Sets the transformation to being a rotation followed by a
+ * translation.
+ * Sets the type to <code>TYPE_UNKNOWN</code>.
+ *
+ * @param angle the angle of rotation in radians.
+ * @param px the distance to translate in the x direction.
+ * @param py the distance to translate in the y direction.
+ */
+ public void setToRotation(double angle, double px, double py) {
+ setToRotation(angle);
+ m02 = px * (1.0 - m00) + py * m10;
+ m12 = py * (1.0 - m00) - px * m10;
+ type = TYPE_UNKNOWN;
+ }
+
+ /**
+ * Creates a new AffineTransformation that is a translation alone
+ * with the translation vector given by the values sent as parameters.
+ * The new transformation's type is <code>TYPE_IDENTITY</code>
+ * if the AffineTransformation is the identity
+ * transformation, otherwise it's <code>TYPE_TRANSLATION</code>.
+ *
+ * @param mx the distance to translate in the x direction.
+ * @param my the distance to translate in the y direction.
+
+ * @return the new AffineTransformation.
+ */
+ public static AffineTransform getTranslateInstance(double mx, double my) {
+ AffineTransform t = new AffineTransform();
+ t.setToTranslation(mx, my);
+ return t;
+ }
+
+ /**
+ * Creates a new AffineTransformation that is a scale alone.
+ * The new transformation's type is <code>TYPE_IDENTITY</code>
+ * if the AffineTransformation is the identity
+ * transformation, otherwise it's <code>TYPE_UNKNOWN</code>.
+ *
+ * @param scx the scaling factor in the x direction.
+ * @param scY the scaling factor in the y direction.
+ *
+ * @return the new AffineTransformation.
+ */
+ public static AffineTransform getScaleInstance(double scx, double scY) {
+ AffineTransform t = new AffineTransform();
+ t.setToScale(scx, scY);
+ return t;
+ }
+
+ /**
+ * Creates a new AffineTransformation that is a shear alone.
+ * The new transformation's type is <code>TYPE_IDENTITY</code>
+ * if the AffineTransformation is the identity
+ * transformation, otherwise it's <code>TYPE_UNKNOWN</code>.
+ *
+ * @param shx the shearing factor in the x direction.
+ * @param shy the shearing factor in the y direction.
+ *
+ * @return the new AffineTransformation.
+ */
+ public static AffineTransform getShearInstance(double shx, double shy) {
+ AffineTransform m = new AffineTransform();
+ m.setToShear(shx, shy);
+ return m;
+ }
+
+ /**
+ * Creates a new AffineTransformation that is a rotation alone.
+ * The new transformation's type is <code>TYPE_IDENTITY</code>
+ * if the AffineTransformation is the identity
+ * transformation, otherwise it's <code>TYPE_UNKNOWN</code>.
+ *
+ * @param angle the angle of rotation in radians.
+ *
+ * @return the new AffineTransformation.
+ */
+ public static AffineTransform getRotateInstance(double angle) {
+ AffineTransform t = new AffineTransform();
+ t.setToRotation(angle);
+ return t;
+ }
+
+ /**
+ * Creates a new AffineTransformation that is a rotation followed by a
+ * translation.
+ * Sets the type to <code>TYPE_UNKNOWN</code>.
+ *
+ * @param angle the angle of rotation in radians.
+ * @param x the distance to translate in the x direction.
+ * @param y the distance to translate in the y direction.
+ *
+ * @return the new AffineTransformation.
+ */
+ public static AffineTransform getRotateInstance(double angle, double x, double y) {
+ AffineTransform t = new AffineTransform();
+ t.setToRotation(angle, x, y);
+ return t;
+ }
+
+ /**
+ * Applies a translation to this AffineTransformation.
+ *
+ * @param mx the distance to translate in the x direction.
+ * @param my the distance to translate in the y direction.
+ */
+ public void translate(double mx, double my) {
+ concatenate(AffineTransform.getTranslateInstance(mx, my));
+ }
+
+ /**
+ * Applies a scaling transformation to this AffineTransformation.
+ *
+ * @param scx the scaling factor in the x direction.
+ * @param scy the scaling factor in the y direction.
+ */
+ public void scale(double scx, double scy) {
+ concatenate(AffineTransform.getScaleInstance(scx, scy));
+ }
+
+ /**
+ * Applies a shearing transformation to this AffineTransformation.
+ *
+ * @param shx the shearing factor in the x direction.
+ * @param shy the shearing factor in the y direction.
+ */
+ public void shear(double shx, double shy) {
+ concatenate(AffineTransform.getShearInstance(shx, shy));
+ }
+
+ /**
+ * Applies a rotation transformation to this AffineTransformation.
+ *
+ * @param angle the angle of rotation in radians.
+ */
+ public void rotate(double angle) {
+ concatenate(AffineTransform.getRotateInstance(angle));
+ }
+
+ /**
+ * Applies a rotation and translation transformation to this
+ * AffineTransformation.
+ *
+ * @param angle the angle of rotation in radians.
+ * @param px the distance to translate in the x direction.
+ * @param py the distance to translate in the y direction.
+ */
+ public void rotate(double angle, double px, double py) {
+ concatenate(AffineTransform.getRotateInstance(angle, px, py));
+ }
+
+ /**
+ * Multiplies the matrix representations of two AffineTransform objects.
+ *
+ * @param t1 - the AffineTransform object is a multiplicand
+ * @param t2 - the AffineTransform object is a multiplier
+ *
+ * @return an AffineTransform object that is the result of t1 multiplied by the matrix t2.
+ */
+ AffineTransform multiply(AffineTransform t1, AffineTransform t2) {
+ return new AffineTransform(
+ t1.m00 * t2.m00 + t1.m10 * t2.m01, // m00
+ t1.m00 * t2.m10 + t1.m10 * t2.m11, // m01
+ t1.m01 * t2.m00 + t1.m11 * t2.m01, // m10
+ t1.m01 * t2.m10 + t1.m11 * t2.m11, // m11
+ t1.m02 * t2.m00 + t1.m12 * t2.m01 + t2.m02, // m02
+ t1.m02 * t2.m10 + t1.m12 * t2.m11 + t2.m12);// m12
+ }
+
+ /**
+ * Applies the given AffineTransform to this AffineTransform
+ * via matrix multiplication.
+ *
+ * @param t the AffineTransform to apply to this AffineTransform.
+ */
+ public void concatenate(AffineTransform t) {
+ setTransform(multiply(t, this));
+ }
+
+ /**
+ * Changes the current AffineTransform the one obtained by
+ * taking the transform t and applying this AffineTransform to it.
+ *
+ * @param t the AffineTransform that this AffineTransform is multiplied by.
+ */
+ public void preConcatenate(AffineTransform t) {
+ setTransform(multiply(this, t));
+ }
+
+ /**
+ * Creates an AffineTransform that is the inverse of this transform.
+ *
+ * @return the affine transform that is the inverse of this AffineTransform.
+ *
+ * @throws NoninvertibleTransformException if this AffineTransform cannot be
+ * inverted (the determinant of the linear transformation part is zero).
+ */
+ public AffineTransform createInverse() throws NoninvertibleTransformException {
+ double det = getDeterminant();
+ if (Math.abs(det) < ZERO) {
+ // awt.204=Determinant is zero
+ throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
+ }
+ return new AffineTransform(
+ m11 / det, // m00
+ -m10 / det, // m10
+ -m01 / det, // m01
+ m00 / det, // m11
+ (m01 * m12 - m11 * m02) / det, // m02
+ (m10 * m02 - m00 * m12) / det // m12
+ );
+ }
+
+ /**
+ * Apply the current AffineTransform to the point.
+ *
+ * @param src the original point.
+ * @param dst Point2D object to be filled with the destination
+ * coordinates (where the original point is sent by this AffineTransform). May be null.
+ *
+ * @return the point in the AffineTransform's image space where the
+ * original point is sent.
+ */
+ public Point2D transform(Point2D src, Point2D dst) {
+ if (dst == null) {
+ if (src instanceof Point2D.Double) {
+ dst = new Point2D.Double();
+ } else {
+ dst = new Point2D.Float();
+ }
+ }
+
+ double x = src.getX();
+ double y = src.getY();
+
+ dst.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
+ return dst;
+ }
+
+ /**
+ * Applies this AffineTransform to an array of points.
+ *
+ * @param src the array of points to be transformed.
+ * @param srcOff the offset in the source point array of the first point
+ * to be transformed.
+ * @param dst the point array where the images of the points (after
+ * applying the AffineTransformation) should be placed.
+ * @param dstOff the offset in the destination array where the new
+ * values should be written.
+ * @param length the number of points to transform.
+ *
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>srcOff + length > src.length</code> or
+ * <code>dstOff + length > dst.length</code>.
+ */
+ public void transform(Point2D[] src, int srcOff, Point2D[] dst, int dstOff, int length) {
+ while (--length >= 0) {
+ Point2D srcPoint = src[srcOff++];
+ double x = srcPoint.getX();
+ double y = srcPoint.getY();
+ Point2D dstPoint = dst[dstOff];
+ if (dstPoint == null) {
+ if (srcPoint instanceof Point2D.Double) {
+ dstPoint = new Point2D.Double();
+ } else {
+ dstPoint = new Point2D.Float();
+ }
+ }
+ dstPoint.setLocation(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12);
+ dst[dstOff++] = dstPoint;
+ }
+ }
+
+ /**
+ * Applies this AffineTransform to a set of points given
+ * as an array of double values where every two values in the array
+ * give the coordinates of a point; the even-indexed values giving the
+ * x coordinates and the odd-indexed values giving the y coordinates.
+ *
+ * @param src the array of points to be transformed.
+ * @param srcOff the offset in the source point array of the first point
+ * to be transformed.
+ * @param dst the point array where the images of the points (after
+ * applying the AffineTransformation) should be placed.
+ * @param dstOff the offset in the destination array where the new
+ * values should be written.
+ * @param length the number of points to transform.
+ *
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>srcOff + length*2 > src.length</code> or
+ * <code>dstOff + length*2 > dst.length</code>.
+ */
+ public void transform(double[] src, int srcOff, double[] dst, int dstOff, int length) {
+ int step = 2;
+ if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
+ srcOff = srcOff + length * 2 - 2;
+ dstOff = dstOff + length * 2 - 2;
+ step = -2;
+ }
+ while (--length >= 0) {
+ double x = src[srcOff + 0];
+ double y = src[srcOff + 1];
+ dst[dstOff + 0] = x * m00 + y * m01 + m02;
+ dst[dstOff + 1] = x * m10 + y * m11 + m12;
+ srcOff += step;
+ dstOff += step;
+ }
+ }
+
+ /**
+ * Applies this AffineTransform to a set of points given
+ * as an array of float values where every two values in the array
+ * give the coordinates of a point; the even-indexed values giving the
+ * x coordinates and the odd-indexed values giving the y coordinates.
+ *
+ * @param src the array of points to be transformed.
+ * @param srcOff the offset in the source point array of the first point
+ * to be transformed.
+ * @param dst the point array where the images of the points (after
+ * applying the AffineTransformation) should be placed.
+ * @param dstOff the offset in the destination array where the new
+ * values should be written.
+ * @param length the number of points to transform.
+ *
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>srcOff + length*2 > src.length</code> or
+ * <code>dstOff + length*2 > dst.length</code>.
+ */
+ public void transform(float[] src, int srcOff, float[] dst, int dstOff, int length) {
+ int step = 2;
+ if (src == dst && srcOff < dstOff && dstOff < srcOff + length * 2) {
+ srcOff = srcOff + length * 2 - 2;
+ dstOff = dstOff + length * 2 - 2;
+ step = -2;
+ }
+ while (--length >= 0) {
+ float x = src[srcOff + 0];
+ float y = src[srcOff + 1];
+ dst[dstOff + 0] = (float)(x * m00 + y * m01 + m02);
+ dst[dstOff + 1] = (float)(x * m10 + y * m11 + m12);
+ srcOff += step;
+ dstOff += step;
+ }
+ }
+
+ /**
+ * Applies this AffineTransform to a set of points given
+ * as an array of float values where every two values in the array
+ * give the coordinates of a point; the even-indexed values giving the
+ * x coordinates and the odd-indexed values giving the y coordinates.
+ * The destination coordinates are given as values of type <code>double</code>.
+ *
+ * @param src the array of points to be transformed.
+ * @param srcOff the offset in the source point array of the first point
+ * to be transformed.
+ * @param dst the point array where the images of the points (after
+ * applying the AffineTransformation) should be placed.
+ * @param dstOff the offset in the destination array where the new
+ * values should be written.
+ * @param length the number of points to transform.
+ *
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>srcOff + length*2 > src.length</code> or
+ * <code>dstOff + length*2 > dst.length</code>.
+ */
+ public void transform(float[] src, int srcOff, double[] dst, int dstOff, int length) {
+ while (--length >= 0) {
+ float x = src[srcOff++];
+ float y = src[srcOff++];
+ dst[dstOff++] = x * m00 + y * m01 + m02;
+ dst[dstOff++] = x * m10 + y * m11 + m12;
+ }
+ }
+
+ /**
+ * Applies this AffineTransform to a set of points given
+ * as an array of double values where every two values in the array
+ * give the coordinates of a point; the even-indexed values giving the
+ * x coordinates and the odd-indexed values giving the y coordinates.
+ * The destination coordinates are given as values of type <code>float</code>.
+ *
+ * @param src the array of points to be transformed.
+ * @param srcOff the offset in the source point array of the first point
+ * to be transformed.
+ * @param dst the point array where the images of the points (after
+ * applying the AffineTransformation) should be placed.
+ * @param dstOff the offset in the destination array where the new
+ * values should be written.
+ * @param length the number of points to transform.
+ *
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>srcOff + length*2 > src.length</code> or
+ * <code>dstOff + length*2 > dst.length</code>.
+ */
+ public void transform(double[] src, int srcOff, float[] dst, int dstOff, int length) {
+ while (--length >= 0) {
+ double x = src[srcOff++];
+ double y = src[srcOff++];
+ dst[dstOff++] = (float)(x * m00 + y * m01 + m02);
+ dst[dstOff++] = (float)(x * m10 + y * m11 + m12);
+ }
+ }
+
+ /**
+ * Transforms the point according to the linear transformation
+ * part of this AffineTransformation (without applying the translation).
+ *
+ * @param src the original point.
+ * @param dst the point object where the result of the delta transform
+ * is written.
+ *
+ * @return the result of applying the delta transform (linear part
+ * only) to the original point.
+ */
+ // TODO: is this right? if dst is null, we check what it's an
+ // instance of? Shouldn't it be src instanceof Point2D.Double?
+ public Point2D deltaTransform(Point2D src, Point2D dst) {
+ if (dst == null) {
+ if (dst instanceof Point2D.Double) {
+ dst = new Point2D.Double();
+ } else {
+ dst = new Point2D.Float();
+ }
+ }
+
+ double x = src.getX();
+ double y = src.getY();
+
+ dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
+ return dst;
+ }
+
+ /**
+ * Applies the linear transformation part of this AffineTransform
+ * (ignoring the translation part) to a set of points given
+ * as an array of double values where every two values in the array
+ * give the coordinates of a point; the even-indexed values giving the
+ * x coordinates and the odd-indexed values giving the y coordinates.
+ *
+ * @param src the array of points to be transformed.
+ * @param srcOff the offset in the source point array of the first point
+ * to be transformed.
+ * @param dst the point array where the images of the points (after
+ * applying the delta transformation) should be placed.
+ * @param dstOff the offset in the destination array where the new
+ * values should be written.
+ * @param length the number of points to transform.
+ *
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>srcOff + length*2 > src.length</code> or
+ * <code>dstOff + length*2 > dst.length</code>.
+ */
+ public void deltaTransform(double[] src, int srcOff, double[] dst, int dstOff, int length) {
+ while (--length >= 0) {
+ double x = src[srcOff++];
+ double y = src[srcOff++];
+ dst[dstOff++] = x * m00 + y * m01;
+ dst[dstOff++] = x * m10 + y * m11;
+ }
+ }
+
+ /**
+ * Transforms the point according to the inverse of this AffineTransformation.
+ *
+ * @param src the original point.
+ * @param dst the point object where the result of the inverse transform
+ * is written (may be null).
+ *
+ * @return the result of applying the inverse transform.
+ * Inverse transform.
+ *
+ * @throws NoninvertibleTransformException if this AffineTransform cannot be
+ * inverted (the determinant of the linear transformation part is zero).
+ */
+ public Point2D inverseTransform(Point2D src, Point2D dst) throws NoninvertibleTransformException {
+ double det = getDeterminant();
+ if (Math.abs(det) < ZERO) {
+ // awt.204=Determinant is zero
+ throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
+ }
+
+ if (dst == null) {
+ if (src instanceof Point2D.Double) {
+ dst = new Point2D.Double();
+ } else {
+ dst = new Point2D.Float();
+ }
+ }
+
+ double x = src.getX() - m02;
+ double y = src.getY() - m12;
+
+ dst.setLocation((x * m11 - y * m01) / det, (y * m00 - x * m10) / det);
+ return dst;
+ }
+
+ /**
+ * Applies the inverse of this AffineTransform to a set of points given
+ * as an array of double values where every two values in the array
+ * give the coordinates of a point; the even-indexed values giving the
+ * x coordinates and the odd-indexed values giving the y coordinates.
+ *
+ * @param src the array of points to be transformed.
+ * @param srcOff the offset in the source point array of the first point
+ * to be transformed.
+ * @param dst the point array where the images of the points (after
+ * applying the inverse of the AffineTransformation) should be placed.
+ * @param dstOff the offset in the destination array where the new
+ * values should be written.
+ * @param length the number of points to transform.
+ *
+ * @throws ArrayIndexOutOfBoundsException if
+ * <code>srcOff + length*2 > src.length</code> or
+ * <code>dstOff + length*2 > dst.length</code>.
+ * @throws NoninvertibleTransformException if this AffineTransform cannot be
+ * inverted (the determinant of the linear transformation part is zero).
+ */
+ public void inverseTransform(double[] src, int srcOff, double[] dst, int dstOff, int length)
+ throws NoninvertibleTransformException
+ {
+ double det = getDeterminant();
+ if (Math.abs(det) < ZERO) {
+ // awt.204=Determinant is zero
+ throw new NoninvertibleTransformException(Messages.getString("awt.204")); //$NON-NLS-1$
+ }
+
+ while (--length >= 0) {
+ double x = src[srcOff++] - m02;
+ double y = src[srcOff++] - m12;
+ dst[dstOff++] = (x * m11 - y * m01) / det;
+ dst[dstOff++] = (y * m00 - x * m10) / det;
+ }
+ }
+
+ /**
+ * Creates a new shape whose data is given by applying this
+ * AffineTransform to the specified shape.
+ *
+ * @param src the original shape whose data is to be transformed.
+ *
+ * @return the new shape found by applying this AffineTransform to
+ * the original shape.
+ */
+ public Shape createTransformedShape(Shape src) {
+ if (src == null) {
+ return null;
+ }
+ if (src instanceof GeneralPath) {
+ return ((GeneralPath)src).createTransformedShape(this);
+ }
+ PathIterator path = src.getPathIterator(this);
+ GeneralPath dst = new GeneralPath(path.getWindingRule());
+ dst.append(path, false);
+ return dst;
+ }
+
+ @Override
+ public String toString() {
+ return
+ getClass().getName() +
+ "[[" + m00 + ", " + m01 + ", " + m02 + "], [" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ + m10 + ", " + m11 + ", " + m12 + "]]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError();
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ HashCode hash = new HashCode();
+ hash.append(m00);
+ hash.append(m01);
+ hash.append(m02);
+ hash.append(m10);
+ hash.append(m11);
+ hash.append(m12);
+ return hash.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof AffineTransform) {
+ AffineTransform t = (AffineTransform)obj;
+ return
+ m00 == t.m00 && m01 == t.m01 &&
+ m02 == t.m02 && m10 == t.m10 &&
+ m11 == t.m11 && m12 == t.m12;
+ }
+ return false;
+ }
+
+
+ /**
+ * Writes the AffineTrassform object to the output steam.
+ *
+ * @param stream - the output stream
+ *
+ * @throws IOException - if there are I/O errors while writing to the output strem
+ */
+ private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
+ stream.defaultWriteObject();
+ }
+
+
+ /**
+ * Read the AffineTransform object from the input stream.
+ *
+ * @param stream - the input steam
+ *
+ * @throws IOException - if there are I/O errors while reading from the input strem
+ * @throws ClassNotFoundException - if class could not be found
+ */
+ private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
+ stream.defaultReadObject();
+ type = TYPE_UNKNOWN;
+ }
+
+}
+