- /*
- * @(#)AffineTransform.java 1.62 00/02/02
- *
- * Copyright 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
- *
- * This software is the proprietary information of Sun Microsystems, Inc.
- * Use is subject to license terms.
- *
- */
- package java.awt.geom;
- import java.awt.Shape;
- /**
- * The <code>AffineTransform</code> class represents a 2D affine transform
- * that performs a linear mapping from 2D coordinates to other 2D
- * coordinates that preserves the "straightness" and
- * "parallelness" of lines. Affine transformations can be constructed
- * using sequences of translations, scales, flips, rotations, and shears.
- * <p>
- * Such a coordinate transformation can be represented by a 3 row by
- * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix
- * transforms source coordinates <code>(x, y)</code> into
- * destination coordinates <code>(x', y')</code> by considering
- * them to be a column vector and multiplying the coordinate vector
- * by the matrix according to the following process:
- * <pre>
- * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
- * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]
- * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
- * </pre>
- *
- * @version 1.62, 02/02/00
- * @author Jim Graham
- */
- public class AffineTransform implements Cloneable, java.io.Serializable {
- /**
- * This constant indicates that the transform defined by this object
- * is an identity transform.
- * An identity transform is one in which the output coordinates are
- * always the same as the input coordinates.
- * If this transform is anything other than the identity transform,
- * the type will either be the constant GENERAL_TRANSFORM or a
- * combination of the appropriate flag bits for the various coordinate
- * conversions that this transform performs.
- * @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;
- /**
- * This flag bit indicates that the transform defined by this object
- * performs a translation in addition to the conversions indicated
- * by other flag bits.
- * A translation moves the coordinates by a constant amount in x
- * and y without changing the length or angle of vectors.
- * @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;
- /**
- * This flag bit indicates that the transform defined by this object
- * performs a uniform scale in addition to the conversions indicated
- * by other flag bits.
- * A uniform scale multiplies the length of vectors by the same amount
- * in both the x and y directions without changing the angle between
- * vectors.
- * This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag.
- * @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 #getType
- */
- public static final int TYPE_UNIFORM_SCALE = 2;
- /**
- * This flag bit indicates that the transform defined by this object
- * performs a general scale in addition to the conversions indicated
- * by other flag bits.
- * A general scale multiplies the length of vectors by different
- * amounts in the x and y directions without changing the angle
- * between perpendicular vectors.
- * This flag bit is mutually exclusive with the TYPE_UNIFORM_SCALE flag.
- * @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 #getType
- */
- public static final int TYPE_GENERAL_SCALE = 4;
- /**
- * This constant is a bit mask for any of the scale flag bits.
- * @see #TYPE_UNIFORM_SCALE
- * @see #TYPE_GENERAL_SCALE
- */
- public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE |
- TYPE_GENERAL_SCALE);
- /**
- * This flag bit indicates that the transform defined by this object
- * performs a mirror image flip about some axis which changes the
- * normally right handed coordinate system into a left handed
- * system in addition to the conversions indicated by other flag bits.
- * A right handed coordinate system is one where the positive X
- * axis rotates counterclockwise to overlay the positive Y axis
- * similar to the direction that the fingers on your right hand
- * curl when you stare end on at your thumb.
- * A left handed coordinate system is one where the positive X
- * axis rotates clockwise to overlay the positive Y axis similar
- * to the direction that the fingers on your left hand curl.
- * There is no mathematical way to determine the angle of the
- * original flipping or mirroring transformation since all angles
- * of flip are identical given an appropriate adjusting rotation.
- * @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;
- /* NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public
- * circulation and the flag bits could no longer be conveniently
- * renumbered without introducing binary incompatibility in outside
- * code.
- */
- /**
- * This flag bit indicates that the transform defined by this object
- * performs a quadrant rotation by some multiple of 90 degrees in
- * addition to the conversions indicated by other flag bits.
- * A rotation changes the angles of vectors by the same amount
- * regardless of the original direction of the vector and without
- * changing the length of the vector.
- * This flag bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag.
- * @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 #getType
- */
- public static final int TYPE_QUADRANT_ROTATION = 8;
- /**
- * This flag bit indicates that the transform defined by this object
- * performs a rotation by an arbitrary angle in addition to the
- * conversions indicated by other flag bits.
- * A rotation changes the angles of vectors by the same amount
- * regardless of the original direction of the vector and without
- * changing the length of the vector.
- * This flag bit is mutually exclusive with the
- * TYPE_QUADRANT_ROTATION flag.
- * @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 #getType
- */
- public static final int TYPE_GENERAL_ROTATION = 16;
- /**
- * This constant is a bit mask for any of the rotation flag bits.
- * @see #TYPE_QUADRANT_ROTATION
- * @see #TYPE_GENERAL_ROTATION
- */
- public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION |
- TYPE_GENERAL_ROTATION);
- /**
- * This constant indicates that the transform defined by this object
- * performs an arbitrary conversion of the input coordinates.
- * If this transform can be classified by any of the above constants,
- * the type will either be the constant TYPE_IDENTITY or a
- * combination of the appropriate flag bits for the various coordinate
- * conversions that this transform performs.
- * @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;
- /**
- * This constant is used for the internal state variable to indicate
- * that no calculations need to be performed and that the source
- * coordinates only need to be copied to their destinations to
- * complete the transformation equation of this transform.
- * @see #APPLY_TRANSLATE
- * @see #APPLY_SCALE
- * @see #APPLY_SHEAR
- * @see #state
- */
- static final int APPLY_IDENTITY = 0;
- /**
- * This constant is used for the internal state variable to indicate
- * that the translation components of the matrix (m02 and m12) need
- * to be added to complete the transformation equation of this transform.
- * @see #APPLY_IDENTITY
- * @see #APPLY_SCALE
- * @see #APPLY_SHEAR
- * @see #state
- */
- static final int APPLY_TRANSLATE = 1;
- /**
- * This constant is used for the internal state variable to indicate
- * that the scaling components of the matrix (m00 and m11) need
- * to be factored in to complete the transformation equation of
- * this transform. If the APPLY_SHEAR bit is also set then it
- * indicates that the scaling components are not both 0.0. If the
- * APPLY_SHEAR bit is not also set then it indicates that the
- * scaling components are not both 1.0. If neither the APPLY_SHEAR
- * nor the APPLY_SCALE bits are set then the scaling components
- * are both 1.0, which means that the x and y components contribute
- * to the transformed coordinate, but they are not multiplied by
- * any scaling factor.
- * @see #APPLY_IDENTITY
- * @see #APPLY_TRANSLATE
- * @see #APPLY_SHEAR
- * @see #state
- */
- static final int APPLY_SCALE = 2;
- /**
- * This constant is used for the internal state variable to indicate
- * that the shearing components of the matrix (m01 and m10) need
- * to be factored in to complete the transformation equation of this
- * transform. The presence of this bit in the state variable changes
- * the interpretation of the APPLY_SCALE bit as indicated in its
- * documentation.
- * @see #APPLY_IDENTITY
- * @see #APPLY_TRANSLATE
- * @see #APPLY_SCALE
- * @see #state
- */
- static final int APPLY_SHEAR = 4;
- /*
- * For methods which combine together the state of two separate
- * transforms and dispatch based upon the combination, these constants
- * specify how far to shift one of the states so that the two states
- * are mutually non-interfering and provide constants for testing the
- * bits of the shifted (HI) state. The methods in this class use
- * the convention that the state of "this" transform is unshifted and
- * the state of the "other" or "argument" transform is shifted (HI).
- */
- private static final int HI_SHIFT = 3;
- private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT;
- private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT;
- private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT;
- private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT;
- /**
- * The X coordinate scaling element of the 3x3
- * affine transformation matrix.
- *
- * @serial
- */
- double m00;
- /**
- * The Y coordinate shearing element of the 3x3
- * affine transformation matrix.
- *
- * @serial
- */
- double m10;
- /**
- * The X coordinate shearing element of the 3x3
- * affine transformation matrix.
- *
- * @serial
- */
- double m01;
- /**
- * The Y coordinate scaling element of the 3x3
- * affine transformation matrix.
- *
- * @serial
- */
- double m11;
- /**
- * The X coordinate of the translation element of the
- * 3x3 affine transformation matrix.
- *
- * @serial
- */
- double m02;
- /**
- * The Y coordinate of the translation element of the
- * 3x3 affine transformation matrix.
- *
- * @serial
- */
- double m12;
- /**
- * This field keeps track of which components of the matrix need to
- * be applied when performing a transformation.
- * @see #APPLY_IDENTITY
- * @see #APPLY_TRANSLATE
- * @see #APPLY_SCALE
- * @see #APPLY_SHEAR
- */
- transient int state;
- private AffineTransform(double m00, double m10,
- double m01, double m11,
- double m02, double m12,
- int state) {
- this.m00 = m00;
- this.m10 = m10;
- this.m01 = m01;
- this.m11 = m11;
- this.m02 = m02;
- this.m12 = m12;
- this.state = state;
- }
- /**
- * Constructs a new <code>AffineTransform</code> representing the
- * Identity transformation.
- */
- public AffineTransform() {
- m00 = m11 = 1.0;
- // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */
- // state = APPLY_IDENTITY; /* Not needed. */
- }
- /**
- * Constructs a new <code>AffineTransform</code> that is a copy of
- * the specified <code>AffineTransform</code> object.
- * @param Tx the <code>AffineTransform</code> object to copy
- */
- public AffineTransform(AffineTransform Tx) {
- this.m00 = Tx.m00;
- this.m10 = Tx.m10;
- this.m01 = Tx.m01;
- this.m11 = Tx.m11;
- this.m02 = Tx.m02;
- this.m12 = Tx.m12;
- this.state = Tx.state;
- }
- /**
- * Constructs a new <code>AffineTransform</code> from 6 floating point
- * values representing the 6 specifiable entries of the 3x3
- * transformation matrix.
- * @param m00, m01, m02, m10, m11, m12 the
- * 6 floating point values that compose the 3x3 transformation matrix
- */
- public AffineTransform(float m00, float m10,
- float m01, float m11,
- float m02, float m12) {
- this.m00 = m00;
- this.m10 = m10;
- this.m01 = m01;
- this.m11 = m11;
- this.m02 = m02;
- this.m12 = m12;
- updateState();
- }
- /**
- * Constructs a new <code>AffineTransform</code> from an array of
- * floating point values representing either the 4 non-translation
- * enries or the 6 specifiable entries of the 3x3 transformation
- * matrix. The values are retrieved from the array as
- * { m00 m10 m01 m11 [m02 m12]}.
- * @param flatmatrix the float array containing the values to be set
- * in the new <code>AffineTransform</code> object. The length of the
- * array is assumed to be at least 4. If the length of the array is
- * less than 6, only the first 4 values are taken. If the length of
- * the array is greater than 6, the first 6 values are taken.
- */
- public AffineTransform(float[] flatmatrix) {
- m00 = flatmatrix[0];
- m10 = flatmatrix[1];
- m01 = flatmatrix[2];
- m11 = flatmatrix[3];
- if (flatmatrix.length > 5) {
- m02 = flatmatrix[4];
- m12 = flatmatrix[5];
- }
- updateState();
- }
- /**
- * Constructs a new <code>AffineTransform</code> from 6 double
- * precision values representing the 6 specifiable entries of the 3x3
- * transformation matrix.
- * @param m00, m01, m02, m10, m11, m12 the
- * 6 floating point values that compose the 3x3 transformation matrix
- */
- public AffineTransform(double m00, double m10,
- double m01, double m11,
- double m02, double m12) {
- this.m00 = m00;
- this.m10 = m10;
- this.m01 = m01;
- this.m11 = m11;
- this.m02 = m02;
- this.m12 = m12;
- updateState();
- }
- /**
- * Constructs a new <code>AffineTransform</code> from an array of
- * double precision values representing either the 4 non-translation
- * entries or the 6 specifiable entries of the 3x3 transformation
- * matrix. The values are retrieved from the array as
- * { m00 m10 m01 m11 [m02 m12]}.
- * @param flatmatrix the double array containing the values to be set
- * in the new <code>AffineTransform</code> object. The length of the
- * array is assumed to be at least 4. If the length of the array is
- * less than 6, only the first 4 values are taken. If the length of
- * the array is greater than 6, the first 6 values are taken.
- */
- public AffineTransform(double[] flatmatrix) {
- m00 = flatmatrix[0];
- m10 = flatmatrix[1];
- m01 = flatmatrix[2];
- m11 = flatmatrix[3];
- if (flatmatrix.length > 5) {
- m02 = flatmatrix[4];
- m12 = flatmatrix[5];
- }
- updateState();
- }
- /**
- * Returns a transform representing a translation transformation.
- * The matrix representing the returned transform is:
- * <pre>
- * [ 1 0 tx ]
- * [ 0 1 ty ]
- * [ 0 0 1 ]
- * </pre>
- * @param tx the distance by which coordinates are translated in the
- * X axis direction
- * @param ty the distance by which coordinates are translated in the
- * Y axis direction
- * @return an <code>AffineTransform</code> object that represents a
- * translation transformation, created with the specified vector.
- */
- public static AffineTransform getTranslateInstance(double tx, double ty) {
- AffineTransform Tx = new AffineTransform();
- Tx.setToTranslation(tx, ty);
- return Tx;
- }
- /**
- * Returns a transform representing a rotation transformation.
- * The matrix representing the returned transform is:
- * <pre>
- * [ cos(theta) -sin(theta) 0 ]
- * [ sin(theta) cos(theta) 0 ]
- * [ 0 0 1 ]
- * </pre>
- * Rotating with a positive angle theta rotates points on the positive
- * x axis toward the positive y axis.
- * @param theta the angle of rotation in radians
- * @return an <code>AffineTransform</code> object that is a rotation
- * transformation, created with the specified angle of rotation.
- */
- public static AffineTransform getRotateInstance(double theta) {
- AffineTransform Tx = new AffineTransform();
- Tx.setToRotation(theta);
- return Tx;
- }
- /**
- * Returns a transform that rotates coordinates around an anchor point.
- * This operation is equivalent to translating the coordinates so
- * that the anchor point is at the origin (S1), then rotating them
- * about the new origin (S2), and finally translating so that the
- * intermediate origin is restored to the coordinates of the original
- * anchor point (S3).
- * <p>
- * This operation is equivalent to the following sequence of calls:
- * <pre>
- * AffineTransform Tx = new AffineTransform();
- * Tx.setToTranslation(x, y); // S3: final translation
- * Tx.rotate(theta); // S2: rotate around anchor
- * Tx.translate(-x, -y); // S1: translate anchor to origin
- * </pre>
- * The matrix representing the returned transform is:
- * <pre>
- * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
- * [ sin(theta) cos(theta) y-x*sin-y*cos ]
- * [ 0 0 1 ]
- * </pre>
- * Rotating with a positive angle theta rotates points on the positive
- * x axis toward the positive y axis.
- * @param theta the angle of rotation in radians
- * @param x, y the coordinates of the anchor point of the
- * rotation
- * @return an <code>AffineTransform</code> object that rotates
- * coordinates around the specified point by the specified angle of
- * rotation.
- */
- public static AffineTransform getRotateInstance(double theta,
- double x, double y) {
- AffineTransform Tx = new AffineTransform();
- Tx.setToRotation(theta, x, y);
- return Tx;
- }
- /**
- * Returns a transform representing a scaling transformation.
- * The matrix representing the returned transform is:
- * <pre>
- * [ sx 0 0 ]
- * [ 0 sy 0 ]
- * [ 0 0 1 ]
- * </pre>
- * @param sx the factor by which coordinates are scaled along the
- * X axis direction
- * @param sy the factor by which coordinates are scaled along the
- * Y axis direction
- * @return an <code>AffineTransform</code> object that scales
- * coordinates by the specified factors.
- */
- public static AffineTransform getScaleInstance(double sx, double sy) {
- AffineTransform Tx = new AffineTransform();
- Tx.setToScale(sx, sy);
- return Tx;
- }
- /**
- * Returns a transform representing a shearing transformation.
- * The matrix representing the returned transform is:
- * <pre>
- * [ 1 shx 0 ]
- * [ shy 1 0 ]
- * [ 0 0 1 ]
- * </pre>
- * @param shx the multiplier by which coordinates are shifted in the
- * direction of the positive X axis as a factor of their Y coordinate
- * @param shy the multiplier by which coordinates are shifted in the
- * direction of the positive Y axis as a factor of their X coordinate
- * @return an <code>AffineTransform</code> object that shears
- * coordinates by the specified multipliers.
- */
- public static AffineTransform getShearInstance(double shx, double shy) {
- AffineTransform Tx = new AffineTransform();
- Tx.setToShear(shx, shy);
- return Tx;
- }
- /**
- * Retrieves the flag bits describing the conversion properties of
- * this transform.
- * The return value is either one of the constants TYPE_IDENTITY
- * or TYPE_GENERAL_TRANSFORM, or a combination of the
- * appriopriate flag bits.
- * A valid combination of flag bits is an exclusive OR operation
- * that can combine
- * the TYPE_TRANSLATION flag bit
- * in addition to either of the
- * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits
- * as well as either of the
- * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits.
- * @return the OR combination of any of the indicated flags that
- * apply to this transform
- * @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() {
- int ret = TYPE_IDENTITY;
- boolean sgn0, sgn1;
- double M0, M1, M2, M3;
- switch (state) {
- default:
- stateError();
- /* NOTREACHED */
- case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- ret = TYPE_TRANSLATION;
- /* NOBREAK */
- case (APPLY_SHEAR | APPLY_SCALE):
- if ((M0 = m00) * (M2 = m01) + (M3 = m10) * (M1 = m11) != 0) {
- // Transformed unit vectors are not perpendicular...
- return TYPE_GENERAL_TRANSFORM;
- }
- sgn0 = (M0 >= 0.0);
- sgn1 = (M1 >= 0.0);
- if (sgn0 == sgn1) {
- // sgn(M0) == sgn(M1) therefore sgn(M2) == -sgn(M3)
- // This is the "unflipped" (right-handed) state
- if (M0 != M1 || M2 != -M3) {
- ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE);
- } else if (M0 * M1 - M2 * M3 != 1.0) {
- ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE);
- } else {
- ret |= TYPE_GENERAL_ROTATION;
- }
- } else {
- // sgn(M0) == -sgn(M1) therefore sgn(M2) == sgn(M3)
- // This is the "flipped" (left-handed) state
- if (M0 != -M1 || M2 != M3) {
- ret |= (TYPE_GENERAL_ROTATION |
- TYPE_FLIP |
- TYPE_GENERAL_SCALE);
- } else if (M0 * M1 - M2 * M3 != 1.0) {
- ret |= (TYPE_GENERAL_ROTATION |
- TYPE_FLIP |
- TYPE_UNIFORM_SCALE);
- } else {
- ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP);
- }
- }
- break;
- case (APPLY_SHEAR | APPLY_TRANSLATE):
- ret = TYPE_TRANSLATION;
- /* NOBREAK */
- case (APPLY_SHEAR):
- sgn0 = ((M0 = m01) >= 0.0);
- sgn1 = ((M1 = m10) >= 0.0);
- if (sgn0 != sgn1) {
- // Different signs - simple 90 degree rotation
- if (M0 == -M1) {
- ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
- } else {
- ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
- }
- } else {
- // Same signs - 90 degree rotation plus an axis flip too
- if (M0 == M1) {
- ret |= (TYPE_QUADRANT_ROTATION |
- TYPE_FLIP |
- TYPE_UNIFORM_SCALE);
- } else {
- ret |= (TYPE_QUADRANT_ROTATION |
- TYPE_FLIP |
- TYPE_GENERAL_SCALE);
- }
- }
- break;
- case (APPLY_SCALE | APPLY_TRANSLATE):
- ret = TYPE_TRANSLATION;
- /* NOBREAK */
- case (APPLY_SCALE):
- sgn0 = ((M0 = m00) >= 0.0);
- sgn1 = ((M1 = m11) >= 0.0);
- if (sgn0 == sgn1) {
- if (sgn0) {
- // Both scaling factors non-negative - simple scale
- if (M0 == M1) {
- ret |= TYPE_UNIFORM_SCALE;
- } else {
- ret |= TYPE_GENERAL_SCALE;
- }
- } else {
- // Both scaling factors negative - 180 degree rotation
- if (M0 == M1) {
- ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
- } else {
- ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
- }
- }
- } else {
- // Scaling factor signs different - flip about some axis
- if (M0 == -M1) {
- if (M0 == 1.0 || M0 == -1.0) {
- ret |= TYPE_FLIP;
- } else {
- ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE);
- }
- } else {
- ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE);
- }
- }
- break;
- case (APPLY_TRANSLATE):
- ret = TYPE_TRANSLATION;
- break;
- case (APPLY_IDENTITY):
- break;
- }
- return ret;
- }
- /**
- * Returns the determinant of the matrix representation of the transform.
- * The determinant is useful both to determine if the transform can
- * be inverted and to get a single value representing the
- * combined X and Y scaling of the transform.
- * <p>
- * If the determinant is non-zero, then this transform is
- * invertible and the various methods that depend on the inverse
- * transform do not need to throw a
- * {@link NoninvertibleTransformException}.
- * If the determinant is zero then this transform can not be
- * inverted since the transform maps all input coordinates onto
- * a line or a point.
- * If the determinant is near enough to zero then inverse transform
- * operations might not carry enough precision to produce meaningful
- * results.
- * <p>
- * If this transform represents a uniform scale, as indicated by
- * the <code>getType</code> method then the determinant also
- * represents the square of the uniform scale factor by which all of
- * the points are expanded from or contracted towards the origin.
- * If this transform represents a non-uniform scale or more general
- * transform then the determinant is not likely to represent a
- * value useful for any purpose other than determining if inverse
- * transforms are possible.
- * <p>
- * Mathematically, the determinant is calculated using the formula:
- * <pre>
- * | m00 m01 m02 |
- * | m10 m11 m12 | = m00 * m11 - m01 * m10
- * | 0 0 1 |
- * </pre>
- *
- * @return the determinant of the matrix used to transform the
- * coordinates.
- * @see #getType
- * @see #createInverse
- * @see #inverseTransform
- * @see #TYPE_UNIFORM_SCALE
- */
- public double getDeterminant() {
- switch (state) {
- default:
- stateError();
- /* NOTREACHED */
- case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (APPLY_SHEAR | APPLY_SCALE):
- return m00 * m11 - m01 * m10;
- case (APPLY_SHEAR | APPLY_TRANSLATE):
- case (APPLY_SHEAR):
- return -(m01 * m10);
- case (APPLY_SCALE | APPLY_TRANSLATE):
- case (APPLY_SCALE):
- return m00 * m11;
- case (APPLY_TRANSLATE):
- case (APPLY_IDENTITY):
- return 1.0;
- }
- }
- /**
- * Manually recalculates the state of the transform when the matrix
- * changes too much to predict the effects on the state.
- */
- void updateState() {
- if (m01 == 0.0 && m10 == 0.0) {
- if (m00 == 1.0 && m11 == 1.0) {
- if (m02 == 0.0 && m12 == 0.0) {
- state = APPLY_IDENTITY;
- } else {
- state = APPLY_TRANSLATE;
- }
- } else {
- if (m02 == 0.0 && m12 == 0.0) {
- state = APPLY_SCALE;
- } else {
- state = (APPLY_SCALE | APPLY_TRANSLATE);
- }
- }
- } else {
- if (m00 == 0.0 && m11 == 0.0) {
- if (m02 == 0.0 && m12 == 0.0) {
- state = APPLY_SHEAR;
- } else {
- state = (APPLY_SHEAR | APPLY_TRANSLATE);
- }
- } else {
- if (m02 == 0.0 && m12 == 0.0) {
- state = (APPLY_SHEAR | APPLY_SCALE);
- } else {
- state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);
- }
- }
- }
- }
- /*
- * Convenience method used internally to throw exceptions when
- * a case was forgotten in a switch statement.
- */
- private void stateError() {
- throw new InternalError("missing case in transform state switch");
- }
- /**
- * Retrieves the 6 specifiable values in the 3x3 affine transformation
- * matrix and places them into an array of double precisions values.
- * The values are stored in the array as
- * { m00 m10 m01 m11 m02 m12 }.
- * An array of 4 doubles can also be specified, in which case only the
- * first four elements representing the non-transform
- * parts of the array are retrieved and the values are stored into
- * the array as { m00 m10 m01 m11 }
- * @param flatmatrix the double array used to store the returned
- * values.
- * @see #getScaleX
- * @see #getScaleY
- * @see #getShearX
- * @see #getShearY
- * @see #getTranslateX
- * @see #getTranslateY
- */
- public void getMatrix(double[] flatmatrix) {
- flatmatrix[0] = m00;
- flatmatrix[1] = m10;
- flatmatrix[2] = m01;
- flatmatrix[3] = m11;
- if (flatmatrix.length > 5) {
- flatmatrix[4] = m02;
- flatmatrix[5] = m12;
- }
- }
- /**
- * Returns the X coordinate scaling element (m00) of the 3x3
- * affine transformation matrix.
- * @return a double value that is the X coordinate of the scaling
- * element of the affine transformation matrix.
- * @see #getMatrix
- */
- public double getScaleX() {
- return m00;
- }
- /**
- * Returns the Y coordinate scaling element (m11) of the 3x3
- * affine transformation matrix.
- * @return a double value that is the Y coordinate of the scaling
- * element of the affine transformation matrix.
- * @see #getMatrix
- */
- public double getScaleY() {
- return m11;
- }
- /**
- * Returns the X coordinate shearing element (m01) of the 3x3
- * affine transformation matrix.
- * @return a double value that is the X coordinate of the shearing
- * element of the affine transformation matrix.
- * @see #getMatrix
- */
- public double getShearX() {
- return m01;
- }
- /**
- * Returns the Y coordinate shearing element (m10) of the 3x3
- * affine transformation matrix.
- * @return a double value that is the Y coordinate of the shearing
- * element of the affine transformation matrix.
- * @see #getMatrix
- */
- public double getShearY() {
- return m10;
- }
- /**
- * Returns the X coordinate of the translation element (m02) of the
- * 3x3 affine transformation matrix.
- * @return a double value that is the X coordinate of the translation
- * element of the affine transformation matrix.
- * @see #getMatrix
- */
- public double getTranslateX() {
- return m02;
- }
- /**
- * Returns the Y coordinate of the translation element (m12) of the
- * 3x3 affine transformation matrix.
- * @return a double value that is the Y coordinate of the translation
- * element of the affine transformation matrix.
- * @see #getMatrix
- */
- public double getTranslateY() {
- return m12;
- }
- /**
- * Concatenates this transform with a translation transformation.
- * This is equivalent to calling concatenate(T), where T is an
- * <code>AffineTransform</code> represented by the following matrix:
- * <pre>
- * [ 1 0 tx ]
- * [ 0 1 ty ]
- * [ 0 0 1 ]
- * </pre>
- * @param tx the distance by which coordinates are translated in the
- * X axis direction
- * @param ty the distance by which coordinates are translated in the
- * Y axis direction
- */
- public void translate(double tx, double ty) {
- switch (state) {
- default:
- stateError();
- /* NOTREACHED */
- case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- m02 = tx * m00 + ty * m01 + m02;
- m12 = tx * m10 + ty * m11 + m12;
- return;
- case (APPLY_SHEAR | APPLY_SCALE):
- m02 = tx * m00 + ty * m01;
- m12 = tx * m10 + ty * m11;
- state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
- return;
- case (APPLY_SHEAR | APPLY_TRANSLATE):
- m02 = ty * m01 + m02;
- m12 = tx * m10 + m12;
- return;
- case (APPLY_SHEAR):
- m02 = ty * m01;
- m12 = tx * m10;
- state = APPLY_SHEAR | APPLY_TRANSLATE;
- return;
- case (APPLY_SCALE | APPLY_TRANSLATE):
- m02 = tx * m00 + m02;
- m12 = ty * m11 + m12;
- return;
- case (APPLY_SCALE):
- m02 = tx * m00;
- m12 = ty * m11;
- state = APPLY_SCALE | APPLY_TRANSLATE;
- return;
- case (APPLY_TRANSLATE):
- m02 = tx + m02;
- m12 = ty + m12;
- return;
- case (APPLY_IDENTITY):
- m02 = tx;
- m12 = ty;
- state = APPLY_TRANSLATE;
- return;
- }
- }
- /**
- * Concatenates this transform with a rotation transformation.
- * This is equivalent to calling concatenate(R), where R is an
- * <code>AffineTransform</code> represented by the following matrix:
- * <pre>
- * [ cos(theta) -sin(theta) 0 ]
- * [ sin(theta) cos(theta) 0 ]
- * [ 0 0 1 ]
- * </pre>
- * Rotating with a positive angle theta rotates points on the positive
- * x axis toward the positive y axis.
- * @param theta the angle of rotation in radians
- */
- public void rotate(double theta) {
- double sin = Math.sin(theta);
- double cos = Math.cos(theta);
- if (Math.abs(sin) < 1E-15) {
- if (cos < 0.0) {
- m00 = -m00;
- m11 = -m11;
- int state = this.state;
- if ((state & (APPLY_SHEAR)) != 0) {
- // If there was a shear, then this rotation has no
- // effect on the state.
- m01 = -m01;
- m10 = -m10;
- } else {
- // No shear means the SCALE state may toggle when
- // m00 and m11 are negated.
- if (m00 == 1.0 && m11 == 1.0) {
- this.state = state & ~APPLY_SCALE;
- } else {
- this.state = state | APPLY_SCALE;
- }
- }
- }
- return;
- }
- if (Math.abs(cos) < 1E-15) {
- if (sin < 0.0) {
- double M0 = m00;
- m00 = -m01;
- m01 = M0;
- M0 = m10;
- m10 = -m11;
- m11 = M0;
- } else {
- double M0 = m00;
- m00 = m01;
- m01 = -M0;
- M0 = m10;
- m10 = m11;
- m11 = -M0;
- }
- state = rot90conversion[state];
- return;
- }
- double M0, M1;
- M0 = m00;
- M1 = m01;
- m00 = cos * M0 + sin * M1;
- m01 = -sin * M0 + cos * M1;
- M0 = m10;
- M1 = m11;
- m10 = cos * M0 + sin * M1;
- m11 = -sin * M0 + cos * M1;
- updateState();
- }
- private static int rot90conversion[] = {
- APPLY_SHEAR, APPLY_SHEAR | APPLY_TRANSLATE,
- APPLY_SHEAR, APPLY_SHEAR | APPLY_TRANSLATE,
- APPLY_SCALE, APPLY_SCALE | APPLY_TRANSLATE,
- APPLY_SHEAR | APPLY_SCALE,
- APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE,
- };
- /**
- * Concatenates this transform with a transform that rotates
- * coordinates around an anchor point.
- * This operation is equivalent to translating the coordinates so
- * that the anchor point is at the origin (S1), then rotating them
- * about the new origin (S2), and finally translating so that the
- * intermediate origin is restored to the coordinates of the original
- * anchor point (S3).
- * <p>
- * This operation is equivalent to the following sequence of calls:
- * <pre>
- * translate(x, y); // S3: final translation
- * rotate(theta); // S2: rotate around anchor
- * translate(-x, -y); // S1: translate anchor to origin
- * </pre>
- * Rotating with a positive angle theta rotates points on the positive
- * x axis toward the positive y axis.
- * @param theta the angle of rotation in radians
- * @param x, y the coordinates of the anchor point of the
- * rotation
- */
- public void rotate(double theta, double x, double y) {
- // REMIND: Simple for now - optimize later
- translate(x, y);
- rotate(theta);
- translate(-x, -y);
- }
- /**
- * Concatenates this transform with a scaling transformation.
- * This is equivalent to calling concatenate(S), where S is an
- * <code>AffineTransform</code> represented by the following matrix:
- * <pre>
- * [ sx 0 0 ]
- * [ 0 sy 0 ]
- * [ 0 0 1 ]
- * </pre>
- * @param sx the factor by which coordinates are scaled along the
- * X axis direction
- * @param sy the factor by which coordinates are scaled along the
- * Y axis direction
- */
- public void scale(double sx, double sy) {
- int state = this.state;
- switch (state) {
- default:
- stateError();
- /* NOTREACHED */
- case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (APPLY_SHEAR | APPLY_SCALE):
- m00 *= sx;
- m11 *= sy;
- /* NOBREAK */
- case (APPLY_SHEAR | APPLY_TRANSLATE):
- case (APPLY_SHEAR):
- m01 *= sy;
- m10 *= sx;
- return;
- case (APPLY_SCALE | APPLY_TRANSLATE):
- case (APPLY_SCALE):
- m00 *= sx;
- m11 *= sy;
- return;
- case (APPLY_TRANSLATE):
- case (APPLY_IDENTITY):
- m00 = sx;
- m11 = sy;
- this.state = state | APPLY_SCALE;
- return;
- }
- }
- /**
- * Concatenates this transform with a shearing transformation.
- * This is equivalent to calling concatenate(SH), where SH is an
- * <code>AffineTransform</code> represented by the following matrix:
- * <pre>
- * [ 1 shx 0 ]
- * [ shy 1 0 ]
- * [ 0 0 1 ]
- * </pre>
- * @param shx the multiplier by which coordinates are shifted in the
- * direction of the positive X axis as a factor of their Y coordinate
- * @param shy the multiplier by which coordinates are shifted in the
- * direction of the positive Y axis as a factor of their X coordinate
- */
- public void shear(double shx, double shy) {
- int state = this.state;
- switch (state) {
- default:
- stateError();
- /* NOTREACHED */
- case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (APPLY_SHEAR | APPLY_SCALE):
- double M0, M1;
- M0 = m00;
- M1 = m01;
- m00 = M0 + M1 * shy;
- m01 = M0 * shx + M1;
- M0 = m10;
- M1 = m11;
- m10 = M0 + M1 * shy;
- m11 = M0 * shx + M1;
- return;
- case (APPLY_SHEAR | APPLY_TRANSLATE):
- case (APPLY_SHEAR):
- m00 = m01 * shy;
- m11 = m10 * shx;
- this.state = state | APPLY_SCALE;
- return;
- case (APPLY_SCALE | APPLY_TRANSLATE):
- case (APPLY_SCALE):
- m01 = m00 * shx;
- m10 = m11 * shy;
- this.state = state | APPLY_SHEAR;
- return;
- case (APPLY_TRANSLATE):
- case (APPLY_IDENTITY):
- m01 = shx;
- m10 = shy;
- this.state = state | APPLY_SCALE | APPLY_SHEAR;
- return;
- }
- }
- /**
- * Resets this transform to the Identity transform.
- */
- public void setToIdentity() {
- m00 = m11 = 1.0;
- m10 = m01 = m02 = m12 = 0.0;
- state = APPLY_IDENTITY;
- }
- /**
- * Sets this transform to a translation transformation.
- * The matrix representing this transform becomes:
- * <pre>
- * [ 1 0 tx ]
- * [ 0 1 ty ]
- * [ 0 0 1 ]
- * </pre>
- * @param tx the distance by which coordinates are translated in the
- * X axis direction
- * @param ty the distance by which coordinates are translated in the
- * Y axis direction
- */
- public void setToTranslation(double tx, double ty) {
- m00 = 1.0;
- m10 = 0.0;
- m01 = 0.0;
- m11 = 1.0;
- m02 = tx;
- m12 = ty;
- state = APPLY_TRANSLATE;
- }
- /**
- * Sets this transform to a rotation transformation.
- * The matrix representing this transform becomes:
- * <pre>
- * [ cos(theta) -sin(theta) 0 ]
- * [ sin(theta) cos(theta) 0 ]
- * [ 0 0 1 ]
- * </pre>
- * Rotating with a positive angle theta rotates points on the positive
- * x axis toward the positive y axis.
- * @param theta the angle of rotation in radians
- */
- public void setToRotation(double theta) {
- m02 = 0.0;
- m12 = 0.0;
- double sin = Math.sin(theta);
- double cos = Math.cos(theta);
- if (Math.abs(sin) < 1E-15) {
- m01 = m10 = 0.0;
- if (cos < 0) {
- m00 = m11 = -1.0;
- state = APPLY_SCALE;
- } else {
- m00 = m11 = 1.0;
- state = APPLY_IDENTITY;
- }
- return;
- }
- if (Math.abs(cos) < 1E-15) {
- m00 = m11 = 0.0;
- if (sin < 0.0) {
- m01 = 1.0;
- m10 = -1.0;
- } else {
- m01 = -1.0;
- m10 = 1.0;
- }
- state = APPLY_SHEAR;
- return;
- }
- m00 = cos;
- m01 = -sin;
- m10 = sin;
- m11 = cos;
- state = APPLY_SHEAR | APPLY_SCALE;
- return;
- }
- /**
- * Sets this transform to a translated rotation transformation.
- * This operation is equivalent to translating the coordinates so
- * that the anchor point is at the origin (S1), then rotating them
- * about the new origin (S2), and finally translating so that the
- * intermediate origin is restored to the coordinates of the original
- * anchor point (S3).
- * <p>
- * This operation is equivalent to the following sequence of calls:
- * <pre>
- * setToTranslation(x, y); // S3: final translation
- * rotate(theta); // S2: rotate around anchor
- * translate(-x, -y); // S1: translate anchor to origin
- * </pre>
- * The matrix representing this transform becomes:
- * <pre>
- * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
- * [ sin(theta) cos(theta) y-x*sin-y*cos ]
- * [ 0 0 1 ]
- * </pre>
- * Rotating with a positive angle theta rotates points on the positive
- * x axis toward the positive y axis.
- * @param theta the angle of rotation in radians
- * @param x, y the coordinates of the anchor point of the
- * rotation
- */
- public void setToRotation(double theta, double x, double y) {
- setToRotation(theta);
- double sin = m10;
- double oneMinusCos = 1.0 - m00;
- m02 = x * oneMinusCos + y * sin;
- m12 = y * oneMinusCos - x * sin;
- state |= APPLY_TRANSLATE;
- return;
- }
- /**
- * Sets this transform to a scaling transformation.
- * The matrix representing this transform becomes:
- * <pre>
- * [ sx 0 0 ]
- * [ 0 sy 0 ]
- * [ 0 0 1 ]
- * </pre>
- * @param sx the factor by which coordinates are scaled along the
- * X axis direction
- * @param sy the factor by which coordinates are scaled along the
- * Y axis direction
- */
- public void setToScale(double sx, double sy) {
- m00 = sx;
- m10 = 0.0;
- m01 = 0.0;
- m11 = sy;
- m02 = 0.0;
- m12 = 0.0;
- state = APPLY_SCALE;
- }
- /**
- * Sets this transform to a shearing transformation.
- * The matrix representing this transform becomes:
- * <pre>
- * [ 1 shx 0 ]
- * [ shy 1 0 ]
- * [ 0 0 1 ]
- * </pre>
- * @param shx the multiplier by which coordinates are shifted in the
- * direction of the positive X axis as a factor of their Y coordinate
- * @param shy the multiplier by which coordinates are shifted in the
- * direction of the positive Y axis as a factor of their X coordinate
- */
- public void setToShear(double shx, double shy) {
- m00 = 1.0;
- m01 = shx;
- m10 = shy;
- m11 = 1.0;
- m02 = 0.0;
- m12 = 0.0;
- state = APPLY_SHEAR | APPLY_SCALE;
- }
- /**
- * Sets this transform to a copy of the transform in the specified
- * <code>AffineTransform</code> object.
- * @param Tx the <code>AffineTransform</code> object from which to
- * copy the transform
- */
- public void setTransform(AffineTransform Tx) {
- this.m00 = Tx.m00;
- this.m10 = Tx.m10;
- this.m01 = Tx.m01;
- this.m11 = Tx.m11;
- this.m02 = Tx.m02;
- this.m12 = Tx.m12;
- this.state = Tx.state;
- }
- /**
- * Sets this transform to the matrix specified by the 6
- * double precision values.
- * @param m00, m01, m02, m10, m11, m12 the
- * 6 floating point values that compose the 3x3 transformation matrix
- */
- public void setTransform(double m00, double m10,
- double m01, double m11,
- double m02, double m12) {
- this.m00 = m00;
- this.m10 = m10;
- this.m01 = m01;
- this.m11 = m11;
- this.m02 = m02;
- this.m12 = m12;
- updateState();
- }
- /**
- * Concatenates an <code>AffineTransform</code> <code>Tx</code> to
- * this <code>AffineTransform</code> Cx in the most commonly useful
- * way to provide a new user space
- * that is mapped to the former user space by <code>Tx</code>.
- * Cx is updated to perform the combined transformation.
- * Transforming a point p by the updated transform Cx' is
- * equivalent to first transforming p by <code>Tx</code> and then
- * transforming the result by the original transform Cx like this:
- * Cx'(p) = Cx(Tx(p))
- * In matrix notation, if this transform Cx is
- * represented by the matrix [this] and <code>Tx</code> is represented
- * by the matrix [Tx] then this method does the following:
- * <pre>
- * [this] = [this] x [Tx]
- * </pre>
- * @param Tx the <code>AffineTransform</code> object to be
- * concatenated with this <code>AffineTransform</code> object.
- * @see #preConcatenate
- */
- public void concatenate(AffineTransform Tx) {
- double M0, M1;
- double T00, T01, T10, T11;
- double T02, T12;
- int mystate = state;
- int txstate = Tx.state;
- switch ((txstate << HI_SHIFT) | mystate) {
- /* ---------- Tx == IDENTITY cases ---------- */
- case (HI_IDENTITY | APPLY_IDENTITY):
- case (HI_IDENTITY | APPLY_TRANSLATE):
- case (HI_IDENTITY | APPLY_SCALE):
- case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_IDENTITY | APPLY_SHEAR):
- case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
- case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
- case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- return;
- /* ---------- this == IDENTITY cases ---------- */
- case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
- m01 = Tx.m01;
- m10 = Tx.m10;
- /* NOBREAK */
- case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
- m00 = Tx.m00;
- m11 = Tx.m11;
- /* NOBREAK */
- case (HI_TRANSLATE | APPLY_IDENTITY):
- m02 = Tx.m02;
- m12 = Tx.m12;
- state = txstate;
- return;
- case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY):
- m01 = Tx.m01;
- m10 = Tx.m10;
- /* NOBREAK */
- case (HI_SCALE | APPLY_IDENTITY):
- m00 = Tx.m00;
- m11 = Tx.m11;
- state = txstate;
- return;
- case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY):
- m02 = Tx.m02;
- m12 = Tx.m12;
- /* NOBREAK */
- case (HI_SHEAR | APPLY_IDENTITY):
- m01 = Tx.m01;
- m10 = Tx.m10;
- m00 = m11 = 0.0;
- state = txstate;
- return;
- /* ---------- Tx == TRANSLATE cases ---------- */
- case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- T02 = Tx.m02;
- T12 = Tx.m12;
- m02 = T02 * m00 + T12 * m01 + m02;
- m12 = T02 * m10 + T12 * m11 + m12;
- return;
- case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
- T02 = Tx.m02;
- T12 = Tx.m12;
- m02 = T02 * m00 + T12 * m01;
- m12 = T02 * m10 + T12 * m11;
- state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
- return;
- case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
- m02 = Tx.m12 * m01 + m02;
- m12 = Tx.m02 * m10 + m12;
- return;
- case (HI_TRANSLATE | APPLY_SHEAR):
- m02 = Tx.m12 * m01;
- m12 = Tx.m02 * m10;
- state = APPLY_SHEAR | APPLY_TRANSLATE;
- return;
- case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
- m02 = Tx.m02 * m00 + m02;
- m12 = Tx.m12 * m11 + m12;
- return;
- case (HI_TRANSLATE | APPLY_SCALE):
- m02 = Tx.m02 * m00;
- m12 = Tx.m12 * m11;
- state = APPLY_SCALE | APPLY_TRANSLATE;
- return;
- case (HI_TRANSLATE | APPLY_TRANSLATE):
- m02 = Tx.m02 + m02;
- m12 = Tx.m12 + m12;
- return;
- /* ---------- Tx == SCALE cases ---------- */
- case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
- T00 = Tx.m00; T11 = Tx.m11;
- m00 *= T00;
- m11 *= T11;
- m01 *= T11;
- m10 *= T00;
- return;
- case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
- case (HI_SCALE | APPLY_SHEAR):
- m01 *= Tx.m11;
- m10 *= Tx.m00;
- return;
- case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_SCALE | APPLY_SCALE):
- m00 *= Tx.m00;
- m11 *= Tx.m11;
- return;
- case (HI_SCALE | APPLY_TRANSLATE):
- m00 = Tx.m00;
- m11 = Tx.m11;
- state = APPLY_SCALE | APPLY_TRANSLATE;
- return;
- /* ---------- Tx == SHEAR cases ---------- */
- case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
- T01 = Tx.m01; T10 = Tx.m10;
- M0 = m00;
- m00 = m01 * T10;
- m01 = M0 * T01;
- M0 = m10;
- m10 = m11 * T10;
- m11 = M0 * T01;
- return;
- case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
- case (HI_SHEAR | APPLY_SHEAR):
- m00 = m01 * Tx.m10;
- m01 = 0.0;
- m11 = m10 * Tx.m01;
- m10 = 0.0;
- state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
- return;
- case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_SHEAR | APPLY_SCALE):
- m01 = m00 * Tx.m01;
- m00 = 0.0;
- m10 = m11 * Tx.m10;
- m11 = 0.0;
- state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
- return;
- case (HI_SHEAR | APPLY_TRANSLATE):
- m00 = 0.0;
- m01 = Tx.m01;
- m10 = Tx.m10;
- m11 = 0.0;
- state = APPLY_TRANSLATE | APPLY_SHEAR;
- return;
- }
- // If Tx has more than one attribute, it is not worth optimizing
- // all of those cases...
- T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;
- T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;
- switch (mystate) {
- default:
- stateError();
- /* NOTREACHED */
- case (APPLY_SHEAR | APPLY_SCALE):
- state = mystate | txstate;
- case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- M0 = m00;
- M1 = m01;
- m00 = T00 * M0 + T10 * M1;
- m01 = T01 * M0 + T11 * M1;
- m02 += T02 * M0 + T12 * M1;
- M0 = m10;
- M1 = m11;
- m10 = T00 * M0 + T10 * M1;
- m11 = T01 * M0 + T11 * M1;
- m12 += T02 * M0 + T12 * M1;
- return;
- case (APPLY_SHEAR | APPLY_TRANSLATE):
- case (APPLY_SHEAR):
- M0 = m01;
- m00 = T10 * M0;
- m01 = T11 * M0;
- m02 += T12 * M0;
- M0 = m10;
- m10 = T00 * M0;
- m11 = T01 * M0;
- m12 += T02 * M0;
- break;
- case (APPLY_SCALE | APPLY_TRANSLATE):
- case (APPLY_SCALE):
- M0 = m00;
- m00 = T00 * M0;
- m01 = T01 * M0;
- m02 += T02 * M0;
- M0 = m11;
- m10 = T10 * M0;
- m11 = T11 * M0;
- m12 += T12 * M0;
- break;
- case (APPLY_TRANSLATE):
- m00 = T00;
- m01 = T01;
- m02 += T02;
- m10 = T10;
- m11 = T11;
- m12 += T12;
- state = txstate | APPLY_TRANSLATE;
- return;
- }
- updateState();
- }
- /**
- * Concatenates an <code>AffineTransform</code> <code>Tx</code> to
- * this <code>AffineTransform</code> Cx
- * in a less commonly used way such that <code>Tx</code> modifies the
- * coordinate transformation relative to the absolute pixel
- * space rather than relative to the existing user space.
- * Cx is updated to perform the combined transformation.
- * Transforming a point p by the updated transform Cx' is
- * equivalent to first transforming p by the original transform
- * Cx and then transforming the result by
- * <code>Tx</code> like this:
- * Cx'(p) = Tx(Cx(p))
- * In matrix notation, if this transform Cx
- * is represented by the matrix [this] and <code>Tx</code> is
- * represented by the matrix [Tx] then this method does the
- * following:
- * <pre>
- * [this] = [Tx] x [this]
- * </pre>
- * @param Tx the <code>AffineTransform</code> object to be
- * concatenated with this <code>AffineTransform</code> object.
- * @see #concatenate
- */
- public void preConcatenate(AffineTransform Tx) {
- double M0, M1;
- double T00, T01, T10, T11;
- double T02, T12;
- int mystate = state;
- int txstate = Tx.state;
- switch ((txstate << HI_SHIFT) | mystate) {
- case (HI_IDENTITY | APPLY_IDENTITY):
- case (HI_IDENTITY | APPLY_TRANSLATE):
- case (HI_IDENTITY | APPLY_SCALE):
- case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_IDENTITY | APPLY_SHEAR):
- case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
- case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
- case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- // Tx is IDENTITY...
- return;
- case (HI_TRANSLATE | APPLY_IDENTITY):
- case (HI_TRANSLATE | APPLY_SCALE):
- case (HI_TRANSLATE | APPLY_SHEAR):
- case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
- // Tx is TRANSLATE, this has no TRANSLATE
- m02 = Tx.m02;
- m12 = Tx.m12;
- state = mystate | APPLY_TRANSLATE;
- return;
- case (HI_TRANSLATE | APPLY_TRANSLATE):
- case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
- case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- // Tx is TRANSLATE, this has one too
- m02 = m02 + Tx.m02;
- m12 = m12 + Tx.m12;
- return;
- case (HI_SCALE | APPLY_TRANSLATE):
- case (HI_SCALE | APPLY_IDENTITY):
- // Only these two existing states need a new state
- state = mystate | APPLY_SCALE;
- /* NOBREAK */
- case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
- case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
- case (HI_SCALE | APPLY_SHEAR):
- case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_SCALE | APPLY_SCALE):
- // Tx is SCALE, this is anything
- T00 = Tx.m00;
- T11 = Tx.m11;
- if ((mystate & APPLY_SHEAR) != 0) {
- m01 = m01 * T00;
- m10 = m10 * T11;
- if ((mystate & APPLY_SCALE) != 0) {
- m00 = m00 * T00;
- m11 = m11 * T11;
- }
- } else {
- m00 = m00 * T00;
- m11 = m11 * T11;
- }
- if ((mystate & APPLY_TRANSLATE) != 0) {
- m02 = m02 * T00;
- m12 = m12 * T11;
- }
- return;
- case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
- case (HI_SHEAR | APPLY_SHEAR):
- mystate = mystate | APPLY_SCALE;
- /* NOBREAK */
- case (HI_SHEAR | APPLY_TRANSLATE):
- case (HI_SHEAR | APPLY_IDENTITY):
- case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_SHEAR | APPLY_SCALE):
- state = mystate ^ APPLY_SHEAR;
- /* NOBREAK */
- case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
- // Tx is SHEAR, this is anything
- T01 = Tx.m01;
- T10 = Tx.m10;
- M0 = m00;
- m00 = m10 * T01;
- m10 = M0 * T10;
- M0 = m01;
- m01 = m11 * T01;
- m11 = M0 * T10;
- M0 = m02;
- m02 = m12 * T01;
- m12 = M0 * T10;
- return;
- }
- // If Tx has more than one attribute, it is not worth optimizing
- // all of those cases...
- T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;
- T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;
- switch (mystate) {
- default:
- stateError();
- /* NOTREACHED */
- case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- M0 = m02;
- M1 = m12;
- T02 += M0 * T00 + M1 * T01;
- T12 += M0 * T10 + M1 * T11;
- /* NOBREAK */
- case (APPLY_SHEAR | APPLY_SCALE):
- m02 = T02;
- m12 = T12;
- M0 = m00;
- M1 = m10;
- m00 = M0 * T00 + M1 * T01;
- m10 = M0 * T10 + M1 * T11;
- M0 = m01;
- M1 = m11;
- m01 = M0 * T00 + M1 * T01;
- m11 = M0 * T10 + M1 * T11;
- break;
- case (APPLY_SHEAR | APPLY_TRANSLATE):
- M0 = m02;
- M1 = m12;
- T02 += M0 * T00 + M1 * T01;
- T12 += M0 * T10 + M1 * T11;
- /* NOBREAK */
- case (APPLY_SHEAR):
- m02 = T02;
- m12 = T12;
- M0 = m10;
- m00 = M0 * T01;
- m10 = M0 * T11;
- M0 = m01;
- m01 = M0 * T00;
- m11 = M0 * T10;
- break;
- case (APPLY_SCALE | APPLY_TRANSLATE):
- M0 = m02;
- M1 = m12;
- T02 += M0 * T00 + M1 * T01;
- T12 += M0 * T10 + M1 * T11;
- /* NOBREAK */
- case (APPLY_SCALE):
- m02 = T02;
- m12 = T12;
- M0 = m00;
- m00 = M0 * T00;
- m10 = M0 * T10;
- M0 = m11;
- m01 = M0 * T01;
- m11 = M0 * T11;
- break;
- case (APPLY_TRANSLATE):
- M0 = m02;
- M1 = m12;
- T02 += M0 * T00 + M1 * T01;
- T12 += M0 * T10 + M1 * T11;
- /* NOBREAK */
- case (APPLY_IDENTITY):
- m02 = T02;
- m12 = T12;
- m00 = T00;
- m10 = T10;
- m01 = T01;
- m11 = T11;
- state = mystate | txstate;
- return;
- }
- updateState();
- }
- /**
- * Returns an <code>AffineTransform</code> object representing the
- * inverse transformation.
- * The inverse transform Tx' of this transform Tx
- * maps coordinates transformed by Tx back
- * to their original coordinates.
- * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
- * <p>
- * If this transform maps all coordinates onto a point or a line
- * then it will not have an inverse, since coordinates that do
- * not lie on the destination point or line will not have an inverse
- * mapping.
- * The <code>getDeterminant</code> method can be used to determine if this
- * transform has no inverse, in which case an exception will be
- * thrown if the <code>createInverse</code> method is called.
- * @return a new <code>AffineTransform</code> object representing the
- * inverse transformation.
- * @see #getDeterminant
- * @exception NoninvertibleTransformException
- * if the matrix cannot be inverted.
- */
- public AffineTransform createInverse()
- throws NoninvertibleTransformException
- {
- double det;
- switch (state) {
- default:
- stateError();
- /* NOTREACHED */
- case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
- det = m00 * m11 - m01 * m10;
- if (Math.abs(det) <= Double.MIN_VALUE) {
- throw new NoninvertibleTransformException("Determinant is "+
- det);
- }
- return new AffineTransform( m11 / det, -m10 / det,
- -m01 / det, m00 / det,
- (m01 * m12 - m11 * m02) / det,
- (m10 * m02 - m00 * m12) / det,
- (APPLY_SHEAR |
- APPLY_SCALE |
- APPLY_TRANSLATE));
- case (APPLY_SHEAR | APPLY_SCALE):
- det = m00 * m11 - m01 * m10;
- if (Math.abs(det) <= Double.MIN_VALUE) {
- throw new NoninvertibleTransformException("Determinant is "+
- det);
- }
- return new AffineTransform( m11 / det, -m10 / det,
- -m01 / det, m00 / det,
- 0.0, 0.0,
- (APPLY_SHEAR | APPLY_SCALE));
- case (APPLY_SHEAR | APPLY_TRANSLATE):
- if (m01 == 0.0 || m10 == 0.0) {
- throw new NoninvertibleTransformException("Determinant is 0");
- }
- return new AffineTransform( 0.0, 1.0 / m01,
- 1.0 / m10, 0.0,
- -m12 / m10, -m02 / m01,
- (APPLY_SHEAR | APPLY_TRANSLATE));
- case (APPLY_SHEAR):
- if (m01 == 0.0 || m10 == 0.0) {
- throw new NoninvertibleTransformException("Determinant is 0");
- }
- return new AffineTransform(0.0, 1.0 / m01,
- 1.0 / m10, 0.0,
- 0.0, 0.0,
- (APPLY_SHEAR));
- case (APPLY_SCALE | APPLY_TRANSLATE):
- if (m00 == 0.0 || m11 == 0.0) {
- throw new NoninvertibleTransformException("Determinant is 0");
- }
- return new AffineTransform( 1.0 / m00, 0.0,
- 0.0, 1.0 / m11,
- -m02 / m00, -m12 / m11,
- (APPLY_SCALE | APPLY_TRANSLATE));
- case (APPLY_SCALE):
- if (m00 == 0.0 || m11 == 0.0) {
- throw new NoninvertibleTransformException("Determinant is 0");
- }
- return new AffineTransform(1.0 / m00, 0.0,
- 0.0, 1.0 / m11,
- 0.0, 0.0,
- (APPLY_SCALE));
- case (APPLY_TRANSLATE):
- return new AffineTransform( 1.0, 0.0,
- 0.0, 1.0,
- -m02, -m12,
- (APPLY_TRANSLATE));
- case (APPLY_IDENTITY):
- return new AffineTransform();
- }
- /* NOTREACHED */
- }
- /**
- * Transforms the specified <code>ptSrc</code> and stores the result
- * in <code>ptDst</code>.
- * If <code>ptDst</code> is <code>null</code>, a new {@link Point2D}
- * object is allocated and then the result of the transformation is
- * stored in this object.
- * In either case, <code>ptDst</code>, which contains the
- * transformed point, is returned for convenience.
- * If <code>ptSrc</code> and <code>ptDst</code> are the