diff options
Diffstat (limited to 'awt/java/awt/geom/AffineTransform.java')
-rw-r--r-- | awt/java/awt/geom/AffineTransform.java | 1158 |
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; + } + +} + |