0N/A/*
2965N/A * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.awt.geom;
0N/A
0N/Aimport java.awt.Shape;
2965N/Aimport java.beans.ConstructorProperties;
0N/A
0N/A/**
0N/A * The <code>AffineTransform</code> class represents a 2D affine transform
0N/A * that performs a linear mapping from 2D coordinates to other 2D
0N/A * coordinates that preserves the "straightness" and
0N/A * "parallelness" of lines. Affine transformations can be constructed
0N/A * using sequences of translations, scales, flips, rotations, and shears.
0N/A * <p>
0N/A * Such a coordinate transformation can be represented by a 3 row by
0N/A * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix
0N/A * transforms source coordinates {@code (x,y)} into
0N/A * destination coordinates {@code (x',y')} by considering
0N/A * them to be a column vector and multiplying the coordinate vector
0N/A * by the matrix according to the following process:
0N/A * <pre>
0N/A * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ]
0N/A * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ]
0N/A * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
0N/A * </pre>
0N/A * <p>
0N/A * <a name="quadrantapproximation"><h4>Handling 90-Degree Rotations</h4></a>
0N/A * <p>
0N/A * In some variations of the <code>rotate</code> methods in the
0N/A * <code>AffineTransform</code> class, a double-precision argument
0N/A * specifies the angle of rotation in radians.
0N/A * These methods have special handling for rotations of approximately
0N/A * 90 degrees (including multiples such as 180, 270, and 360 degrees),
0N/A * so that the common case of quadrant rotation is handled more
0N/A * efficiently.
0N/A * This special handling can cause angles very close to multiples of
0N/A * 90 degrees to be treated as if they were exact multiples of
0N/A * 90 degrees.
0N/A * For small multiples of 90 degrees the range of angles treated
0N/A * as a quadrant rotation is approximately 0.00000121 degrees wide.
0N/A * This section explains why such special care is needed and how
0N/A * it is implemented.
0N/A * <p>
0N/A * Since 90 degrees is represented as <code>PI/2</code> in radians,
0N/A * and since PI is a transcendental (and therefore irrational) number,
0N/A * it is not possible to exactly represent a multiple of 90 degrees as
0N/A * an exact double precision value measured in radians.
0N/A * As a result it is theoretically impossible to describe quadrant
0N/A * rotations (90, 180, 270 or 360 degrees) using these values.
0N/A * Double precision floating point values can get very close to
0N/A * non-zero multiples of <code>PI/2</code> but never close enough
0N/A * for the sine or cosine to be exactly 0.0, 1.0 or -1.0.
0N/A * The implementations of <code>Math.sin()</code> and
0N/A * <code>Math.cos()</code> correspondingly never return 0.0
0N/A * for any case other than <code>Math.sin(0.0)</code>.
0N/A * These same implementations do, however, return exactly 1.0 and
0N/A * -1.0 for some range of numbers around each multiple of 90
0N/A * degrees since the correct answer is so close to 1.0 or -1.0 that
0N/A * the double precision significand cannot represent the difference
0N/A * as accurately as it can for numbers that are near 0.0.
0N/A * <p>
0N/A * The net result of these issues is that if the
0N/A * <code>Math.sin()</code> and <code>Math.cos()</code> methods
0N/A * are used to directly generate the values for the matrix modifications
0N/A * during these radian-based rotation operations then the resulting
0N/A * transform is never strictly classifiable as a quadrant rotation
0N/A * even for a simple case like <code>rotate(Math.PI/2.0)</code>,
0N/A * due to minor variations in the matrix caused by the non-0.0 values
0N/A * obtained for the sine and cosine.
0N/A * If these transforms are not classified as quadrant rotations then
0N/A * subsequent code which attempts to optimize further operations based
0N/A * upon the type of the transform will be relegated to its most general
0N/A * implementation.
0N/A * <p>
0N/A * Because quadrant rotations are fairly common,
0N/A * this class should handle these cases reasonably quickly, both in
0N/A * applying the rotations to the transform and in applying the resulting
0N/A * transform to the coordinates.
0N/A * To facilitate this optimal handling, the methods which take an angle
0N/A * of rotation measured in radians attempt to detect angles that are
0N/A * intended to be quadrant rotations and treat them as such.
0N/A * These methods therefore treat an angle <em>theta</em> as a quadrant
0N/A * rotation if either <code>Math.sin(<em>theta</em>)</code> or
0N/A * <code>Math.cos(<em>theta</em>)</code> returns exactly 1.0 or -1.0.
0N/A * As a rule of thumb, this property holds true for a range of
0N/A * approximately 0.0000000211 radians (or 0.00000121 degrees) around
0N/A * small multiples of <code>Math.PI/2.0</code>.
0N/A *
0N/A * @author Jim Graham
0N/A * @since 1.2
0N/A */
0N/Apublic class AffineTransform implements Cloneable, java.io.Serializable {
0N/A
0N/A /*
0N/A * This constant is only useful for the cached type field.
0N/A * It indicates that the type has been decached and must be recalculated.
0N/A */
0N/A private static final int TYPE_UNKNOWN = -1;
0N/A
0N/A /**
0N/A * This constant indicates that the transform defined by this object
0N/A * is an identity transform.
0N/A * An identity transform is one in which the output coordinates are
0N/A * always the same as the input coordinates.
0N/A * If this transform is anything other than the identity transform,
0N/A * the type will either be the constant GENERAL_TRANSFORM or a
0N/A * combination of the appropriate flag bits for the various coordinate
0N/A * conversions that this transform performs.
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_FLIP
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @see #getType
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_IDENTITY = 0;
0N/A
0N/A /**
0N/A * This flag bit indicates that the transform defined by this object
0N/A * performs a translation in addition to the conversions indicated
0N/A * by other flag bits.
0N/A * A translation moves the coordinates by a constant amount in x
0N/A * and y without changing the length or angle of vectors.
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_FLIP
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @see #getType
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_TRANSLATION = 1;
0N/A
0N/A /**
0N/A * This flag bit indicates that the transform defined by this object
0N/A * performs a uniform scale in addition to the conversions indicated
0N/A * by other flag bits.
0N/A * A uniform scale multiplies the length of vectors by the same amount
0N/A * in both the x and y directions without changing the angle between
0N/A * vectors.
0N/A * This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag.
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_FLIP
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @see #getType
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_UNIFORM_SCALE = 2;
0N/A
0N/A /**
0N/A * This flag bit indicates that the transform defined by this object
0N/A * performs a general scale in addition to the conversions indicated
0N/A * by other flag bits.
0N/A * A general scale multiplies the length of vectors by different
0N/A * amounts in the x and y directions without changing the angle
0N/A * between perpendicular vectors.
0N/A * This flag bit is mutually exclusive with the TYPE_UNIFORM_SCALE flag.
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_FLIP
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @see #getType
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_GENERAL_SCALE = 4;
0N/A
0N/A /**
0N/A * This constant is a bit mask for any of the scale flag bits.
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE |
0N/A TYPE_GENERAL_SCALE);
0N/A
0N/A /**
0N/A * This flag bit indicates that the transform defined by this object
0N/A * performs a mirror image flip about some axis which changes the
0N/A * normally right handed coordinate system into a left handed
0N/A * system in addition to the conversions indicated by other flag bits.
0N/A * A right handed coordinate system is one where the positive X
0N/A * axis rotates counterclockwise to overlay the positive Y axis
0N/A * similar to the direction that the fingers on your right hand
0N/A * curl when you stare end on at your thumb.
0N/A * A left handed coordinate system is one where the positive X
0N/A * axis rotates clockwise to overlay the positive Y axis similar
0N/A * to the direction that the fingers on your left hand curl.
0N/A * There is no mathematical way to determine the angle of the
0N/A * original flipping or mirroring transformation since all angles
0N/A * of flip are identical given an appropriate adjusting rotation.
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @see #getType
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_FLIP = 64;
0N/A /* NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public
0N/A * circulation and the flag bits could no longer be conveniently
0N/A * renumbered without introducing binary incompatibility in outside
0N/A * code.
0N/A */
0N/A
0N/A /**
0N/A * This flag bit indicates that the transform defined by this object
0N/A * performs a quadrant rotation by some multiple of 90 degrees in
0N/A * addition to the conversions indicated by other flag bits.
0N/A * A rotation changes the angles of vectors by the same amount
0N/A * regardless of the original direction of the vector and without
0N/A * changing the length of the vector.
0N/A * This flag bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag.
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_FLIP
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @see #getType
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_QUADRANT_ROTATION = 8;
0N/A
0N/A /**
0N/A * This flag bit indicates that the transform defined by this object
0N/A * performs a rotation by an arbitrary angle in addition to the
0N/A * conversions indicated by other flag bits.
0N/A * A rotation changes the angles of vectors by the same amount
0N/A * regardless of the original direction of the vector and without
0N/A * changing the length of the vector.
0N/A * This flag bit is mutually exclusive with the
0N/A * TYPE_QUADRANT_ROTATION flag.
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_FLIP
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @see #getType
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_GENERAL_ROTATION = 16;
0N/A
0N/A /**
0N/A * This constant is a bit mask for any of the rotation flag bits.
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION |
0N/A TYPE_GENERAL_ROTATION);
0N/A
0N/A /**
0N/A * This constant indicates that the transform defined by this object
0N/A * performs an arbitrary conversion of the input coordinates.
0N/A * If this transform can be classified by any of the above constants,
0N/A * the type will either be the constant TYPE_IDENTITY or a
0N/A * combination of the appropriate flag bits for the various coordinate
0N/A * conversions that this transform performs.
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_FLIP
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #getType
0N/A * @since 1.2
0N/A */
0N/A public static final int TYPE_GENERAL_TRANSFORM = 32;
0N/A
0N/A /**
0N/A * This constant is used for the internal state variable to indicate
0N/A * that no calculations need to be performed and that the source
0N/A * coordinates only need to be copied to their destinations to
0N/A * complete the transformation equation of this transform.
0N/A * @see #APPLY_TRANSLATE
0N/A * @see #APPLY_SCALE
0N/A * @see #APPLY_SHEAR
0N/A * @see #state
0N/A */
0N/A static final int APPLY_IDENTITY = 0;
0N/A
0N/A /**
0N/A * This constant is used for the internal state variable to indicate
0N/A * that the translation components of the matrix (m02 and m12) need
0N/A * to be added to complete the transformation equation of this transform.
0N/A * @see #APPLY_IDENTITY
0N/A * @see #APPLY_SCALE
0N/A * @see #APPLY_SHEAR
0N/A * @see #state
0N/A */
0N/A static final int APPLY_TRANSLATE = 1;
0N/A
0N/A /**
0N/A * This constant is used for the internal state variable to indicate
0N/A * that the scaling components of the matrix (m00 and m11) need
0N/A * to be factored in to complete the transformation equation of
0N/A * this transform. If the APPLY_SHEAR bit is also set then it
0N/A * indicates that the scaling components are not both 0.0. If the
0N/A * APPLY_SHEAR bit is not also set then it indicates that the
0N/A * scaling components are not both 1.0. If neither the APPLY_SHEAR
0N/A * nor the APPLY_SCALE bits are set then the scaling components
0N/A * are both 1.0, which means that the x and y components contribute
0N/A * to the transformed coordinate, but they are not multiplied by
0N/A * any scaling factor.
0N/A * @see #APPLY_IDENTITY
0N/A * @see #APPLY_TRANSLATE
0N/A * @see #APPLY_SHEAR
0N/A * @see #state
0N/A */
0N/A static final int APPLY_SCALE = 2;
0N/A
0N/A /**
0N/A * This constant is used for the internal state variable to indicate
0N/A * that the shearing components of the matrix (m01 and m10) need
0N/A * to be factored in to complete the transformation equation of this
0N/A * transform. The presence of this bit in the state variable changes
0N/A * the interpretation of the APPLY_SCALE bit as indicated in its
0N/A * documentation.
0N/A * @see #APPLY_IDENTITY
0N/A * @see #APPLY_TRANSLATE
0N/A * @see #APPLY_SCALE
0N/A * @see #state
0N/A */
0N/A static final int APPLY_SHEAR = 4;
0N/A
0N/A /*
0N/A * For methods which combine together the state of two separate
0N/A * transforms and dispatch based upon the combination, these constants
0N/A * specify how far to shift one of the states so that the two states
0N/A * are mutually non-interfering and provide constants for testing the
0N/A * bits of the shifted (HI) state. The methods in this class use
0N/A * the convention that the state of "this" transform is unshifted and
0N/A * the state of the "other" or "argument" transform is shifted (HI).
0N/A */
0N/A private static final int HI_SHIFT = 3;
0N/A private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT;
0N/A private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT;
0N/A private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT;
0N/A private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT;
0N/A
0N/A /**
0N/A * The X coordinate scaling element of the 3x3
0N/A * affine transformation matrix.
0N/A *
0N/A * @serial
0N/A */
0N/A double m00;
0N/A
0N/A /**
0N/A * The Y coordinate shearing element of the 3x3
0N/A * affine transformation matrix.
0N/A *
0N/A * @serial
0N/A */
0N/A double m10;
0N/A
0N/A /**
0N/A * The X coordinate shearing element of the 3x3
0N/A * affine transformation matrix.
0N/A *
0N/A * @serial
0N/A */
0N/A double m01;
0N/A
0N/A /**
0N/A * The Y coordinate scaling element of the 3x3
0N/A * affine transformation matrix.
0N/A *
0N/A * @serial
0N/A */
0N/A double m11;
0N/A
0N/A /**
0N/A * The X coordinate of the translation element of the
0N/A * 3x3 affine transformation matrix.
0N/A *
0N/A * @serial
0N/A */
0N/A double m02;
0N/A
0N/A /**
0N/A * The Y coordinate of the translation element of the
0N/A * 3x3 affine transformation matrix.
0N/A *
0N/A * @serial
0N/A */
0N/A double m12;
0N/A
0N/A /**
0N/A * This field keeps track of which components of the matrix need to
0N/A * be applied when performing a transformation.
0N/A * @see #APPLY_IDENTITY
0N/A * @see #APPLY_TRANSLATE
0N/A * @see #APPLY_SCALE
0N/A * @see #APPLY_SHEAR
0N/A */
0N/A transient int state;
0N/A
0N/A /**
0N/A * This field caches the current transformation type of the matrix.
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_FLIP
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @see #TYPE_UNKNOWN
0N/A * @see #getType
0N/A */
0N/A private transient int type;
0N/A
0N/A private AffineTransform(double m00, double m10,
0N/A double m01, double m11,
0N/A double m02, double m12,
0N/A int state) {
0N/A this.m00 = m00;
0N/A this.m10 = m10;
0N/A this.m01 = m01;
0N/A this.m11 = m11;
0N/A this.m02 = m02;
0N/A this.m12 = m12;
0N/A this.state = state;
0N/A this.type = TYPE_UNKNOWN;
0N/A }
0N/A
0N/A /**
0N/A * Constructs a new <code>AffineTransform</code> representing the
0N/A * Identity transformation.
0N/A * @since 1.2
0N/A */
0N/A public AffineTransform() {
0N/A m00 = m11 = 1.0;
0N/A // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */
0N/A // state = APPLY_IDENTITY; /* Not needed. */
0N/A // type = TYPE_IDENTITY; /* Not needed. */
0N/A }
0N/A
0N/A /**
0N/A * Constructs a new <code>AffineTransform</code> that is a copy of
0N/A * the specified <code>AffineTransform</code> object.
0N/A * @param Tx the <code>AffineTransform</code> object to copy
0N/A * @since 1.2
0N/A */
0N/A public AffineTransform(AffineTransform Tx) {
0N/A this.m00 = Tx.m00;
0N/A this.m10 = Tx.m10;
0N/A this.m01 = Tx.m01;
0N/A this.m11 = Tx.m11;
0N/A this.m02 = Tx.m02;
0N/A this.m12 = Tx.m12;
0N/A this.state = Tx.state;
0N/A this.type = Tx.type;
0N/A }
0N/A
0N/A /**
0N/A * Constructs a new <code>AffineTransform</code> from 6 floating point
0N/A * values representing the 6 specifiable entries of the 3x3
0N/A * transformation matrix.
0N/A *
0N/A * @param m00 the X coordinate scaling element of the 3x3 matrix
0N/A * @param m10 the Y coordinate shearing element of the 3x3 matrix
0N/A * @param m01 the X coordinate shearing element of the 3x3 matrix
0N/A * @param m11 the Y coordinate scaling element of the 3x3 matrix
0N/A * @param m02 the X coordinate translation element of the 3x3 matrix
0N/A * @param m12 the Y coordinate translation element of the 3x3 matrix
0N/A * @since 1.2
0N/A */
2965N/A @ConstructorProperties({ "scaleX", "shearY", "shearX", "scaleY", "translateX", "translateY" })
0N/A public AffineTransform(float m00, float m10,
0N/A float m01, float m11,
0N/A float m02, float m12) {
0N/A this.m00 = m00;
0N/A this.m10 = m10;
0N/A this.m01 = m01;
0N/A this.m11 = m11;
0N/A this.m02 = m02;
0N/A this.m12 = m12;
0N/A updateState();
0N/A }
0N/A
0N/A /**
0N/A * Constructs a new <code>AffineTransform</code> from an array of
0N/A * floating point values representing either the 4 non-translation
0N/A * enries or the 6 specifiable entries of the 3x3 transformation
0N/A * matrix. The values are retrieved from the array as
0N/A * {&nbsp;m00&nbsp;m10&nbsp;m01&nbsp;m11&nbsp;[m02&nbsp;m12]}.
0N/A * @param flatmatrix the float array containing the values to be set
0N/A * in the new <code>AffineTransform</code> object. The length of the
0N/A * array is assumed to be at least 4. If the length of the array is
0N/A * less than 6, only the first 4 values are taken. If the length of
0N/A * the array is greater than 6, the first 6 values are taken.
0N/A * @since 1.2
0N/A */
0N/A public AffineTransform(float[] flatmatrix) {
0N/A m00 = flatmatrix[0];
0N/A m10 = flatmatrix[1];
0N/A m01 = flatmatrix[2];
0N/A m11 = flatmatrix[3];
0N/A if (flatmatrix.length > 5) {
0N/A m02 = flatmatrix[4];
0N/A m12 = flatmatrix[5];
0N/A }
0N/A updateState();
0N/A }
0N/A
0N/A /**
0N/A * Constructs a new <code>AffineTransform</code> from 6 double
0N/A * precision values representing the 6 specifiable entries of the 3x3
0N/A * transformation matrix.
0N/A *
0N/A * @param m00 the X coordinate scaling element of the 3x3 matrix
0N/A * @param m10 the Y coordinate shearing element of the 3x3 matrix
0N/A * @param m01 the X coordinate shearing element of the 3x3 matrix
0N/A * @param m11 the Y coordinate scaling element of the 3x3 matrix
0N/A * @param m02 the X coordinate translation element of the 3x3 matrix
0N/A * @param m12 the Y coordinate translation element of the 3x3 matrix
0N/A * @since 1.2
0N/A */
0N/A public AffineTransform(double m00, double m10,
0N/A double m01, double m11,
0N/A double m02, double m12) {
0N/A this.m00 = m00;
0N/A this.m10 = m10;
0N/A this.m01 = m01;
0N/A this.m11 = m11;
0N/A this.m02 = m02;
0N/A this.m12 = m12;
0N/A updateState();
0N/A }
0N/A
0N/A /**
0N/A * Constructs a new <code>AffineTransform</code> from an array of
0N/A * double precision values representing either the 4 non-translation
0N/A * entries or the 6 specifiable entries of the 3x3 transformation
0N/A * matrix. The values are retrieved from the array as
0N/A * {&nbsp;m00&nbsp;m10&nbsp;m01&nbsp;m11&nbsp;[m02&nbsp;m12]}.
0N/A * @param flatmatrix the double array containing the values to be set
0N/A * in the new <code>AffineTransform</code> object. The length of the
0N/A * array is assumed to be at least 4. If the length of the array is
0N/A * less than 6, only the first 4 values are taken. If the length of
0N/A * the array is greater than 6, the first 6 values are taken.
0N/A * @since 1.2
0N/A */
0N/A public AffineTransform(double[] flatmatrix) {
0N/A m00 = flatmatrix[0];
0N/A m10 = flatmatrix[1];
0N/A m01 = flatmatrix[2];
0N/A m11 = flatmatrix[3];
0N/A if (flatmatrix.length > 5) {
0N/A m02 = flatmatrix[4];
0N/A m12 = flatmatrix[5];
0N/A }
0N/A updateState();
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform representing a translation transformation.
0N/A * The matrix representing the returned transform is:
0N/A * <pre>
0N/A * [ 1 0 tx ]
0N/A * [ 0 1 ty ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param tx the distance by which coordinates are translated in the
0N/A * X axis direction
0N/A * @param ty the distance by which coordinates are translated in the
0N/A * Y axis direction
0N/A * @return an <code>AffineTransform</code> object that represents a
0N/A * translation transformation, created with the specified vector.
0N/A * @since 1.2
0N/A */
0N/A public static AffineTransform getTranslateInstance(double tx, double ty) {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToTranslation(tx, ty);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform representing a rotation transformation.
0N/A * The matrix representing the returned transform is:
0N/A * <pre>
0N/A * [ cos(theta) -sin(theta) 0 ]
0N/A * [ sin(theta) cos(theta) 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * Rotating by a positive angle theta rotates points on the positive
0N/A * X axis toward the positive Y axis.
0N/A * Note also the discussion of
0N/A * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a>
0N/A * above.
0N/A * @param theta the angle of rotation measured in radians
0N/A * @return an <code>AffineTransform</code> object that is a rotation
0N/A * transformation, created with the specified angle of rotation.
0N/A * @since 1.2
0N/A */
0N/A public static AffineTransform getRotateInstance(double theta) {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToRotation(theta);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform that rotates coordinates around an anchor point.
0N/A * This operation is equivalent to translating the coordinates so
0N/A * that the anchor point is at the origin (S1), then rotating them
0N/A * about the new origin (S2), and finally translating so that the
0N/A * intermediate origin is restored to the coordinates of the original
0N/A * anchor point (S3).
0N/A * <p>
0N/A * This operation is equivalent to the following sequence of calls:
0N/A * <pre>
0N/A * AffineTransform Tx = new AffineTransform();
0N/A * Tx.translate(anchorx, anchory); // S3: final translation
0N/A * Tx.rotate(theta); // S2: rotate around anchor
0N/A * Tx.translate(-anchorx, -anchory); // S1: translate anchor to origin
0N/A * </pre>
0N/A * The matrix representing the returned transform is:
0N/A * <pre>
0N/A * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
0N/A * [ sin(theta) cos(theta) y-x*sin-y*cos ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * Rotating by a positive angle theta rotates points on the positive
0N/A * X axis toward the positive Y axis.
0N/A * Note also the discussion of
0N/A * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a>
0N/A * above.
0N/A *
0N/A * @param theta the angle of rotation measured in radians
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @return an <code>AffineTransform</code> object that rotates
0N/A * coordinates around the specified point by the specified angle of
0N/A * rotation.
0N/A * @since 1.2
0N/A */
0N/A public static AffineTransform getRotateInstance(double theta,
0N/A double anchorx,
0N/A double anchory)
0N/A {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToRotation(theta, anchorx, anchory);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform that rotates coordinates according to
0N/A * a rotation vector.
0N/A * All coordinates rotate about the origin by the same amount.
0N/A * The amount of rotation is such that coordinates along the former
0N/A * positive X axis will subsequently align with the vector pointing
0N/A * from the origin to the specified vector coordinates.
0N/A * If both <code>vecx</code> and <code>vecy</code> are 0.0,
0N/A * an identity transform is returned.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * AffineTransform.getRotateInstance(Math.atan2(vecy, vecx));
0N/A * </pre>
0N/A *
0N/A * @param vecx the X coordinate of the rotation vector
0N/A * @param vecy the Y coordinate of the rotation vector
0N/A * @return an <code>AffineTransform</code> object that rotates
0N/A * coordinates according to the specified rotation vector.
0N/A * @since 1.6
0N/A */
0N/A public static AffineTransform getRotateInstance(double vecx, double vecy) {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToRotation(vecx, vecy);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform that rotates coordinates around an anchor
0N/A * point accordinate to a rotation vector.
0N/A * All coordinates rotate about the specified anchor coordinates
0N/A * by the same amount.
0N/A * The amount of rotation is such that coordinates along the former
0N/A * positive X axis will subsequently align with the vector pointing
0N/A * from the origin to the specified vector coordinates.
0N/A * If both <code>vecx</code> and <code>vecy</code> are 0.0,
0N/A * an identity transform is returned.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * AffineTransform.getRotateInstance(Math.atan2(vecy, vecx),
0N/A * anchorx, anchory);
0N/A * </pre>
0N/A *
0N/A * @param vecx the X coordinate of the rotation vector
0N/A * @param vecy the Y coordinate of the rotation vector
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @return an <code>AffineTransform</code> object that rotates
0N/A * coordinates around the specified point according to the
0N/A * specified rotation vector.
0N/A * @since 1.6
0N/A */
0N/A public static AffineTransform getRotateInstance(double vecx,
0N/A double vecy,
0N/A double anchorx,
0N/A double anchory)
0N/A {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToRotation(vecx, vecy, anchorx, anchory);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform that rotates coordinates by the specified
0N/A * number of quadrants.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0);
0N/A * </pre>
0N/A * Rotating by a positive number of quadrants rotates points on
0N/A * the positive X axis toward the positive Y axis.
0N/A * @param numquadrants the number of 90 degree arcs to rotate by
0N/A * @return an <code>AffineTransform</code> object that rotates
0N/A * coordinates by the specified number of quadrants.
0N/A * @since 1.6
0N/A */
0N/A public static AffineTransform getQuadrantRotateInstance(int numquadrants) {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToQuadrantRotation(numquadrants);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform that rotates coordinates by the specified
0N/A * number of quadrants around the specified anchor point.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * AffineTransform.getRotateInstance(numquadrants * Math.PI / 2.0,
0N/A * anchorx, anchory);
0N/A * </pre>
0N/A * Rotating by a positive number of quadrants rotates points on
0N/A * the positive X axis toward the positive Y axis.
0N/A *
0N/A * @param numquadrants the number of 90 degree arcs to rotate by
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @return an <code>AffineTransform</code> object that rotates
0N/A * coordinates by the specified number of quadrants around the
0N/A * specified anchor point.
0N/A * @since 1.6
0N/A */
0N/A public static AffineTransform getQuadrantRotateInstance(int numquadrants,
0N/A double anchorx,
0N/A double anchory)
0N/A {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToQuadrantRotation(numquadrants, anchorx, anchory);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform representing a scaling transformation.
0N/A * The matrix representing the returned transform is:
0N/A * <pre>
0N/A * [ sx 0 0 ]
0N/A * [ 0 sy 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param sx the factor by which coordinates are scaled along the
0N/A * X axis direction
0N/A * @param sy the factor by which coordinates are scaled along the
0N/A * Y axis direction
0N/A * @return an <code>AffineTransform</code> object that scales
0N/A * coordinates by the specified factors.
0N/A * @since 1.2
0N/A */
0N/A public static AffineTransform getScaleInstance(double sx, double sy) {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToScale(sx, sy);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Returns a transform representing a shearing transformation.
0N/A * The matrix representing the returned transform is:
0N/A * <pre>
0N/A * [ 1 shx 0 ]
0N/A * [ shy 1 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param shx the multiplier by which coordinates are shifted in the
0N/A * direction of the positive X axis as a factor of their Y coordinate
0N/A * @param shy the multiplier by which coordinates are shifted in the
0N/A * direction of the positive Y axis as a factor of their X coordinate
0N/A * @return an <code>AffineTransform</code> object that shears
0N/A * coordinates by the specified multipliers.
0N/A * @since 1.2
0N/A */
0N/A public static AffineTransform getShearInstance(double shx, double shy) {
0N/A AffineTransform Tx = new AffineTransform();
0N/A Tx.setToShear(shx, shy);
0N/A return Tx;
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the flag bits describing the conversion properties of
0N/A * this transform.
0N/A * The return value is either one of the constants TYPE_IDENTITY
0N/A * or TYPE_GENERAL_TRANSFORM, or a combination of the
0N/A * appriopriate flag bits.
0N/A * A valid combination of flag bits is an exclusive OR operation
0N/A * that can combine
0N/A * the TYPE_TRANSLATION flag bit
0N/A * in addition to either of the
0N/A * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits
0N/A * as well as either of the
0N/A * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits.
0N/A * @return the OR combination of any of the indicated flags that
0N/A * apply to this transform
0N/A * @see #TYPE_IDENTITY
0N/A * @see #TYPE_TRANSLATION
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @see #TYPE_GENERAL_SCALE
0N/A * @see #TYPE_QUADRANT_ROTATION
0N/A * @see #TYPE_GENERAL_ROTATION
0N/A * @see #TYPE_GENERAL_TRANSFORM
0N/A * @since 1.2
0N/A */
0N/A public int getType() {
0N/A if (type == TYPE_UNKNOWN) {
0N/A calculateType();
0N/A }
0N/A return type;
0N/A }
0N/A
0N/A /**
0N/A * This is the utility function to calculate the flag bits when
0N/A * they have not been cached.
0N/A * @see #getType
0N/A */
0N/A private void calculateType() {
0N/A int ret = TYPE_IDENTITY;
0N/A boolean sgn0, sgn1;
0N/A double M0, M1, M2, M3;
0N/A updateState();
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A ret = TYPE_TRANSLATION;
0N/A /* NOBREAK */
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A if ((M0 = m00) * (M2 = m01) + (M3 = m10) * (M1 = m11) != 0) {
0N/A // Transformed unit vectors are not perpendicular...
0N/A this.type = TYPE_GENERAL_TRANSFORM;
0N/A return;
0N/A }
0N/A sgn0 = (M0 >= 0.0);
0N/A sgn1 = (M1 >= 0.0);
0N/A if (sgn0 == sgn1) {
0N/A // sgn(M0) == sgn(M1) therefore sgn(M2) == -sgn(M3)
0N/A // This is the "unflipped" (right-handed) state
0N/A if (M0 != M1 || M2 != -M3) {
0N/A ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE);
0N/A } else if (M0 * M1 - M2 * M3 != 1.0) {
0N/A ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE);
0N/A } else {
0N/A ret |= TYPE_GENERAL_ROTATION;
0N/A }
0N/A } else {
0N/A // sgn(M0) == -sgn(M1) therefore sgn(M2) == sgn(M3)
0N/A // This is the "flipped" (left-handed) state
0N/A if (M0 != -M1 || M2 != M3) {
0N/A ret |= (TYPE_GENERAL_ROTATION |
0N/A TYPE_FLIP |
0N/A TYPE_GENERAL_SCALE);
0N/A } else if (M0 * M1 - M2 * M3 != 1.0) {
0N/A ret |= (TYPE_GENERAL_ROTATION |
0N/A TYPE_FLIP |
0N/A TYPE_UNIFORM_SCALE);
0N/A } else {
0N/A ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP);
0N/A }
0N/A }
0N/A break;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A ret = TYPE_TRANSLATION;
0N/A /* NOBREAK */
0N/A case (APPLY_SHEAR):
0N/A sgn0 = ((M0 = m01) >= 0.0);
0N/A sgn1 = ((M1 = m10) >= 0.0);
0N/A if (sgn0 != sgn1) {
0N/A // Different signs - simple 90 degree rotation
0N/A if (M0 != -M1) {
0N/A ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
0N/A } else if (M0 != 1.0 && M0 != -1.0) {
0N/A ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
0N/A } else {
0N/A ret |= TYPE_QUADRANT_ROTATION;
0N/A }
0N/A } else {
0N/A // Same signs - 90 degree rotation plus an axis flip too
0N/A if (M0 == M1) {
0N/A ret |= (TYPE_QUADRANT_ROTATION |
0N/A TYPE_FLIP |
0N/A TYPE_UNIFORM_SCALE);
0N/A } else {
0N/A ret |= (TYPE_QUADRANT_ROTATION |
0N/A TYPE_FLIP |
0N/A TYPE_GENERAL_SCALE);
0N/A }
0N/A }
0N/A break;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A ret = TYPE_TRANSLATION;
0N/A /* NOBREAK */
0N/A case (APPLY_SCALE):
0N/A sgn0 = ((M0 = m00) >= 0.0);
0N/A sgn1 = ((M1 = m11) >= 0.0);
0N/A if (sgn0 == sgn1) {
0N/A if (sgn0) {
0N/A // Both scaling factors non-negative - simple scale
0N/A // Note: APPLY_SCALE implies M0, M1 are not both 1
0N/A if (M0 == M1) {
0N/A ret |= TYPE_UNIFORM_SCALE;
0N/A } else {
0N/A ret |= TYPE_GENERAL_SCALE;
0N/A }
0N/A } else {
0N/A // Both scaling factors negative - 180 degree rotation
0N/A if (M0 != M1) {
0N/A ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE);
0N/A } else if (M0 != -1.0) {
0N/A ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE);
0N/A } else {
0N/A ret |= TYPE_QUADRANT_ROTATION;
0N/A }
0N/A }
0N/A } else {
0N/A // Scaling factor signs different - flip about some axis
0N/A if (M0 == -M1) {
0N/A if (M0 == 1.0 || M0 == -1.0) {
0N/A ret |= TYPE_FLIP;
0N/A } else {
0N/A ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE);
0N/A }
0N/A } else {
0N/A ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE);
0N/A }
0N/A }
0N/A break;
0N/A case (APPLY_TRANSLATE):
0N/A ret = TYPE_TRANSLATION;
0N/A break;
0N/A case (APPLY_IDENTITY):
0N/A break;
0N/A }
0N/A this.type = ret;
0N/A }
0N/A
0N/A /**
0N/A * Returns the determinant of the matrix representation of the transform.
0N/A * The determinant is useful both to determine if the transform can
0N/A * be inverted and to get a single value representing the
0N/A * combined X and Y scaling of the transform.
0N/A * <p>
0N/A * If the determinant is non-zero, then this transform is
0N/A * invertible and the various methods that depend on the inverse
0N/A * transform do not need to throw a
0N/A * {@link NoninvertibleTransformException}.
0N/A * If the determinant is zero then this transform can not be
0N/A * inverted since the transform maps all input coordinates onto
0N/A * a line or a point.
0N/A * If the determinant is near enough to zero then inverse transform
0N/A * operations might not carry enough precision to produce meaningful
0N/A * results.
0N/A * <p>
0N/A * If this transform represents a uniform scale, as indicated by
0N/A * the <code>getType</code> method then the determinant also
0N/A * represents the square of the uniform scale factor by which all of
0N/A * the points are expanded from or contracted towards the origin.
0N/A * If this transform represents a non-uniform scale or more general
0N/A * transform then the determinant is not likely to represent a
0N/A * value useful for any purpose other than determining if inverse
0N/A * transforms are possible.
0N/A * <p>
0N/A * Mathematically, the determinant is calculated using the formula:
0N/A * <pre>
0N/A * | m00 m01 m02 |
0N/A * | m10 m11 m12 | = m00 * m11 - m01 * m10
0N/A * | 0 0 1 |
0N/A * </pre>
0N/A *
0N/A * @return the determinant of the matrix used to transform the
0N/A * coordinates.
0N/A * @see #getType
0N/A * @see #createInverse
0N/A * @see #inverseTransform
0N/A * @see #TYPE_UNIFORM_SCALE
0N/A * @since 1.2
0N/A */
0N/A public double getDeterminant() {
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A return m00 * m11 - m01 * m10;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR):
0N/A return -(m01 * m10);
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SCALE):
0N/A return m00 * m11;
0N/A case (APPLY_TRANSLATE):
0N/A case (APPLY_IDENTITY):
0N/A return 1.0;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Manually recalculates the state of the transform when the matrix
0N/A * changes too much to predict the effects on the state.
0N/A * The following table specifies what the various settings of the
0N/A * state field say about the values of the corresponding matrix
0N/A * element fields.
0N/A * Note that the rules governing the SCALE fields are slightly
0N/A * different depending on whether the SHEAR flag is also set.
0N/A * <pre>
0N/A * SCALE SHEAR TRANSLATE
0N/A * m00/m11 m01/m10 m02/m12
0N/A *
0N/A * IDENTITY 1.0 0.0 0.0
0N/A * TRANSLATE (TR) 1.0 0.0 not both 0.0
0N/A * SCALE (SC) not both 1.0 0.0 0.0
0N/A * TR | SC not both 1.0 0.0 not both 0.0
0N/A * SHEAR (SH) 0.0 not both 0.0 0.0
0N/A * TR | SH 0.0 not both 0.0 not both 0.0
0N/A * SC | SH not both 0.0 not both 0.0 0.0
0N/A * TR | SC | SH not both 0.0 not both 0.0 not both 0.0
0N/A * </pre>
0N/A */
0N/A void updateState() {
0N/A if (m01 == 0.0 && m10 == 0.0) {
0N/A if (m00 == 1.0 && m11 == 1.0) {
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A } else {
0N/A state = APPLY_TRANSLATE;
0N/A type = TYPE_TRANSLATION;
0N/A }
0N/A } else {
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_SCALE;
0N/A type = TYPE_UNKNOWN;
0N/A } else {
0N/A state = (APPLY_SCALE | APPLY_TRANSLATE);
0N/A type = TYPE_UNKNOWN;
0N/A }
0N/A }
0N/A } else {
0N/A if (m00 == 0.0 && m11 == 0.0) {
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_SHEAR;
0N/A type = TYPE_UNKNOWN;
0N/A } else {
0N/A state = (APPLY_SHEAR | APPLY_TRANSLATE);
0N/A type = TYPE_UNKNOWN;
0N/A }
0N/A } else {
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = (APPLY_SHEAR | APPLY_SCALE);
0N/A type = TYPE_UNKNOWN;
0N/A } else {
0N/A state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);
0N/A type = TYPE_UNKNOWN;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Convenience method used internally to throw exceptions when
0N/A * a case was forgotten in a switch statement.
0N/A */
0N/A private void stateError() {
0N/A throw new InternalError("missing case in transform state switch");
0N/A }
0N/A
0N/A /**
0N/A * Retrieves the 6 specifiable values in the 3x3 affine transformation
0N/A * matrix and places them into an array of double precisions values.
0N/A * The values are stored in the array as
0N/A * {&nbsp;m00&nbsp;m10&nbsp;m01&nbsp;m11&nbsp;m02&nbsp;m12&nbsp;}.
0N/A * An array of 4 doubles can also be specified, in which case only the
0N/A * first four elements representing the non-transform
0N/A * parts of the array are retrieved and the values are stored into
0N/A * the array as {&nbsp;m00&nbsp;m10&nbsp;m01&nbsp;m11&nbsp;}
0N/A * @param flatmatrix the double array used to store the returned
0N/A * values.
0N/A * @see #getScaleX
0N/A * @see #getScaleY
0N/A * @see #getShearX
0N/A * @see #getShearY
0N/A * @see #getTranslateX
0N/A * @see #getTranslateY
0N/A * @since 1.2
0N/A */
0N/A public void getMatrix(double[] flatmatrix) {
0N/A flatmatrix[0] = m00;
0N/A flatmatrix[1] = m10;
0N/A flatmatrix[2] = m01;
0N/A flatmatrix[3] = m11;
0N/A if (flatmatrix.length > 5) {
0N/A flatmatrix[4] = m02;
0N/A flatmatrix[5] = m12;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the X coordinate scaling element (m00) of the 3x3
0N/A * affine transformation matrix.
0N/A * @return a double value that is the X coordinate of the scaling
0N/A * element of the affine transformation matrix.
0N/A * @see #getMatrix
0N/A * @since 1.2
0N/A */
0N/A public double getScaleX() {
0N/A return m00;
0N/A }
0N/A
0N/A /**
0N/A * Returns the Y coordinate scaling element (m11) of the 3x3
0N/A * affine transformation matrix.
0N/A * @return a double value that is the Y coordinate of the scaling
0N/A * element of the affine transformation matrix.
0N/A * @see #getMatrix
0N/A * @since 1.2
0N/A */
0N/A public double getScaleY() {
0N/A return m11;
0N/A }
0N/A
0N/A /**
0N/A * Returns the X coordinate shearing element (m01) of the 3x3
0N/A * affine transformation matrix.
0N/A * @return a double value that is the X coordinate of the shearing
0N/A * element of the affine transformation matrix.
0N/A * @see #getMatrix
0N/A * @since 1.2
0N/A */
0N/A public double getShearX() {
0N/A return m01;
0N/A }
0N/A
0N/A /**
0N/A * Returns the Y coordinate shearing element (m10) of the 3x3
0N/A * affine transformation matrix.
0N/A * @return a double value that is the Y coordinate of the shearing
0N/A * element of the affine transformation matrix.
0N/A * @see #getMatrix
0N/A * @since 1.2
0N/A */
0N/A public double getShearY() {
0N/A return m10;
0N/A }
0N/A
0N/A /**
0N/A * Returns the X coordinate of the translation element (m02) of the
0N/A * 3x3 affine transformation matrix.
0N/A * @return a double value that is the X coordinate of the translation
0N/A * element of the affine transformation matrix.
0N/A * @see #getMatrix
0N/A * @since 1.2
0N/A */
0N/A public double getTranslateX() {
0N/A return m02;
0N/A }
0N/A
0N/A /**
0N/A * Returns the Y coordinate of the translation element (m12) of the
0N/A * 3x3 affine transformation matrix.
0N/A * @return a double value that is the Y coordinate of the translation
0N/A * element of the affine transformation matrix.
0N/A * @see #getMatrix
0N/A * @since 1.2
0N/A */
0N/A public double getTranslateY() {
0N/A return m12;
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a translation transformation.
0N/A * This is equivalent to calling concatenate(T), where T is an
0N/A * <code>AffineTransform</code> represented by the following matrix:
0N/A * <pre>
0N/A * [ 1 0 tx ]
0N/A * [ 0 1 ty ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param tx the distance by which coordinates are translated in the
0N/A * X axis direction
0N/A * @param ty the distance by which coordinates are translated in the
0N/A * Y axis direction
0N/A * @since 1.2
0N/A */
0N/A public void translate(double tx, double ty) {
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A m02 = tx * m00 + ty * m01 + m02;
0N/A m12 = tx * m10 + ty * m11 + m12;
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_SHEAR | APPLY_SCALE;
0N/A if (type != TYPE_UNKNOWN) {
0N/A type -= TYPE_TRANSLATION;
0N/A }
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A m02 = tx * m00 + ty * m01;
0N/A m12 = tx * m10 + ty * m11;
0N/A if (m02 != 0.0 || m12 != 0.0) {
0N/A state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE;
0N/A type |= TYPE_TRANSLATION;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A m02 = ty * m01 + m02;
0N/A m12 = tx * m10 + m12;
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_SHEAR;
0N/A if (type != TYPE_UNKNOWN) {
0N/A type -= TYPE_TRANSLATION;
0N/A }
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR):
0N/A m02 = ty * m01;
0N/A m12 = tx * m10;
0N/A if (m02 != 0.0 || m12 != 0.0) {
0N/A state = APPLY_SHEAR | APPLY_TRANSLATE;
0N/A type |= TYPE_TRANSLATION;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A m02 = tx * m00 + m02;
0N/A m12 = ty * m11 + m12;
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_SCALE;
0N/A if (type != TYPE_UNKNOWN) {
0N/A type -= TYPE_TRANSLATION;
0N/A }
0N/A }
0N/A return;
0N/A case (APPLY_SCALE):
0N/A m02 = tx * m00;
0N/A m12 = ty * m11;
0N/A if (m02 != 0.0 || m12 != 0.0) {
0N/A state = APPLY_SCALE | APPLY_TRANSLATE;
0N/A type |= TYPE_TRANSLATION;
0N/A }
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A m02 = tx + m02;
0N/A m12 = ty + m12;
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A }
0N/A return;
0N/A case (APPLY_IDENTITY):
0N/A m02 = tx;
0N/A m12 = ty;
0N/A if (tx != 0.0 || ty != 0.0) {
0N/A state = APPLY_TRANSLATE;
0N/A type = TYPE_TRANSLATION;
0N/A }
0N/A return;
0N/A }
0N/A }
0N/A
0N/A // Utility methods to optimize rotate methods.
0N/A // These tables translate the flags during predictable quadrant
0N/A // rotations where the shear and scale values are swapped and negated.
0N/A private static final int rot90conversion[] = {
0N/A /* IDENTITY => */ APPLY_SHEAR,
0N/A /* TRANSLATE (TR) => */ APPLY_SHEAR | APPLY_TRANSLATE,
0N/A /* SCALE (SC) => */ APPLY_SHEAR,
0N/A /* SC | TR => */ APPLY_SHEAR | APPLY_TRANSLATE,
0N/A /* SHEAR (SH) => */ APPLY_SCALE,
0N/A /* SH | TR => */ APPLY_SCALE | APPLY_TRANSLATE,
0N/A /* SH | SC => */ APPLY_SHEAR | APPLY_SCALE,
0N/A /* SH | SC | TR => */ APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE,
0N/A };
0N/A private final void rotate90() {
0N/A double M0 = m00;
0N/A m00 = m01;
0N/A m01 = -M0;
0N/A M0 = m10;
0N/A m10 = m11;
0N/A m11 = -M0;
0N/A int state = rot90conversion[this.state];
0N/A if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE &&
0N/A m00 == 1.0 && m11 == 1.0)
0N/A {
0N/A state -= APPLY_SCALE;
0N/A }
0N/A this.state = state;
0N/A type = TYPE_UNKNOWN;
0N/A }
0N/A private final void rotate180() {
0N/A m00 = -m00;
0N/A m11 = -m11;
0N/A int state = this.state;
0N/A if ((state & (APPLY_SHEAR)) != 0) {
0N/A // If there was a shear, then this rotation has no
0N/A // effect on the state.
0N/A m01 = -m01;
0N/A m10 = -m10;
0N/A } else {
0N/A // No shear means the SCALE state may toggle when
0N/A // m00 and m11 are negated.
0N/A if (m00 == 1.0 && m11 == 1.0) {
0N/A this.state = state & ~APPLY_SCALE;
0N/A } else {
0N/A this.state = state | APPLY_SCALE;
0N/A }
0N/A }
0N/A type = TYPE_UNKNOWN;
0N/A }
0N/A private final void rotate270() {
0N/A double M0 = m00;
0N/A m00 = -m01;
0N/A m01 = M0;
0N/A M0 = m10;
0N/A m10 = -m11;
0N/A m11 = M0;
0N/A int state = rot90conversion[this.state];
0N/A if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE &&
0N/A m00 == 1.0 && m11 == 1.0)
0N/A {
0N/A state -= APPLY_SCALE;
0N/A }
0N/A this.state = state;
0N/A type = TYPE_UNKNOWN;
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a rotation transformation.
0N/A * This is equivalent to calling concatenate(R), where R is an
0N/A * <code>AffineTransform</code> represented by the following matrix:
0N/A * <pre>
0N/A * [ cos(theta) -sin(theta) 0 ]
0N/A * [ sin(theta) cos(theta) 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * Rotating by a positive angle theta rotates points on the positive
0N/A * X axis toward the positive Y axis.
0N/A * Note also the discussion of
0N/A * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a>
0N/A * above.
0N/A * @param theta the angle of rotation measured in radians
0N/A * @since 1.2
0N/A */
0N/A public void rotate(double theta) {
0N/A double sin = Math.sin(theta);
0N/A if (sin == 1.0) {
0N/A rotate90();
0N/A } else if (sin == -1.0) {
0N/A rotate270();
0N/A } else {
0N/A double cos = Math.cos(theta);
0N/A if (cos == -1.0) {
0N/A rotate180();
0N/A } else if (cos != 1.0) {
0N/A double M0, M1;
0N/A M0 = m00;
0N/A M1 = m01;
0N/A m00 = cos * M0 + sin * M1;
0N/A m01 = -sin * M0 + cos * M1;
0N/A M0 = m10;
0N/A M1 = m11;
0N/A m10 = cos * M0 + sin * M1;
0N/A m11 = -sin * M0 + cos * M1;
0N/A updateState();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a transform that rotates
0N/A * coordinates around an anchor point.
0N/A * This operation is equivalent to translating the coordinates so
0N/A * that the anchor point is at the origin (S1), then rotating them
0N/A * about the new origin (S2), and finally translating so that the
0N/A * intermediate origin is restored to the coordinates of the original
0N/A * anchor point (S3).
0N/A * <p>
0N/A * This operation is equivalent to the following sequence of calls:
0N/A * <pre>
0N/A * translate(anchorx, anchory); // S3: final translation
0N/A * rotate(theta); // S2: rotate around anchor
0N/A * translate(-anchorx, -anchory); // S1: translate anchor to origin
0N/A * </pre>
0N/A * Rotating by a positive angle theta rotates points on the positive
0N/A * X axis toward the positive Y axis.
0N/A * Note also the discussion of
0N/A * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a>
0N/A * above.
0N/A *
0N/A * @param theta the angle of rotation measured in radians
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @since 1.2
0N/A */
0N/A public void rotate(double theta, double anchorx, double anchory) {
0N/A // REMIND: Simple for now - optimize later
0N/A translate(anchorx, anchory);
0N/A rotate(theta);
0N/A translate(-anchorx, -anchory);
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a transform that rotates
0N/A * coordinates according to a rotation vector.
0N/A * All coordinates rotate about the origin by the same amount.
0N/A * The amount of rotation is such that coordinates along the former
0N/A * positive X axis will subsequently align with the vector pointing
0N/A * from the origin to the specified vector coordinates.
0N/A * If both <code>vecx</code> and <code>vecy</code> are 0.0,
0N/A * no additional rotation is added to this transform.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * rotate(Math.atan2(vecy, vecx));
0N/A * </pre>
0N/A *
0N/A * @param vecx the X coordinate of the rotation vector
0N/A * @param vecy the Y coordinate of the rotation vector
0N/A * @since 1.6
0N/A */
0N/A public void rotate(double vecx, double vecy) {
0N/A if (vecy == 0.0) {
0N/A if (vecx < 0.0) {
0N/A rotate180();
0N/A }
0N/A // If vecx > 0.0 - no rotation
0N/A // If vecx == 0.0 - undefined rotation - treat as no rotation
0N/A } else if (vecx == 0.0) {
0N/A if (vecy > 0.0) {
0N/A rotate90();
0N/A } else { // vecy must be < 0.0
0N/A rotate270();
0N/A }
0N/A } else {
0N/A double len = Math.sqrt(vecx * vecx + vecy * vecy);
0N/A double sin = vecy / len;
0N/A double cos = vecx / len;
0N/A double M0, M1;
0N/A M0 = m00;
0N/A M1 = m01;
0N/A m00 = cos * M0 + sin * M1;
0N/A m01 = -sin * M0 + cos * M1;
0N/A M0 = m10;
0N/A M1 = m11;
0N/A m10 = cos * M0 + sin * M1;
0N/A m11 = -sin * M0 + cos * M1;
0N/A updateState();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a transform that rotates
0N/A * coordinates around an anchor point according to a rotation
0N/A * vector.
0N/A * All coordinates rotate about the specified anchor coordinates
0N/A * by the same amount.
0N/A * The amount of rotation is such that coordinates along the former
0N/A * positive X axis will subsequently align with the vector pointing
0N/A * from the origin to the specified vector coordinates.
0N/A * If both <code>vecx</code> and <code>vecy</code> are 0.0,
0N/A * the transform is not modified in any way.
0N/A * This method is equivalent to calling:
0N/A * <pre>
0N/A * rotate(Math.atan2(vecy, vecx), anchorx, anchory);
0N/A * </pre>
0N/A *
0N/A * @param vecx the X coordinate of the rotation vector
0N/A * @param vecy the Y coordinate of the rotation vector
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @since 1.6
0N/A */
0N/A public void rotate(double vecx, double vecy,
0N/A double anchorx, double anchory)
0N/A {
0N/A // REMIND: Simple for now - optimize later
0N/A translate(anchorx, anchory);
0N/A rotate(vecx, vecy);
0N/A translate(-anchorx, -anchory);
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a transform that rotates
0N/A * coordinates by the specified number of quadrants.
0N/A * This is equivalent to calling:
0N/A * <pre>
0N/A * rotate(numquadrants * Math.PI / 2.0);
0N/A * </pre>
0N/A * Rotating by a positive number of quadrants rotates points on
0N/A * the positive X axis toward the positive Y axis.
0N/A * @param numquadrants the number of 90 degree arcs to rotate by
0N/A * @since 1.6
0N/A */
0N/A public void quadrantRotate(int numquadrants) {
0N/A switch (numquadrants & 3) {
0N/A case 0:
0N/A break;
0N/A case 1:
0N/A rotate90();
0N/A break;
0N/A case 2:
0N/A rotate180();
0N/A break;
0N/A case 3:
0N/A rotate270();
0N/A break;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a transform that rotates
0N/A * coordinates by the specified number of quadrants around
0N/A * the specified anchor point.
0N/A * This method is equivalent to calling:
0N/A * <pre>
0N/A * rotate(numquadrants * Math.PI / 2.0, anchorx, anchory);
0N/A * </pre>
0N/A * Rotating by a positive number of quadrants rotates points on
0N/A * the positive X axis toward the positive Y axis.
0N/A *
0N/A * @param numquadrants the number of 90 degree arcs to rotate by
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @since 1.6
0N/A */
0N/A public void quadrantRotate(int numquadrants,
0N/A double anchorx, double anchory)
0N/A {
0N/A switch (numquadrants & 3) {
0N/A case 0:
0N/A return;
0N/A case 1:
0N/A m02 += anchorx * (m00 - m01) + anchory * (m01 + m00);
0N/A m12 += anchorx * (m10 - m11) + anchory * (m11 + m10);
0N/A rotate90();
0N/A break;
0N/A case 2:
0N/A m02 += anchorx * (m00 + m00) + anchory * (m01 + m01);
0N/A m12 += anchorx * (m10 + m10) + anchory * (m11 + m11);
0N/A rotate180();
0N/A break;
0N/A case 3:
0N/A m02 += anchorx * (m00 + m01) + anchory * (m01 - m00);
0N/A m12 += anchorx * (m10 + m11) + anchory * (m11 - m10);
0N/A rotate270();
0N/A break;
0N/A }
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state &= ~APPLY_TRANSLATE;
0N/A } else {
0N/A state |= APPLY_TRANSLATE;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a scaling transformation.
0N/A * This is equivalent to calling concatenate(S), where S is an
0N/A * <code>AffineTransform</code> represented by the following matrix:
0N/A * <pre>
0N/A * [ sx 0 0 ]
0N/A * [ 0 sy 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param sx the factor by which coordinates are scaled along the
0N/A * X axis direction
0N/A * @param sy the factor by which coordinates are scaled along the
0N/A * Y axis direction
0N/A * @since 1.2
0N/A */
0N/A public void scale(double sx, double sy) {
0N/A int state = this.state;
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A m00 *= sx;
0N/A m11 *= sy;
0N/A /* NOBREAK */
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR):
0N/A m01 *= sy;
0N/A m10 *= sx;
0N/A if (m01 == 0 && m10 == 0) {
0N/A state &= APPLY_TRANSLATE;
0N/A if (m00 == 1.0 && m11 == 1.0) {
0N/A this.type = (state == APPLY_IDENTITY
0N/A ? TYPE_IDENTITY
0N/A : TYPE_TRANSLATION);
0N/A } else {
0N/A state |= APPLY_SCALE;
0N/A this.type = TYPE_UNKNOWN;
0N/A }
0N/A this.state = state;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SCALE):
0N/A m00 *= sx;
0N/A m11 *= sy;
0N/A if (m00 == 1.0 && m11 == 1.0) {
0N/A this.state = (state &= APPLY_TRANSLATE);
0N/A this.type = (state == APPLY_IDENTITY
0N/A ? TYPE_IDENTITY
0N/A : TYPE_TRANSLATION);
0N/A } else {
0N/A this.type = TYPE_UNKNOWN;
0N/A }
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A case (APPLY_IDENTITY):
0N/A m00 = sx;
0N/A m11 = sy;
0N/A if (sx != 1.0 || sy != 1.0) {
0N/A this.state = state | APPLY_SCALE;
0N/A this.type = TYPE_UNKNOWN;
0N/A }
0N/A return;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Concatenates this transform with a shearing transformation.
0N/A * This is equivalent to calling concatenate(SH), where SH is an
0N/A * <code>AffineTransform</code> represented by the following matrix:
0N/A * <pre>
0N/A * [ 1 shx 0 ]
0N/A * [ shy 1 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param shx the multiplier by which coordinates are shifted in the
0N/A * direction of the positive X axis as a factor of their Y coordinate
0N/A * @param shy the multiplier by which coordinates are shifted in the
0N/A * direction of the positive Y axis as a factor of their X coordinate
0N/A * @since 1.2
0N/A */
0N/A public void shear(double shx, double shy) {
0N/A int state = this.state;
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A double M0, M1;
0N/A M0 = m00;
0N/A M1 = m01;
0N/A m00 = M0 + M1 * shy;
0N/A m01 = M0 * shx + M1;
0N/A
0N/A M0 = m10;
0N/A M1 = m11;
0N/A m10 = M0 + M1 * shy;
0N/A m11 = M0 * shx + M1;
0N/A updateState();
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR):
0N/A m00 = m01 * shy;
0N/A m11 = m10 * shx;
0N/A if (m00 != 0.0 || m11 != 0.0) {
0N/A this.state = state | APPLY_SCALE;
0N/A }
0N/A this.type = TYPE_UNKNOWN;
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SCALE):
0N/A m01 = m00 * shx;
0N/A m10 = m11 * shy;
0N/A if (m01 != 0.0 || m10 != 0.0) {
0N/A this.state = state | APPLY_SHEAR;
0N/A }
0N/A this.type = TYPE_UNKNOWN;
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A case (APPLY_IDENTITY):
0N/A m01 = shx;
0N/A m10 = shy;
0N/A if (m01 != 0.0 || m10 != 0.0) {
0N/A this.state = state | APPLY_SCALE | APPLY_SHEAR;
0N/A this.type = TYPE_UNKNOWN;
0N/A }
0N/A return;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Resets this transform to the Identity transform.
0N/A * @since 1.2
0N/A */
0N/A public void setToIdentity() {
0N/A m00 = m11 = 1.0;
0N/A m10 = m01 = m02 = m12 = 0.0;
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a translation transformation.
0N/A * The matrix representing this transform becomes:
0N/A * <pre>
0N/A * [ 1 0 tx ]
0N/A * [ 0 1 ty ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param tx the distance by which coordinates are translated in the
0N/A * X axis direction
0N/A * @param ty the distance by which coordinates are translated in the
0N/A * Y axis direction
0N/A * @since 1.2
0N/A */
0N/A public void setToTranslation(double tx, double ty) {
0N/A m00 = 1.0;
0N/A m10 = 0.0;
0N/A m01 = 0.0;
0N/A m11 = 1.0;
0N/A m02 = tx;
0N/A m12 = ty;
0N/A if (tx != 0.0 || ty != 0.0) {
0N/A state = APPLY_TRANSLATE;
0N/A type = TYPE_TRANSLATION;
0N/A } else {
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a rotation transformation.
0N/A * The matrix representing this transform becomes:
0N/A * <pre>
0N/A * [ cos(theta) -sin(theta) 0 ]
0N/A * [ sin(theta) cos(theta) 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * Rotating by a positive angle theta rotates points on the positive
0N/A * X axis toward the positive Y axis.
0N/A * Note also the discussion of
0N/A * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a>
0N/A * above.
0N/A * @param theta the angle of rotation measured in radians
0N/A * @since 1.2
0N/A */
0N/A public void setToRotation(double theta) {
0N/A double sin = Math.sin(theta);
0N/A double cos;
0N/A if (sin == 1.0 || sin == -1.0) {
0N/A cos = 0.0;
0N/A state = APPLY_SHEAR;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A } else {
0N/A cos = Math.cos(theta);
0N/A if (cos == -1.0) {
0N/A sin = 0.0;
0N/A state = APPLY_SCALE;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A } else if (cos == 1.0) {
0N/A sin = 0.0;
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A } else {
0N/A state = APPLY_SHEAR | APPLY_SCALE;
0N/A type = TYPE_GENERAL_ROTATION;
0N/A }
0N/A }
0N/A m00 = cos;
0N/A m10 = sin;
0N/A m01 = -sin;
0N/A m11 = cos;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a translated rotation transformation.
0N/A * This operation is equivalent to translating the coordinates so
0N/A * that the anchor point is at the origin (S1), then rotating them
0N/A * about the new origin (S2), and finally translating so that the
0N/A * intermediate origin is restored to the coordinates of the original
0N/A * anchor point (S3).
0N/A * <p>
0N/A * This operation is equivalent to the following sequence of calls:
0N/A * <pre>
0N/A * setToTranslation(anchorx, anchory); // S3: final translation
0N/A * rotate(theta); // S2: rotate around anchor
0N/A * translate(-anchorx, -anchory); // S1: translate anchor to origin
0N/A * </pre>
0N/A * The matrix representing this transform becomes:
0N/A * <pre>
0N/A * [ cos(theta) -sin(theta) x-x*cos+y*sin ]
0N/A * [ sin(theta) cos(theta) y-x*sin-y*cos ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * Rotating by a positive angle theta rotates points on the positive
0N/A * X axis toward the positive Y axis.
0N/A * Note also the discussion of
0N/A * <a href="#quadrantapproximation">Handling 90-Degree Rotations</a>
0N/A * above.
0N/A *
0N/A * @param theta the angle of rotation measured in radians
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @since 1.2
0N/A */
0N/A public void setToRotation(double theta, double anchorx, double anchory) {
0N/A setToRotation(theta);
0N/A double sin = m10;
0N/A double oneMinusCos = 1.0 - m00;
0N/A m02 = anchorx * oneMinusCos + anchory * sin;
0N/A m12 = anchory * oneMinusCos - anchorx * sin;
0N/A if (m02 != 0.0 || m12 != 0.0) {
0N/A state |= APPLY_TRANSLATE;
0N/A type |= TYPE_TRANSLATION;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a rotation transformation that rotates
0N/A * coordinates according to a rotation vector.
0N/A * All coordinates rotate about the origin by the same amount.
0N/A * The amount of rotation is such that coordinates along the former
0N/A * positive X axis will subsequently align with the vector pointing
0N/A * from the origin to the specified vector coordinates.
0N/A * If both <code>vecx</code> and <code>vecy</code> are 0.0,
0N/A * the transform is set to an identity transform.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * setToRotation(Math.atan2(vecy, vecx));
0N/A * </pre>
0N/A *
0N/A * @param vecx the X coordinate of the rotation vector
0N/A * @param vecy the Y coordinate of the rotation vector
0N/A * @since 1.6
0N/A */
0N/A public void setToRotation(double vecx, double vecy) {
0N/A double sin, cos;
0N/A if (vecy == 0) {
0N/A sin = 0.0;
0N/A if (vecx < 0.0) {
0N/A cos = -1.0;
0N/A state = APPLY_SCALE;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A } else {
0N/A cos = 1.0;
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A }
0N/A } else if (vecx == 0) {
0N/A cos = 0.0;
0N/A sin = (vecy > 0.0) ? 1.0 : -1.0;
0N/A state = APPLY_SHEAR;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A } else {
0N/A double len = Math.sqrt(vecx * vecx + vecy * vecy);
0N/A cos = vecx / len;
0N/A sin = vecy / len;
0N/A state = APPLY_SHEAR | APPLY_SCALE;
0N/A type = TYPE_GENERAL_ROTATION;
0N/A }
0N/A m00 = cos;
0N/A m10 = sin;
0N/A m01 = -sin;
0N/A m11 = cos;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a rotation transformation that rotates
0N/A * coordinates around an anchor point according to a rotation
0N/A * vector.
0N/A * All coordinates rotate about the specified anchor coordinates
0N/A * by the same amount.
0N/A * The amount of rotation is such that coordinates along the former
0N/A * positive X axis will subsequently align with the vector pointing
0N/A * from the origin to the specified vector coordinates.
0N/A * If both <code>vecx</code> and <code>vecy</code> are 0.0,
0N/A * the transform is set to an identity transform.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory);
0N/A * </pre>
0N/A *
0N/A * @param vecx the X coordinate of the rotation vector
0N/A * @param vecy the Y coordinate of the rotation vector
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @since 1.6
0N/A */
0N/A public void setToRotation(double vecx, double vecy,
0N/A double anchorx, double anchory)
0N/A {
0N/A setToRotation(vecx, vecy);
0N/A double sin = m10;
0N/A double oneMinusCos = 1.0 - m00;
0N/A m02 = anchorx * oneMinusCos + anchory * sin;
0N/A m12 = anchory * oneMinusCos - anchorx * sin;
0N/A if (m02 != 0.0 || m12 != 0.0) {
0N/A state |= APPLY_TRANSLATE;
0N/A type |= TYPE_TRANSLATION;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a rotation transformation that rotates
0N/A * coordinates by the specified number of quadrants.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * setToRotation(numquadrants * Math.PI / 2.0);
0N/A * </pre>
0N/A * Rotating by a positive number of quadrants rotates points on
0N/A * the positive X axis toward the positive Y axis.
0N/A * @param numquadrants the number of 90 degree arcs to rotate by
0N/A * @since 1.6
0N/A */
0N/A public void setToQuadrantRotation(int numquadrants) {
0N/A switch (numquadrants & 3) {
0N/A case 0:
0N/A m00 = 1.0;
0N/A m10 = 0.0;
0N/A m01 = 0.0;
0N/A m11 = 1.0;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A break;
0N/A case 1:
0N/A m00 = 0.0;
0N/A m10 = 1.0;
0N/A m01 = -1.0;
0N/A m11 = 0.0;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A state = APPLY_SHEAR;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A break;
0N/A case 2:
0N/A m00 = -1.0;
0N/A m10 = 0.0;
0N/A m01 = 0.0;
0N/A m11 = -1.0;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A state = APPLY_SCALE;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A break;
0N/A case 3:
0N/A m00 = 0.0;
0N/A m10 = -1.0;
0N/A m01 = 1.0;
0N/A m11 = 0.0;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A state = APPLY_SHEAR;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A break;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a translated rotation transformation
0N/A * that rotates coordinates by the specified number of quadrants
0N/A * around the specified anchor point.
0N/A * This operation is equivalent to calling:
0N/A * <pre>
0N/A * setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory);
0N/A * </pre>
0N/A * Rotating by a positive number of quadrants rotates points on
0N/A * the positive X axis toward the positive Y axis.
0N/A *
0N/A * @param numquadrants the number of 90 degree arcs to rotate by
0N/A * @param anchorx the X coordinate of the rotation anchor point
0N/A * @param anchory the Y coordinate of the rotation anchor point
0N/A * @since 1.6
0N/A */
0N/A public void setToQuadrantRotation(int numquadrants,
0N/A double anchorx, double anchory)
0N/A {
0N/A switch (numquadrants & 3) {
0N/A case 0:
0N/A m00 = 1.0;
0N/A m10 = 0.0;
0N/A m01 = 0.0;
0N/A m11 = 1.0;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A break;
0N/A case 1:
0N/A m00 = 0.0;
0N/A m10 = 1.0;
0N/A m01 = -1.0;
0N/A m11 = 0.0;
0N/A m02 = anchorx + anchory;
0N/A m12 = anchory - anchorx;
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_SHEAR;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A } else {
0N/A state = APPLY_SHEAR | APPLY_TRANSLATE;
0N/A type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
0N/A }
0N/A break;
0N/A case 2:
0N/A m00 = -1.0;
0N/A m10 = 0.0;
0N/A m01 = 0.0;
0N/A m11 = -1.0;
0N/A m02 = anchorx + anchorx;
0N/A m12 = anchory + anchory;
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_SCALE;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A } else {
0N/A state = APPLY_SCALE | APPLY_TRANSLATE;
0N/A type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
0N/A }
0N/A break;
0N/A case 3:
0N/A m00 = 0.0;
0N/A m10 = -1.0;
0N/A m01 = 1.0;
0N/A m11 = 0.0;
0N/A m02 = anchorx - anchory;
0N/A m12 = anchory + anchorx;
0N/A if (m02 == 0.0 && m12 == 0.0) {
0N/A state = APPLY_SHEAR;
0N/A type = TYPE_QUADRANT_ROTATION;
0N/A } else {
0N/A state = APPLY_SHEAR | APPLY_TRANSLATE;
0N/A type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION;
0N/A }
0N/A break;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a scaling transformation.
0N/A * The matrix representing this transform becomes:
0N/A * <pre>
0N/A * [ sx 0 0 ]
0N/A * [ 0 sy 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param sx the factor by which coordinates are scaled along the
0N/A * X axis direction
0N/A * @param sy the factor by which coordinates are scaled along the
0N/A * Y axis direction
0N/A * @since 1.2
0N/A */
0N/A public void setToScale(double sx, double sy) {
0N/A m00 = sx;
0N/A m10 = 0.0;
0N/A m01 = 0.0;
0N/A m11 = sy;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A if (sx != 1.0 || sy != 1.0) {
0N/A state = APPLY_SCALE;
0N/A type = TYPE_UNKNOWN;
0N/A } else {
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a shearing transformation.
0N/A * The matrix representing this transform becomes:
0N/A * <pre>
0N/A * [ 1 shx 0 ]
0N/A * [ shy 1 0 ]
0N/A * [ 0 0 1 ]
0N/A * </pre>
0N/A * @param shx the multiplier by which coordinates are shifted in the
0N/A * direction of the positive X axis as a factor of their Y coordinate
0N/A * @param shy the multiplier by which coordinates are shifted in the
0N/A * direction of the positive Y axis as a factor of their X coordinate
0N/A * @since 1.2
0N/A */
0N/A public void setToShear(double shx, double shy) {
0N/A m00 = 1.0;
0N/A m01 = shx;
0N/A m10 = shy;
0N/A m11 = 1.0;
0N/A m02 = 0.0;
0N/A m12 = 0.0;
0N/A if (shx != 0.0 || shy != 0.0) {
0N/A state = (APPLY_SHEAR | APPLY_SCALE);
0N/A type = TYPE_UNKNOWN;
0N/A } else {
0N/A state = APPLY_IDENTITY;
0N/A type = TYPE_IDENTITY;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to a copy of the transform in the specified
0N/A * <code>AffineTransform</code> object.
0N/A * @param Tx the <code>AffineTransform</code> object from which to
0N/A * copy the transform
0N/A * @since 1.2
0N/A */
0N/A public void setTransform(AffineTransform Tx) {
0N/A this.m00 = Tx.m00;
0N/A this.m10 = Tx.m10;
0N/A this.m01 = Tx.m01;
0N/A this.m11 = Tx.m11;
0N/A this.m02 = Tx.m02;
0N/A this.m12 = Tx.m12;
0N/A this.state = Tx.state;
0N/A this.type = Tx.type;
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to the matrix specified by the 6
0N/A * double precision values.
0N/A *
0N/A * @param m00 the X coordinate scaling element of the 3x3 matrix
0N/A * @param m10 the Y coordinate shearing element of the 3x3 matrix
0N/A * @param m01 the X coordinate shearing element of the 3x3 matrix
0N/A * @param m11 the Y coordinate scaling element of the 3x3 matrix
0N/A * @param m02 the X coordinate translation element of the 3x3 matrix
0N/A * @param m12 the Y coordinate translation element of the 3x3 matrix
0N/A * @since 1.2
0N/A */
0N/A public void setTransform(double m00, double m10,
0N/A double m01, double m11,
0N/A double m02, double m12) {
0N/A this.m00 = m00;
0N/A this.m10 = m10;
0N/A this.m01 = m01;
0N/A this.m11 = m11;
0N/A this.m02 = m02;
0N/A this.m12 = m12;
0N/A updateState();
0N/A }
0N/A
0N/A /**
0N/A * Concatenates an <code>AffineTransform</code> <code>Tx</code> to
0N/A * this <code>AffineTransform</code> Cx in the most commonly useful
0N/A * way to provide a new user space
0N/A * that is mapped to the former user space by <code>Tx</code>.
0N/A * Cx is updated to perform the combined transformation.
0N/A * Transforming a point p by the updated transform Cx' is
0N/A * equivalent to first transforming p by <code>Tx</code> and then
0N/A * transforming the result by the original transform Cx like this:
0N/A * Cx'(p) = Cx(Tx(p))
0N/A * In matrix notation, if this transform Cx is
0N/A * represented by the matrix [this] and <code>Tx</code> is represented
0N/A * by the matrix [Tx] then this method does the following:
0N/A * <pre>
0N/A * [this] = [this] x [Tx]
0N/A * </pre>
0N/A * @param Tx the <code>AffineTransform</code> object to be
0N/A * concatenated with this <code>AffineTransform</code> object.
0N/A * @see #preConcatenate
0N/A * @since 1.2
0N/A */
0N/A public void concatenate(AffineTransform Tx) {
0N/A double M0, M1;
0N/A double T00, T01, T10, T11;
0N/A double T02, T12;
0N/A int mystate = state;
0N/A int txstate = Tx.state;
0N/A switch ((txstate << HI_SHIFT) | mystate) {
0N/A
0N/A /* ---------- Tx == IDENTITY cases ---------- */
0N/A case (HI_IDENTITY | APPLY_IDENTITY):
0N/A case (HI_IDENTITY | APPLY_TRANSLATE):
0N/A case (HI_IDENTITY | APPLY_SCALE):
0N/A case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_IDENTITY | APPLY_SHEAR):
0N/A case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
0N/A case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A return;
0N/A
0N/A /* ---------- this == IDENTITY cases ---------- */
0N/A case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
0N/A m01 = Tx.m01;
0N/A m10 = Tx.m10;
0N/A /* NOBREAK */
0N/A case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY):
0N/A m00 = Tx.m00;
0N/A m11 = Tx.m11;
0N/A /* NOBREAK */
0N/A case (HI_TRANSLATE | APPLY_IDENTITY):
0N/A m02 = Tx.m02;
0N/A m12 = Tx.m12;
0N/A state = txstate;
0N/A type = Tx.type;
0N/A return;
0N/A case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY):
0N/A m01 = Tx.m01;
0N/A m10 = Tx.m10;
0N/A /* NOBREAK */
0N/A case (HI_SCALE | APPLY_IDENTITY):
0N/A m00 = Tx.m00;
0N/A m11 = Tx.m11;
0N/A state = txstate;
0N/A type = Tx.type;
0N/A return;
0N/A case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY):
0N/A m02 = Tx.m02;
0N/A m12 = Tx.m12;
0N/A /* NOBREAK */
0N/A case (HI_SHEAR | APPLY_IDENTITY):
0N/A m01 = Tx.m01;
0N/A m10 = Tx.m10;
0N/A m00 = m11 = 0.0;
0N/A state = txstate;
0N/A type = Tx.type;
0N/A return;
0N/A
0N/A /* ---------- Tx == TRANSLATE cases ---------- */
0N/A case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
0N/A case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_TRANSLATE | APPLY_SHEAR):
0N/A case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_TRANSLATE | APPLY_SCALE):
0N/A case (HI_TRANSLATE | APPLY_TRANSLATE):
0N/A translate(Tx.m02, Tx.m12);
0N/A return;
0N/A
0N/A /* ---------- Tx == SCALE cases ---------- */
0N/A case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
0N/A case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_SCALE | APPLY_SHEAR):
0N/A case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SCALE | APPLY_SCALE):
0N/A case (HI_SCALE | APPLY_TRANSLATE):
0N/A scale(Tx.m00, Tx.m11);
0N/A return;
0N/A
0N/A /* ---------- Tx == SHEAR cases ---------- */
0N/A case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
0N/A T01 = Tx.m01; T10 = Tx.m10;
0N/A M0 = m00;
0N/A m00 = m01 * T10;
0N/A m01 = M0 * T01;
0N/A M0 = m10;
0N/A m10 = m11 * T10;
0N/A m11 = M0 * T01;
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_SHEAR | APPLY_SHEAR):
0N/A m00 = m01 * Tx.m10;
0N/A m01 = 0.0;
0N/A m11 = m10 * Tx.m01;
0N/A m10 = 0.0;
0N/A state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SHEAR | APPLY_SCALE):
0N/A m01 = m00 * Tx.m01;
0N/A m00 = 0.0;
0N/A m10 = m11 * Tx.m10;
0N/A m11 = 0.0;
0N/A state = mystate ^ (APPLY_SHEAR | APPLY_SCALE);
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A case (HI_SHEAR | APPLY_TRANSLATE):
0N/A m00 = 0.0;
0N/A m01 = Tx.m01;
0N/A m10 = Tx.m10;
0N/A m11 = 0.0;
0N/A state = APPLY_TRANSLATE | APPLY_SHEAR;
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A }
0N/A // If Tx has more than one attribute, it is not worth optimizing
0N/A // all of those cases...
0N/A T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;
0N/A T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;
0N/A switch (mystate) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A state = mystate | txstate;
0N/A /* NOBREAK */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A M0 = m00;
0N/A M1 = m01;
0N/A m00 = T00 * M0 + T10 * M1;
0N/A m01 = T01 * M0 + T11 * M1;
0N/A m02 += T02 * M0 + T12 * M1;
0N/A
0N/A M0 = m10;
0N/A M1 = m11;
0N/A m10 = T00 * M0 + T10 * M1;
0N/A m11 = T01 * M0 + T11 * M1;
0N/A m12 += T02 * M0 + T12 * M1;
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR):
0N/A M0 = m01;
0N/A m00 = T10 * M0;
0N/A m01 = T11 * M0;
0N/A m02 += T12 * M0;
0N/A
0N/A M0 = m10;
0N/A m10 = T00 * M0;
0N/A m11 = T01 * M0;
0N/A m12 += T02 * M0;
0N/A break;
0N/A
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SCALE):
0N/A M0 = m00;
0N/A m00 = T00 * M0;
0N/A m01 = T01 * M0;
0N/A m02 += T02 * M0;
0N/A
0N/A M0 = m11;
0N/A m10 = T10 * M0;
0N/A m11 = T11 * M0;
0N/A m12 += T12 * M0;
0N/A break;
0N/A
0N/A case (APPLY_TRANSLATE):
0N/A m00 = T00;
0N/A m01 = T01;
0N/A m02 += T02;
0N/A
0N/A m10 = T10;
0N/A m11 = T11;
0N/A m12 += T12;
0N/A state = txstate | APPLY_TRANSLATE;
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A }
0N/A updateState();
0N/A }
0N/A
0N/A /**
0N/A * Concatenates an <code>AffineTransform</code> <code>Tx</code> to
0N/A * this <code>AffineTransform</code> Cx
0N/A * in a less commonly used way such that <code>Tx</code> modifies the
0N/A * coordinate transformation relative to the absolute pixel
0N/A * space rather than relative to the existing user space.
0N/A * Cx is updated to perform the combined transformation.
0N/A * Transforming a point p by the updated transform Cx' is
0N/A * equivalent to first transforming p by the original transform
0N/A * Cx and then transforming the result by
0N/A * <code>Tx</code> like this:
0N/A * Cx'(p) = Tx(Cx(p))
0N/A * In matrix notation, if this transform Cx
0N/A * is represented by the matrix [this] and <code>Tx</code> is
0N/A * represented by the matrix [Tx] then this method does the
0N/A * following:
0N/A * <pre>
0N/A * [this] = [Tx] x [this]
0N/A * </pre>
0N/A * @param Tx the <code>AffineTransform</code> object to be
0N/A * concatenated with this <code>AffineTransform</code> object.
0N/A * @see #concatenate
0N/A * @since 1.2
0N/A */
0N/A public void preConcatenate(AffineTransform Tx) {
0N/A double M0, M1;
0N/A double T00, T01, T10, T11;
0N/A double T02, T12;
0N/A int mystate = state;
0N/A int txstate = Tx.state;
0N/A switch ((txstate << HI_SHIFT) | mystate) {
0N/A case (HI_IDENTITY | APPLY_IDENTITY):
0N/A case (HI_IDENTITY | APPLY_TRANSLATE):
0N/A case (HI_IDENTITY | APPLY_SCALE):
0N/A case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_IDENTITY | APPLY_SHEAR):
0N/A case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE):
0N/A case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A // Tx is IDENTITY...
0N/A return;
0N/A
0N/A case (HI_TRANSLATE | APPLY_IDENTITY):
0N/A case (HI_TRANSLATE | APPLY_SCALE):
0N/A case (HI_TRANSLATE | APPLY_SHEAR):
0N/A case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE):
0N/A // Tx is TRANSLATE, this has no TRANSLATE
0N/A m02 = Tx.m02;
0N/A m12 = Tx.m12;
0N/A state = mystate | APPLY_TRANSLATE;
0N/A type |= TYPE_TRANSLATION;
0N/A return;
0N/A
0N/A case (HI_TRANSLATE | APPLY_TRANSLATE):
0N/A case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A // Tx is TRANSLATE, this has one too
0N/A m02 = m02 + Tx.m02;
0N/A m12 = m12 + Tx.m12;
0N/A return;
0N/A
0N/A case (HI_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SCALE | APPLY_IDENTITY):
0N/A // Only these two existing states need a new state
0N/A state = mystate | APPLY_SCALE;
0N/A /* NOBREAK */
0N/A case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE):
0N/A case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_SCALE | APPLY_SHEAR):
0N/A case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SCALE | APPLY_SCALE):
0N/A // Tx is SCALE, this is anything
0N/A T00 = Tx.m00;
0N/A T11 = Tx.m11;
0N/A if ((mystate & APPLY_SHEAR) != 0) {
0N/A m01 = m01 * T00;
0N/A m10 = m10 * T11;
0N/A if ((mystate & APPLY_SCALE) != 0) {
0N/A m00 = m00 * T00;
0N/A m11 = m11 * T11;
0N/A }
0N/A } else {
0N/A m00 = m00 * T00;
0N/A m11 = m11 * T11;
0N/A }
0N/A if ((mystate & APPLY_TRANSLATE) != 0) {
0N/A m02 = m02 * T00;
0N/A m12 = m12 * T11;
0N/A }
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_SHEAR | APPLY_SHEAR):
0N/A mystate = mystate | APPLY_SCALE;
0N/A /* NOBREAK */
0N/A case (HI_SHEAR | APPLY_TRANSLATE):
0N/A case (HI_SHEAR | APPLY_IDENTITY):
0N/A case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SHEAR | APPLY_SCALE):
0N/A state = mystate ^ APPLY_SHEAR;
0N/A /* NOBREAK */
0N/A case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE):
0N/A // Tx is SHEAR, this is anything
0N/A T01 = Tx.m01;
0N/A T10 = Tx.m10;
0N/A
0N/A M0 = m00;
0N/A m00 = m10 * T01;
0N/A m10 = M0 * T10;
0N/A
0N/A M0 = m01;
0N/A m01 = m11 * T01;
0N/A m11 = M0 * T10;
0N/A
0N/A M0 = m02;
0N/A m02 = m12 * T01;
0N/A m12 = M0 * T10;
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A }
0N/A // If Tx has more than one attribute, it is not worth optimizing
0N/A // all of those cases...
0N/A T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02;
0N/A T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12;
0N/A switch (mystate) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A M0 = m02;
0N/A M1 = m12;
0N/A T02 += M0 * T00 + M1 * T01;
0N/A T12 += M0 * T10 + M1 * T11;
0N/A
0N/A /* NOBREAK */
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A m02 = T02;
0N/A m12 = T12;
0N/A
0N/A M0 = m00;
0N/A M1 = m10;
0N/A m00 = M0 * T00 + M1 * T01;
0N/A m10 = M0 * T10 + M1 * T11;
0N/A
0N/A M0 = m01;
0N/A M1 = m11;
0N/A m01 = M0 * T00 + M1 * T01;
0N/A m11 = M0 * T10 + M1 * T11;
0N/A break;
0N/A
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A M0 = m02;
0N/A M1 = m12;
0N/A T02 += M0 * T00 + M1 * T01;
0N/A T12 += M0 * T10 + M1 * T11;
0N/A
0N/A /* NOBREAK */
0N/A case (APPLY_SHEAR):
0N/A m02 = T02;
0N/A m12 = T12;
0N/A
0N/A M0 = m10;
0N/A m00 = M0 * T01;
0N/A m10 = M0 * T11;
0N/A
0N/A M0 = m01;
0N/A m01 = M0 * T00;
0N/A m11 = M0 * T10;
0N/A break;
0N/A
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A M0 = m02;
0N/A M1 = m12;
0N/A T02 += M0 * T00 + M1 * T01;
0N/A T12 += M0 * T10 + M1 * T11;
0N/A
0N/A /* NOBREAK */
0N/A case (APPLY_SCALE):
0N/A m02 = T02;
0N/A m12 = T12;
0N/A
0N/A M0 = m00;
0N/A m00 = M0 * T00;
0N/A m10 = M0 * T10;
0N/A
0N/A M0 = m11;
0N/A m01 = M0 * T01;
0N/A m11 = M0 * T11;
0N/A break;
0N/A
0N/A case (APPLY_TRANSLATE):
0N/A M0 = m02;
0N/A M1 = m12;
0N/A T02 += M0 * T00 + M1 * T01;
0N/A T12 += M0 * T10 + M1 * T11;
0N/A
0N/A /* NOBREAK */
0N/A case (APPLY_IDENTITY):
0N/A m02 = T02;
0N/A m12 = T12;
0N/A
0N/A m00 = T00;
0N/A m10 = T10;
0N/A
0N/A m01 = T01;
0N/A m11 = T11;
0N/A
0N/A state = mystate | txstate;
0N/A type = TYPE_UNKNOWN;
0N/A return;
0N/A }
0N/A updateState();
0N/A }
0N/A
0N/A /**
0N/A * Returns an <code>AffineTransform</code> object representing the
0N/A * inverse transformation.
0N/A * The inverse transform Tx' of this transform Tx
0N/A * maps coordinates transformed by Tx back
0N/A * to their original coordinates.
0N/A * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
0N/A * <p>
0N/A * If this transform maps all coordinates onto a point or a line
0N/A * then it will not have an inverse, since coordinates that do
0N/A * not lie on the destination point or line will not have an inverse
0N/A * mapping.
0N/A * The <code>getDeterminant</code> method can be used to determine if this
0N/A * transform has no inverse, in which case an exception will be
0N/A * thrown if the <code>createInverse</code> method is called.
0N/A * @return a new <code>AffineTransform</code> object representing the
0N/A * inverse transformation.
0N/A * @see #getDeterminant
0N/A * @exception NoninvertibleTransformException
0N/A * if the matrix cannot be inverted.
0N/A * @since 1.2
0N/A */
0N/A public AffineTransform createInverse()
0N/A throws NoninvertibleTransformException
0N/A {
0N/A double det;
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A det = m00 * m11 - m01 * m10;
0N/A if (Math.abs(det) <= Double.MIN_VALUE) {
0N/A throw new NoninvertibleTransformException("Determinant is "+
0N/A det);
0N/A }
0N/A return new AffineTransform( m11 / det, -m10 / det,
0N/A -m01 / det, m00 / det,
0N/A (m01 * m12 - m11 * m02) / det,
0N/A (m10 * m02 - m00 * m12) / det,
0N/A (APPLY_SHEAR |
0N/A APPLY_SCALE |
0N/A APPLY_TRANSLATE));
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A det = m00 * m11 - m01 * m10;
0N/A if (Math.abs(det) <= Double.MIN_VALUE) {
0N/A throw new NoninvertibleTransformException("Determinant is "+
0N/A det);
0N/A }
0N/A return new AffineTransform( m11 / det, -m10 / det,
0N/A -m01 / det, m00 / det,
0N/A 0.0, 0.0,
0N/A (APPLY_SHEAR | APPLY_SCALE));
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A if (m01 == 0.0 || m10 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A return new AffineTransform( 0.0, 1.0 / m01,
0N/A 1.0 / m10, 0.0,
0N/A -m12 / m10, -m02 / m01,
0N/A (APPLY_SHEAR | APPLY_TRANSLATE));
0N/A case (APPLY_SHEAR):
0N/A if (m01 == 0.0 || m10 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A return new AffineTransform(0.0, 1.0 / m01,
0N/A 1.0 / m10, 0.0,
0N/A 0.0, 0.0,
0N/A (APPLY_SHEAR));
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A if (m00 == 0.0 || m11 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A return new AffineTransform( 1.0 / m00, 0.0,
0N/A 0.0, 1.0 / m11,
0N/A -m02 / m00, -m12 / m11,
0N/A (APPLY_SCALE | APPLY_TRANSLATE));
0N/A case (APPLY_SCALE):
0N/A if (m00 == 0.0 || m11 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A return new AffineTransform(1.0 / m00, 0.0,
0N/A 0.0, 1.0 / m11,
0N/A 0.0, 0.0,
0N/A (APPLY_SCALE));
0N/A case (APPLY_TRANSLATE):
0N/A return new AffineTransform( 1.0, 0.0,
0N/A 0.0, 1.0,
0N/A -m02, -m12,
0N/A (APPLY_TRANSLATE));
0N/A case (APPLY_IDENTITY):
0N/A return new AffineTransform();
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Sets this transform to the inverse of itself.
0N/A * The inverse transform Tx' of this transform Tx
0N/A * maps coordinates transformed by Tx back
0N/A * to their original coordinates.
0N/A * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)).
0N/A * <p>
0N/A * If this transform maps all coordinates onto a point or a line
0N/A * then it will not have an inverse, since coordinates that do
0N/A * not lie on the destination point or line will not have an inverse
0N/A * mapping.
0N/A * The <code>getDeterminant</code> method can be used to determine if this
0N/A * transform has no inverse, in which case an exception will be
0N/A * thrown if the <code>invert</code> method is called.
0N/A * @see #getDeterminant
0N/A * @exception NoninvertibleTransformException
0N/A * if the matrix cannot be inverted.
0N/A * @since 1.6
0N/A */
0N/A public void invert()
0N/A throws NoninvertibleTransformException
0N/A {
0N/A double M00, M01, M02;
0N/A double M10, M11, M12;
0N/A double det;
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M01 = m01; M02 = m02;
0N/A M10 = m10; M11 = m11; M12 = m12;
0N/A det = M00 * M11 - M01 * M10;
0N/A if (Math.abs(det) <= Double.MIN_VALUE) {
0N/A throw new NoninvertibleTransformException("Determinant is "+
0N/A det);
0N/A }
0N/A m00 = M11 / det;
0N/A m10 = -M10 / det;
0N/A m01 = -M01 / det;
0N/A m11 = M00 / det;
0N/A m02 = (M01 * M12 - M11 * M02) / det;
0N/A m12 = (M10 * M02 - M00 * M12) / det;
0N/A break;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A M00 = m00; M01 = m01;
0N/A M10 = m10; M11 = m11;
0N/A det = M00 * M11 - M01 * M10;
0N/A if (Math.abs(det) <= Double.MIN_VALUE) {
0N/A throw new NoninvertibleTransformException("Determinant is "+
0N/A det);
0N/A }
0N/A m00 = M11 / det;
0N/A m10 = -M10 / det;
0N/A m01 = -M01 / det;
0N/A m11 = M00 / det;
0N/A // m02 = 0.0;
0N/A // m12 = 0.0;
0N/A break;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A M01 = m01; M02 = m02;
0N/A M10 = m10; M12 = m12;
0N/A if (M01 == 0.0 || M10 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A // m00 = 0.0;
0N/A m10 = 1.0 / M01;
0N/A m01 = 1.0 / M10;
0N/A // m11 = 0.0;
0N/A m02 = -M12 / M10;
0N/A m12 = -M02 / M01;
0N/A break;
0N/A case (APPLY_SHEAR):
0N/A M01 = m01;
0N/A M10 = m10;
0N/A if (M01 == 0.0 || M10 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A // m00 = 0.0;
0N/A m10 = 1.0 / M01;
0N/A m01 = 1.0 / M10;
0N/A // m11 = 0.0;
0N/A // m02 = 0.0;
0N/A // m12 = 0.0;
0N/A break;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M02 = m02;
0N/A M11 = m11; M12 = m12;
0N/A if (M00 == 0.0 || M11 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A m00 = 1.0 / M00;
0N/A // m10 = 0.0;
0N/A // m01 = 0.0;
0N/A m11 = 1.0 / M11;
0N/A m02 = -M02 / M00;
0N/A m12 = -M12 / M11;
0N/A break;
0N/A case (APPLY_SCALE):
0N/A M00 = m00;
0N/A M11 = m11;
0N/A if (M00 == 0.0 || M11 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A m00 = 1.0 / M00;
0N/A // m10 = 0.0;
0N/A // m01 = 0.0;
0N/A m11 = 1.0 / M11;
0N/A // m02 = 0.0;
0N/A // m12 = 0.0;
0N/A break;
0N/A case (APPLY_TRANSLATE):
0N/A // m00 = 1.0;
0N/A // m10 = 0.0;
0N/A // m01 = 0.0;
0N/A // m11 = 1.0;
0N/A m02 = -m02;
0N/A m12 = -m12;
0N/A break;
0N/A case (APPLY_IDENTITY):
0N/A // m00 = 1.0;
0N/A // m10 = 0.0;
0N/A // m01 = 0.0;
0N/A // m11 = 1.0;
0N/A // m02 = 0.0;
0N/A // m12 = 0.0;
0N/A break;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Transforms the specified <code>ptSrc</code> and stores the result
0N/A * in <code>ptDst</code>.
0N/A * If <code>ptDst</code> is <code>null</code>, a new {@link Point2D}
0N/A * object is allocated and then the result of the transformation is
0N/A * stored in this object.
0N/A * In either case, <code>ptDst</code>, which contains the
0N/A * transformed point, is returned for convenience.
0N/A * If <code>ptSrc</code> and <code>ptDst</code> are the same
0N/A * object, the input point is correctly overwritten with
0N/A * the transformed point.
0N/A * @param ptSrc the specified <code>Point2D</code> to be transformed
0N/A * @param ptDst the specified <code>Point2D</code> that stores the
0N/A * result of transforming <code>ptSrc</code>
0N/A * @return the <code>ptDst</code> after transforming
0N/A * <code>ptSrc</code> and stroring the result in <code>ptDst</code>.
0N/A * @since 1.2
0N/A */
0N/A public Point2D transform(Point2D ptSrc, Point2D ptDst) {
0N/A if (ptDst == null) {
0N/A if (ptSrc instanceof Point2D.Double) {
0N/A ptDst = new Point2D.Double();
0N/A } else {
0N/A ptDst = new Point2D.Float();
0N/A }
0N/A }
0N/A // Copy source coords into local variables in case src == dst
0N/A double x = ptSrc.getX();
0N/A double y = ptSrc.getY();
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A ptDst.setLocation(x * m00 + y * m01 + m02,
0N/A x * m10 + y * m11 + m12);
0N/A return ptDst;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
0N/A return ptDst;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A ptDst.setLocation(y * m01 + m02, x * m10 + m12);
0N/A return ptDst;
0N/A case (APPLY_SHEAR):
0N/A ptDst.setLocation(y * m01, x * m10);
0N/A return ptDst;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A ptDst.setLocation(x * m00 + m02, y * m11 + m12);
0N/A return ptDst;
0N/A case (APPLY_SCALE):
0N/A ptDst.setLocation(x * m00, y * m11);
0N/A return ptDst;
0N/A case (APPLY_TRANSLATE):
0N/A ptDst.setLocation(x + m02, y + m12);
0N/A return ptDst;
0N/A case (APPLY_IDENTITY):
0N/A ptDst.setLocation(x, y);
0N/A return ptDst;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Transforms an array of point objects by this transform.
0N/A * If any element of the <code>ptDst</code> array is
0N/A * <code>null</code>, a new <code>Point2D</code> object is allocated
0N/A * and stored into that element before storing the results of the
0N/A * transformation.
0N/A * <p>
0N/A * Note that this method does not take any precautions to
0N/A * avoid problems caused by storing results into <code>Point2D</code>
0N/A * objects that will be used as the source for calculations
0N/A * further down the source array.
0N/A * This method does guarantee that if a specified <code>Point2D</code>
0N/A * object is both the source and destination for the same single point
0N/A * transform operation then the results will not be stored until
0N/A * the calculations are complete to avoid storing the results on
0N/A * top of the operands.
0N/A * If, however, the destination <code>Point2D</code> object for one
0N/A * operation is the same object as the source <code>Point2D</code>
0N/A * object for another operation further down the source array then
0N/A * the original coordinates in that point are overwritten before
0N/A * they can be converted.
0N/A * @param ptSrc the array containing the source point objects
0N/A * @param ptDst the array into which the transform point objects are
0N/A * returned
0N/A * @param srcOff the offset to the first point object to be
0N/A * transformed in the source array
0N/A * @param dstOff the offset to the location of the first
0N/A * transformed point object that is stored in the destination array
0N/A * @param numPts the number of point objects to be transformed
0N/A * @since 1.2
0N/A */
0N/A public void transform(Point2D[] ptSrc, int srcOff,
0N/A Point2D[] ptDst, int dstOff,
0N/A int numPts) {
0N/A int state = this.state;
0N/A while (--numPts >= 0) {
0N/A // Copy source coords into local variables in case src == dst
0N/A Point2D src = ptSrc[srcOff++];
0N/A double x = src.getX();
0N/A double y = src.getY();
0N/A Point2D dst = ptDst[dstOff++];
0N/A if (dst == null) {
0N/A if (src instanceof Point2D.Double) {
0N/A dst = new Point2D.Double();
0N/A } else {
0N/A dst = new Point2D.Float();
0N/A }
0N/A ptDst[dstOff - 1] = dst;
0N/A }
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A dst.setLocation(x * m00 + y * m01 + m02,
0N/A x * m10 + y * m11 + m12);
0N/A break;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A dst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
0N/A break;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A dst.setLocation(y * m01 + m02, x * m10 + m12);
0N/A break;
0N/A case (APPLY_SHEAR):
0N/A dst.setLocation(y * m01, x * m10);
0N/A break;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A dst.setLocation(x * m00 + m02, y * m11 + m12);
0N/A break;
0N/A case (APPLY_SCALE):
0N/A dst.setLocation(x * m00, y * m11);
0N/A break;
0N/A case (APPLY_TRANSLATE):
0N/A dst.setLocation(x + m02, y + m12);
0N/A break;
0N/A case (APPLY_IDENTITY):
0N/A dst.setLocation(x, y);
0N/A break;
0N/A }
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Transforms an array of floating point coordinates by this transform.
0N/A * The two coordinate array sections can be exactly the same or
0N/A * can be overlapping sections of the same array without affecting the
0N/A * validity of the results.
0N/A * This method ensures that no source coordinates are overwritten by a
0N/A * previous operation before they can be transformed.
0N/A * The coordinates are stored in the arrays starting at the specified
0N/A * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
0N/A * @param srcPts the array containing the source point coordinates.
0N/A * Each point is stored as a pair of x,&nbsp;y coordinates.
0N/A * @param dstPts the array into which the transformed point coordinates
0N/A * are returned. Each point is stored as a pair of x,&nbsp;y
0N/A * coordinates.
0N/A * @param srcOff the offset to the first point to be transformed
0N/A * in the source array
0N/A * @param dstOff the offset to the location of the first
0N/A * transformed point that is stored in the destination array
0N/A * @param numPts the number of points to be transformed
0N/A * @since 1.2
0N/A */
0N/A public void transform(float[] srcPts, int srcOff,
0N/A float[] dstPts, int dstOff,
0N/A int numPts) {
0N/A double M00, M01, M02, M10, M11, M12; // For caching
0N/A if (dstPts == srcPts &&
0N/A dstOff > srcOff && dstOff < srcOff + numPts * 2)
0N/A {
0N/A // If the arrays overlap partially with the destination higher
0N/A // than the source and we transform the coordinates normally
0N/A // we would overwrite some of the later source coordinates
0N/A // with results of previous transformations.
0N/A // To get around this we use arraycopy to copy the points
0N/A // to their final destination with correct overwrite
0N/A // handling and then transform them in place in the new
0N/A // safer location.
0N/A System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
0N/A // srcPts = dstPts; // They are known to be equal.
0N/A srcOff = dstOff;
0N/A }
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M01 = m01; M02 = m02;
0N/A M10 = m10; M11 = m11; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
0N/A dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A M00 = m00; M01 = m01;
0N/A M10 = m10; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (float) (M00 * x + M01 * y);
0N/A dstPts[dstOff++] = (float) (M10 * x + M11 * y);
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A M01 = m01; M02 = m02;
0N/A M10 = m10; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
0N/A dstPts[dstOff++] = (float) (M10 * x + M12);
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR):
0N/A M01 = m01; M10 = m10;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
0N/A dstPts[dstOff++] = (float) (M10 * x);
0N/A }
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M02 = m02;
0N/A M11 = m11; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
0N/A dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
0N/A }
0N/A return;
0N/A case (APPLY_SCALE):
0N/A M00 = m00; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
0N/A dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
0N/A }
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A M02 = m02; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
0N/A dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
0N/A }
0N/A return;
0N/A case (APPLY_IDENTITY):
0N/A if (srcPts != dstPts || srcOff != dstOff) {
0N/A System.arraycopy(srcPts, srcOff, dstPts, dstOff,
0N/A numPts * 2);
0N/A }
0N/A return;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Transforms an array of double precision coordinates by this transform.
0N/A * The two coordinate array sections can be exactly the same or
0N/A * can be overlapping sections of the same array without affecting the
0N/A * validity of the results.
0N/A * This method ensures that no source coordinates are
0N/A * overwritten by a previous operation before they can be transformed.
0N/A * The coordinates are stored in the arrays starting at the indicated
0N/A * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
0N/A * @param srcPts the array containing the source point coordinates.
0N/A * Each point is stored as a pair of x,&nbsp;y coordinates.
0N/A * @param dstPts the array into which the transformed point
0N/A * coordinates are returned. Each point is stored as a pair of
0N/A * x,&nbsp;y coordinates.
0N/A * @param srcOff the offset to the first point to be transformed
0N/A * in the source array
0N/A * @param dstOff the offset to the location of the first
0N/A * transformed point that is stored in the destination array
0N/A * @param numPts the number of point objects to be transformed
0N/A * @since 1.2
0N/A */
0N/A public void transform(double[] srcPts, int srcOff,
0N/A double[] dstPts, int dstOff,
0N/A int numPts) {
0N/A double M00, M01, M02, M10, M11, M12; // For caching
0N/A if (dstPts == srcPts &&
0N/A dstOff > srcOff && dstOff < srcOff + numPts * 2)
0N/A {
0N/A // If the arrays overlap partially with the destination higher
0N/A // than the source and we transform the coordinates normally
0N/A // we would overwrite some of the later source coordinates
0N/A // with results of previous transformations.
0N/A // To get around this we use arraycopy to copy the points
0N/A // to their final destination with correct overwrite
0N/A // handling and then transform them in place in the new
0N/A // safer location.
0N/A System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
0N/A // srcPts = dstPts; // They are known to be equal.
0N/A srcOff = dstOff;
0N/A }
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M01 = m01; M02 = m02;
0N/A M10 = m10; M11 = m11; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = M00 * x + M01 * y + M02;
0N/A dstPts[dstOff++] = M10 * x + M11 * y + M12;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A M00 = m00; M01 = m01;
0N/A M10 = m10; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = M00 * x + M01 * y;
0N/A dstPts[dstOff++] = M10 * x + M11 * y;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A M01 = m01; M02 = m02;
0N/A M10 = m10; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
0N/A dstPts[dstOff++] = M10 * x + M12;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR):
0N/A M01 = m01; M10 = m10;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = M01 * srcPts[srcOff++];
0N/A dstPts[dstOff++] = M10 * x;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M02 = m02;
0N/A M11 = m11; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
0N/A dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE):
0N/A M00 = m00; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = M00 * srcPts[srcOff++];
0N/A dstPts[dstOff++] = M11 * srcPts[srcOff++];
0N/A }
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A M02 = m02; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = srcPts[srcOff++] + M02;
0N/A dstPts[dstOff++] = srcPts[srcOff++] + M12;
0N/A }
0N/A return;
0N/A case (APPLY_IDENTITY):
0N/A if (srcPts != dstPts || srcOff != dstOff) {
0N/A System.arraycopy(srcPts, srcOff, dstPts, dstOff,
0N/A numPts * 2);
0N/A }
0N/A return;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Transforms an array of floating point coordinates by this transform
0N/A * and stores the results into an array of doubles.
0N/A * The coordinates are stored in the arrays starting at the specified
0N/A * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
0N/A * @param srcPts the array containing the source point coordinates.
0N/A * Each point is stored as a pair of x,&nbsp;y coordinates.
0N/A * @param dstPts the array into which the transformed point coordinates
0N/A * are returned. Each point is stored as a pair of x,&nbsp;y
0N/A * coordinates.
0N/A * @param srcOff the offset to the first point to be transformed
0N/A * in the source array
0N/A * @param dstOff the offset to the location of the first
0N/A * transformed point that is stored in the destination array
0N/A * @param numPts the number of points to be transformed
0N/A * @since 1.2
0N/A */
0N/A public void transform(float[] srcPts, int srcOff,
0N/A double[] dstPts, int dstOff,
0N/A int numPts) {
0N/A double M00, M01, M02, M10, M11, M12; // For caching
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M01 = m01; M02 = m02;
0N/A M10 = m10; M11 = m11; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = M00 * x + M01 * y + M02;
0N/A dstPts[dstOff++] = M10 * x + M11 * y + M12;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A M00 = m00; M01 = m01;
0N/A M10 = m10; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = M00 * x + M01 * y;
0N/A dstPts[dstOff++] = M10 * x + M11 * y;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A M01 = m01; M02 = m02;
0N/A M10 = m10; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02;
0N/A dstPts[dstOff++] = M10 * x + M12;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR):
0N/A M01 = m01; M10 = m10;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = M01 * srcPts[srcOff++];
0N/A dstPts[dstOff++] = M10 * x;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M02 = m02;
0N/A M11 = m11; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02;
0N/A dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE):
0N/A M00 = m00; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = M00 * srcPts[srcOff++];
0N/A dstPts[dstOff++] = M11 * srcPts[srcOff++];
0N/A }
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A M02 = m02; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = srcPts[srcOff++] + M02;
0N/A dstPts[dstOff++] = srcPts[srcOff++] + M12;
0N/A }
0N/A return;
0N/A case (APPLY_IDENTITY):
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = srcPts[srcOff++];
0N/A dstPts[dstOff++] = srcPts[srcOff++];
0N/A }
0N/A return;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Transforms an array of double precision coordinates by this transform
0N/A * and stores the results into an array of floats.
0N/A * The coordinates are stored in the arrays starting at the specified
0N/A * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
0N/A * @param srcPts the array containing the source point coordinates.
0N/A * Each point is stored as a pair of x,&nbsp;y coordinates.
0N/A * @param dstPts the array into which the transformed point
0N/A * coordinates are returned. Each point is stored as a pair of
0N/A * x,&nbsp;y coordinates.
0N/A * @param srcOff the offset to the first point to be transformed
0N/A * in the source array
0N/A * @param dstOff the offset to the location of the first
0N/A * transformed point that is stored in the destination array
0N/A * @param numPts the number of point objects to be transformed
0N/A * @since 1.2
0N/A */
0N/A public void transform(double[] srcPts, int srcOff,
0N/A float[] dstPts, int dstOff,
0N/A int numPts) {
0N/A double M00, M01, M02, M10, M11, M12; // For caching
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M01 = m01; M02 = m02;
0N/A M10 = m10; M11 = m11; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02);
0N/A dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12);
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A M00 = m00; M01 = m01;
0N/A M10 = m10; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (float) (M00 * x + M01 * y);
0N/A dstPts[dstOff++] = (float) (M10 * x + M11 * y);
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A M01 = m01; M02 = m02;
0N/A M10 = m10; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02);
0N/A dstPts[dstOff++] = (float) (M10 * x + M12);
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR):
0N/A M01 = m01; M10 = m10;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]);
0N/A dstPts[dstOff++] = (float) (M10 * x);
0N/A }
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M02 = m02;
0N/A M11 = m11; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02);
0N/A dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12);
0N/A }
0N/A return;
0N/A case (APPLY_SCALE):
0N/A M00 = m00; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]);
0N/A dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]);
0N/A }
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A M02 = m02; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02);
0N/A dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12);
0N/A }
0N/A return;
0N/A case (APPLY_IDENTITY):
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = (float) (srcPts[srcOff++]);
0N/A dstPts[dstOff++] = (float) (srcPts[srcOff++]);
0N/A }
0N/A return;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Inverse transforms the specified <code>ptSrc</code> and stores the
0N/A * result in <code>ptDst</code>.
0N/A * If <code>ptDst</code> is <code>null</code>, a new
0N/A * <code>Point2D</code> object is allocated and then the result of the
0N/A * transform is stored in this object.
0N/A * In either case, <code>ptDst</code>, which contains the transformed
0N/A * point, is returned for convenience.
0N/A * If <code>ptSrc</code> and <code>ptDst</code> are the same
0N/A * object, the input point is correctly overwritten with the
0N/A * transformed point.
0N/A * @param ptSrc the point to be inverse transformed
0N/A * @param ptDst the resulting transformed point
0N/A * @return <code>ptDst</code>, which contains the result of the
0N/A * inverse transform.
0N/A * @exception NoninvertibleTransformException if the matrix cannot be
0N/A * inverted.
0N/A * @since 1.2
0N/A */
0N/A public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst)
0N/A throws NoninvertibleTransformException
0N/A {
0N/A if (ptDst == null) {
0N/A if (ptSrc instanceof Point2D.Double) {
0N/A ptDst = new Point2D.Double();
0N/A } else {
0N/A ptDst = new Point2D.Float();
0N/A }
0N/A }
0N/A // Copy source coords into local variables in case src == dst
0N/A double x = ptSrc.getX();
0N/A double y = ptSrc.getY();
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A x -= m02;
0N/A y -= m12;
0N/A /* NOBREAK */
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A double det = m00 * m11 - m01 * m10;
0N/A if (Math.abs(det) <= Double.MIN_VALUE) {
0N/A throw new NoninvertibleTransformException("Determinant is "+
0N/A det);
0N/A }
0N/A ptDst.setLocation((x * m11 - y * m01) / det,
0N/A (y * m00 - x * m10) / det);
0N/A return ptDst;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A x -= m02;
0N/A y -= m12;
0N/A /* NOBREAK */
0N/A case (APPLY_SHEAR):
0N/A if (m01 == 0.0 || m10 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A ptDst.setLocation(y / m10, x / m01);
0N/A return ptDst;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A x -= m02;
0N/A y -= m12;
0N/A /* NOBREAK */
0N/A case (APPLY_SCALE):
0N/A if (m00 == 0.0 || m11 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A ptDst.setLocation(x / m00, y / m11);
0N/A return ptDst;
0N/A case (APPLY_TRANSLATE):
0N/A ptDst.setLocation(x - m02, y - m12);
0N/A return ptDst;
0N/A case (APPLY_IDENTITY):
0N/A ptDst.setLocation(x, y);
0N/A return ptDst;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Inverse transforms an array of double precision coordinates by
0N/A * this transform.
0N/A * The two coordinate array sections can be exactly the same or
0N/A * can be overlapping sections of the same array without affecting the
0N/A * validity of the results.
0N/A * This method ensures that no source coordinates are
0N/A * overwritten by a previous operation before they can be transformed.
0N/A * The coordinates are stored in the arrays starting at the specified
0N/A * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
0N/A * @param srcPts the array containing the source point coordinates.
0N/A * Each point is stored as a pair of x,&nbsp;y coordinates.
0N/A * @param dstPts the array into which the transformed point
0N/A * coordinates are returned. Each point is stored as a pair of
0N/A * x,&nbsp;y coordinates.
0N/A * @param srcOff the offset to the first point to be transformed
0N/A * in the source array
0N/A * @param dstOff the offset to the location of the first
0N/A * transformed point that is stored in the destination array
0N/A * @param numPts the number of point objects to be transformed
0N/A * @exception NoninvertibleTransformException if the matrix cannot be
0N/A * inverted.
0N/A * @since 1.2
0N/A */
0N/A public void inverseTransform(double[] srcPts, int srcOff,
0N/A double[] dstPts, int dstOff,
0N/A int numPts)
0N/A throws NoninvertibleTransformException
0N/A {
0N/A double M00, M01, M02, M10, M11, M12; // For caching
0N/A double det;
0N/A if (dstPts == srcPts &&
0N/A dstOff > srcOff && dstOff < srcOff + numPts * 2)
0N/A {
0N/A // If the arrays overlap partially with the destination higher
0N/A // than the source and we transform the coordinates normally
0N/A // we would overwrite some of the later source coordinates
0N/A // with results of previous transformations.
0N/A // To get around this we use arraycopy to copy the points
0N/A // to their final destination with correct overwrite
0N/A // handling and then transform them in place in the new
0N/A // safer location.
0N/A System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
0N/A // srcPts = dstPts; // They are known to be equal.
0N/A srcOff = dstOff;
0N/A }
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M01 = m01; M02 = m02;
0N/A M10 = m10; M11 = m11; M12 = m12;
0N/A det = M00 * M11 - M01 * M10;
0N/A if (Math.abs(det) <= Double.MIN_VALUE) {
0N/A throw new NoninvertibleTransformException("Determinant is "+
0N/A det);
0N/A }
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++] - M02;
0N/A double y = srcPts[srcOff++] - M12;
0N/A dstPts[dstOff++] = (x * M11 - y * M01) / det;
0N/A dstPts[dstOff++] = (y * M00 - x * M10) / det;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A M00 = m00; M01 = m01;
0N/A M10 = m10; M11 = m11;
0N/A det = M00 * M11 - M01 * M10;
0N/A if (Math.abs(det) <= Double.MIN_VALUE) {
0N/A throw new NoninvertibleTransformException("Determinant is "+
0N/A det);
0N/A }
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = (x * M11 - y * M01) / det;
0N/A dstPts[dstOff++] = (y * M00 - x * M10) / det;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A M01 = m01; M02 = m02;
0N/A M10 = m10; M12 = m12;
0N/A if (M01 == 0.0 || M10 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++] - M02;
0N/A dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10;
0N/A dstPts[dstOff++] = x / M01;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR):
0N/A M01 = m01; M10 = m10;
0N/A if (M01 == 0.0 || M10 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = srcPts[srcOff++] / M10;
0N/A dstPts[dstOff++] = x / M01;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A M00 = m00; M02 = m02;
0N/A M11 = m11; M12 = m12;
0N/A if (M00 == 0.0 || M11 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00;
0N/A dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE):
0N/A M00 = m00; M11 = m11;
0N/A if (M00 == 0.0 || M11 == 0.0) {
0N/A throw new NoninvertibleTransformException("Determinant is 0");
0N/A }
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = srcPts[srcOff++] / M00;
0N/A dstPts[dstOff++] = srcPts[srcOff++] / M11;
0N/A }
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A M02 = m02; M12 = m12;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = srcPts[srcOff++] - M02;
0N/A dstPts[dstOff++] = srcPts[srcOff++] - M12;
0N/A }
0N/A return;
0N/A case (APPLY_IDENTITY):
0N/A if (srcPts != dstPts || srcOff != dstOff) {
0N/A System.arraycopy(srcPts, srcOff, dstPts, dstOff,
0N/A numPts * 2);
0N/A }
0N/A return;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Transforms the relative distance vector specified by
0N/A * <code>ptSrc</code> and stores the result in <code>ptDst</code>.
0N/A * A relative distance vector is transformed without applying the
0N/A * translation components of the affine transformation matrix
0N/A * using the following equations:
0N/A * <pre>
0N/A * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ]
0N/A * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ]
0N/A * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ]
0N/A * </pre>
0N/A * If <code>ptDst</code> is <code>null</code>, a new
0N/A * <code>Point2D</code> object is allocated and then the result of the
0N/A * transform is stored in this object.
0N/A * In either case, <code>ptDst</code>, which contains the
0N/A * transformed point, is returned for convenience.
0N/A * If <code>ptSrc</code> and <code>ptDst</code> are the same object,
0N/A * the input point is correctly overwritten with the transformed
0N/A * point.
0N/A * @param ptSrc the distance vector to be delta transformed
0N/A * @param ptDst the resulting transformed distance vector
0N/A * @return <code>ptDst</code>, which contains the result of the
0N/A * transformation.
0N/A * @since 1.2
0N/A */
0N/A public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) {
0N/A if (ptDst == null) {
0N/A if (ptSrc instanceof Point2D.Double) {
0N/A ptDst = new Point2D.Double();
0N/A } else {
0N/A ptDst = new Point2D.Float();
0N/A }
0N/A }
0N/A // Copy source coords into local variables in case src == dst
0N/A double x = ptSrc.getX();
0N/A double y = ptSrc.getY();
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11);
0N/A return ptDst;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR):
0N/A ptDst.setLocation(y * m01, x * m10);
0N/A return ptDst;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SCALE):
0N/A ptDst.setLocation(x * m00, y * m11);
0N/A return ptDst;
0N/A case (APPLY_TRANSLATE):
0N/A case (APPLY_IDENTITY):
0N/A ptDst.setLocation(x, y);
0N/A return ptDst;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Transforms an array of relative distance vectors by this
0N/A * transform.
0N/A * A relative distance vector is transformed without applying the
0N/A * translation components of the affine transformation matrix
0N/A * using the following equations:
0N/A * <pre>
0N/A * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ]
0N/A * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ]
0N/A * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ]
0N/A * </pre>
0N/A * The two coordinate array sections can be exactly the same or
0N/A * can be overlapping sections of the same array without affecting the
0N/A * validity of the results.
0N/A * This method ensures that no source coordinates are
0N/A * overwritten by a previous operation before they can be transformed.
0N/A * The coordinates are stored in the arrays starting at the indicated
0N/A * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>.
0N/A * @param srcPts the array containing the source distance vectors.
0N/A * Each vector is stored as a pair of relative x,&nbsp;y coordinates.
0N/A * @param dstPts the array into which the transformed distance vectors
0N/A * are returned. Each vector is stored as a pair of relative
0N/A * x,&nbsp;y coordinates.
0N/A * @param srcOff the offset to the first vector to be transformed
0N/A * in the source array
0N/A * @param dstOff the offset to the location of the first
0N/A * transformed vector that is stored in the destination array
0N/A * @param numPts the number of vector coordinate pairs to be
0N/A * transformed
0N/A * @since 1.2
0N/A */
0N/A public void deltaTransform(double[] srcPts, int srcOff,
0N/A double[] dstPts, int dstOff,
0N/A int numPts) {
0N/A double M00, M01, M10, M11; // For caching
0N/A if (dstPts == srcPts &&
0N/A dstOff > srcOff && dstOff < srcOff + numPts * 2)
0N/A {
0N/A // If the arrays overlap partially with the destination higher
0N/A // than the source and we transform the coordinates normally
0N/A // we would overwrite some of the later source coordinates
0N/A // with results of previous transformations.
0N/A // To get around this we use arraycopy to copy the points
0N/A // to their final destination with correct overwrite
0N/A // handling and then transform them in place in the new
0N/A // safer location.
0N/A System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2);
0N/A // srcPts = dstPts; // They are known to be equal.
0N/A srcOff = dstOff;
0N/A }
0N/A switch (state) {
0N/A default:
0N/A stateError();
0N/A /* NOTREACHED */
0N/A case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR | APPLY_SCALE):
0N/A M00 = m00; M01 = m01;
0N/A M10 = m10; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A double y = srcPts[srcOff++];
0N/A dstPts[dstOff++] = x * M00 + y * M01;
0N/A dstPts[dstOff++] = x * M10 + y * M11;
0N/A }
0N/A return;
0N/A case (APPLY_SHEAR | APPLY_TRANSLATE):
0N/A case (APPLY_SHEAR):
0N/A M01 = m01; M10 = m10;
0N/A while (--numPts >= 0) {
0N/A double x = srcPts[srcOff++];
0N/A dstPts[dstOff++] = srcPts[srcOff++] * M01;
0N/A dstPts[dstOff++] = x * M10;
0N/A }
0N/A return;
0N/A case (APPLY_SCALE | APPLY_TRANSLATE):
0N/A case (APPLY_SCALE):
0N/A M00 = m00; M11 = m11;
0N/A while (--numPts >= 0) {
0N/A dstPts[dstOff++] = srcPts[srcOff++] * M00;
0N/A dstPts[dstOff++] = srcPts[srcOff++] * M11;
0N/A }
0N/A return;
0N/A case (APPLY_TRANSLATE):
0N/A case (APPLY_IDENTITY):
0N/A if (srcPts != dstPts || srcOff != dstOff) {
0N/A System.arraycopy(srcPts, srcOff, dstPts, dstOff,
0N/A numPts * 2);
0N/A }
0N/A return;
0N/A }
0N/A
0N/A /* NOTREACHED */
0N/A }
0N/A
0N/A /**
0N/A * Returns a new {@link Shape} object defined by the geometry of the
0N/A * specified <code>Shape</code> after it has been transformed by
0N/A * this transform.
0N/A * @param pSrc the specified <code>Shape</code> object to be
0N/A * transformed by this transform.
0N/A * @return a new <code>Shape</code> object that defines the geometry
0N/A * of the transformed <code>Shape</code>, or null if {@code pSrc} is null.
0N/A * @since 1.2
0N/A */
0N/A public Shape createTransformedShape(Shape pSrc) {
0N/A if (pSrc == null) {
0N/A return null;
0N/A }
0N/A return new Path2D.Double(pSrc, this);
0N/A }
0N/A
0N/A // Round values to sane precision for printing
0N/A // Note that Math.sin(Math.PI) has an error of about 10^-16
0N/A private static double _matround(double matval) {
0N/A return Math.rint(matval * 1E15) / 1E15;
0N/A }
0N/A
0N/A /**
0N/A * Returns a <code>String</code> that represents the value of this
0N/A * {@link Object}.
0N/A * @return a <code>String</code> representing the value of this
0N/A * <code>Object</code>.
0N/A * @since 1.2
0N/A */
0N/A public String toString() {
0N/A return ("AffineTransform[["
0N/A + _matround(m00) + ", "
0N/A + _matround(m01) + ", "
0N/A + _matround(m02) + "], ["
0N/A + _matround(m10) + ", "
0N/A + _matround(m11) + ", "
0N/A + _matround(m12) + "]]");
0N/A }
0N/A
0N/A /**
0N/A * Returns <code>true</code> if this <code>AffineTransform</code> is
0N/A * an identity transform.
0N/A * @return <code>true</code> if this <code>AffineTransform</code> is
0N/A * an identity transform; <code>false</code> otherwise.
0N/A * @since 1.2
0N/A */
0N/A public boolean isIdentity() {
0N/A return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY));
0N/A }
0N/A
0N/A /**
0N/A * Returns a copy of this <code>AffineTransform</code> object.
0N/A * @return an <code>Object</code> that is a copy of this
0N/A * <code>AffineTransform</code> object.
0N/A * @since 1.2
0N/A */
0N/A public Object clone() {
0N/A try {
0N/A return super.clone();
0N/A } catch (CloneNotSupportedException e) {
0N/A // this shouldn't happen, since we are Cloneable
0N/A throw new InternalError();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the hashcode for this transform.
0N/A * @return a hash code for this transform.
0N/A * @since 1.2
0N/A */
0N/A public int hashCode() {
0N/A long bits = Double.doubleToLongBits(m00);
0N/A bits = bits * 31 + Double.doubleToLongBits(m01);
0N/A bits = bits * 31 + Double.doubleToLongBits(m02);
0N/A bits = bits * 31 + Double.doubleToLongBits(m10);
0N/A bits = bits * 31 + Double.doubleToLongBits(m11);
0N/A bits = bits * 31 + Double.doubleToLongBits(m12);
0N/A return (((int) bits) ^ ((int) (bits >> 32)));
0N/A }
0N/A
0N/A /**
0N/A * Returns <code>true</code> if this <code>AffineTransform</code>
0N/A * represents the same affine coordinate transform as the specified
0N/A * argument.
0N/A * @param obj the <code>Object</code> to test for equality with this
0N/A * <code>AffineTransform</code>
0N/A * @return <code>true</code> if <code>obj</code> equals this
0N/A * <code>AffineTransform</code> object; <code>false</code> otherwise.
0N/A * @since 1.2
0N/A */
0N/A public boolean equals(Object obj) {
0N/A if (!(obj instanceof AffineTransform)) {
0N/A return false;
0N/A }
0N/A
0N/A AffineTransform a = (AffineTransform)obj;
0N/A
0N/A return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) &&
0N/A (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12));
0N/A }
0N/A
0N/A /* Serialization support. A readObject method is neccessary because
0N/A * the state field is part of the implementation of this particular
0N/A * AffineTransform and not part of the public specification. The
0N/A * state variable's value needs to be recalculated on the fly by the
0N/A * readObject method as it is in the 6-argument matrix constructor.
0N/A */
0N/A
0N/A /*
0N/A * JDK 1.2 serialVersionUID
0N/A */
0N/A private static final long serialVersionUID = 1330973210523860834L;
0N/A
0N/A private void writeObject(java.io.ObjectOutputStream s)
0N/A throws java.lang.ClassNotFoundException, java.io.IOException
0N/A {
0N/A s.defaultWriteObject();
0N/A }
0N/A
0N/A private void readObject(java.io.ObjectInputStream s)
0N/A throws java.lang.ClassNotFoundException, java.io.IOException
0N/A {
0N/A s.defaultReadObject();
0N/A updateState();
0N/A }
0N/A}