/* * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.himamis.retex.renderer.web.graphics; import java.awt.geom.NoninvertibleTransformException; import com.himamis.retex.renderer.share.platform.graphics.Transform; public class TransformW implements Transform { /* * This constant is only useful for the cached type field. It indicates that the type has been * decached and must be recalculated. */ private static final int TYPE_UNKNOWN = -1; /** * This constant indicates that the transform defined by this object is an identity transform. * An identity transform is one in which the output coordinates are always the same as the input * coordinates. If this transform is anything other than the identity transform, the type will * either be the constant GENERAL_TRANSFORM or a combination of the appropriate flag bits for * the various coordinate conversions that this transform performs. * * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType * @since 1.2 */ public static final int TYPE_IDENTITY = 0; /** * This flag bit indicates that the transform defined by this object performs a translation in * addition to the conversions indicated by other flag bits. A translation moves the coordinates * by a constant amount in x and y without changing the length or angle of vectors. * * @see #TYPE_IDENTITY * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType * @since 1.2 */ public static final int TYPE_TRANSLATION = 1; /** * This flag bit indicates that the transform defined by this object performs a uniform scale in * addition to the conversions indicated by other flag bits. A uniform scale multiplies the * length of vectors by the same amount in both the x and y directions without changing the * angle between vectors. This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag. * * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType * @since 1.2 */ public static final int TYPE_UNIFORM_SCALE = 2; /** * This flag bit indicates that the transform defined by this object performs a general scale in * addition to the conversions indicated by other flag bits. A general scale multiplies the * length of vectors by different amounts in the x and y directions without changing the angle * between perpendicular vectors. This flag bit is mutually exclusive with the * TYPE_UNIFORM_SCALE flag. * * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType * @since 1.2 */ public static final int TYPE_GENERAL_SCALE = 4; /** * This constant is a bit mask for any of the scale flag bits. * * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @since 1.2 */ public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE); /** * This flag bit indicates that the transform defined by this object performs a mirror image * flip about some axis which changes the normally right handed coordinate system into a left * handed system in addition to the conversions indicated by other flag bits. A right handed * coordinate system is one where the positive X axis rotates counterclockwise to overlay the * positive Y axis similar to the direction that the fingers on your right hand curl when you * stare end on at your thumb. A left handed coordinate system is one where the positive X axis * rotates clockwise to overlay the positive Y axis similar to the direction that the fingers on * your left hand curl. There is no mathematical way to determine the angle of the original * flipping or mirroring transformation since all angles of flip are identical given an * appropriate adjusting rotation. * * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType * @since 1.2 */ public static final int TYPE_FLIP = 64; /* * NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public circulation and the flag bits * could no longer be conveniently renumbered without introducing binary incompatibility in * outside code. */ /** * This flag bit indicates that the transform defined by this object performs a quadrant * rotation by some multiple of 90 degrees in addition to the conversions indicated by other * flag bits. A rotation changes the angles of vectors by the same amount regardless of the * original direction of the vector and without changing the length of the vector. This flag bit * is mutually exclusive with the TYPE_GENERAL_ROTATION flag. * * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType * @since 1.2 */ public static final int TYPE_QUADRANT_ROTATION = 8; /** * This flag bit indicates that the transform defined by this object performs a rotation by an * arbitrary angle in addition to the conversions indicated by other flag bits. A rotation * changes the angles of vectors by the same amount regardless of the original direction of the * vector and without changing the length of the vector. This flag bit is mutually exclusive * with the TYPE_QUADRANT_ROTATION flag. * * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType * @since 1.2 */ public static final int TYPE_GENERAL_ROTATION = 16; /** * This constant is a bit mask for any of the rotation flag bits. * * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @since 1.2 */ public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION); /** * This constant indicates that the transform defined by this object performs an arbitrary * conversion of the input coordinates. If this transform can be classified by any of the above * constants, the type will either be the constant TYPE_IDENTITY or a combination of the * appropriate flag bits for the various coordinate conversions that this transform performs. * * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #getType * @since 1.2 */ public static final int TYPE_GENERAL_TRANSFORM = 32; /** * This constant is used for the internal state variable to indicate that no calculations need * to be performed and that the source coordinates only need to be copied to their destinations * to complete the transformation equation of this transform. * * @see #APPLY_TRANSLATE * @see #APPLY_SCALE * @see #APPLY_SHEAR * @see #state */ static final int APPLY_IDENTITY = 0; /** * This constant is used for the internal state variable to indicate that the translation * components of the matrix (m02 and m12) need to be added to complete the transformation * equation of this transform. * * @see #APPLY_IDENTITY * @see #APPLY_SCALE * @see #APPLY_SHEAR * @see #state */ static final int APPLY_TRANSLATE = 1; /** * This constant is used for the internal state variable to indicate that the scaling components * of the matrix (m00 and m11) need to be factored in to complete the transformation equation of * this transform. If the APPLY_SHEAR bit is also set then it indicates that the scaling * components are not both 0.0. If the APPLY_SHEAR bit is not also set then it indicates that * the scaling components are not both 1.0. If neither the APPLY_SHEAR nor the APPLY_SCALE bits * are set then the scaling components are both 1.0, which means that the x and y components * contribute to the transformed coordinate, but they are not multiplied by any scaling factor. * * @see #APPLY_IDENTITY * @see #APPLY_TRANSLATE * @see #APPLY_SHEAR * @see #state */ static final int APPLY_SCALE = 2; /** * This constant is used for the internal state variable to indicate that the shearing * components of the matrix (m01 and m10) need to be factored in to complete the transformation * equation of this transform. The presence of this bit in the state variable changes the * interpretation of the APPLY_SCALE bit as indicated in its documentation. * * @see #APPLY_IDENTITY * @see #APPLY_TRANSLATE * @see #APPLY_SCALE * @see #state */ static final int APPLY_SHEAR = 4; /* * For methods which combine together the state of two separate transforms and dispatch based * upon the combination, these constants specify how far to shift one of the states so that the * two states are mutually non-interfering and provide constants for testing the bits of the * shifted (HI) state. The methods in this class use the convention that the state of "this" * transform is unshifted and the state of the "other" or "argument" transform is shifted (HI). */ private static final int HI_SHIFT = 3; private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT; private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT; private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT; private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT; /** * The X coordinate scaling element of the 3x3 affine transformation matrix. * * */ double m00; /** * The Y coordinate shearing element of the 3x3 affine transformation matrix. * * */ double m10; /** * The X coordinate shearing element of the 3x3 affine transformation matrix. * * */ double m01; /** * The Y coordinate scaling element of the 3x3 affine transformation matrix. * * */ double m11; /** * The X coordinate of the translation element of the 3x3 affine transformation matrix. * * */ double m02; /** * The Y coordinate of the translation element of the 3x3 affine transformation matrix. * * */ double m12; /** * This field keeps track of which components of the matrix need to be applied when performing a * transformation. * * @see #APPLY_IDENTITY * @see #APPLY_TRANSLATE * @see #APPLY_SCALE * @see #APPLY_SHEAR */ transient int state; /** * This field caches the current transformation type of the matrix. * * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #TYPE_UNKNOWN * @see #getType */ private transient int type; /** * Constructs a new <code>TransformW</code> representing the Identity transformation. * * @since 1.2 */ public TransformW() { m00 = m11 = 1.0; // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */ // state = APPLY_IDENTITY; /* Not needed. */ // type = TYPE_IDENTITY; /* Not needed. */ } /** * Constructs a new <code>TransformW</code> that is a copy of the specified * <code>TransformW</code> object. * * @param Tx the <code>TransformW</code> object to copy * @since 1.2 */ public TransformW(TransformW Tx) { this.m00 = Tx.m00; this.m10 = Tx.m10; this.m01 = Tx.m01; this.m11 = Tx.m11; this.m02 = Tx.m02; this.m12 = Tx.m12; this.state = Tx.state; this.type = Tx.type; } /** * Constructs a new <code>TransformW</code> from 6 double precision values representing the 6 * specifiable entries of the 3x3 transformation matrix. * * @param m00 the X coordinate scaling element of the 3x3 matrix * @param m10 the Y coordinate shearing element of the 3x3 matrix * @param m01 the X coordinate shearing element of the 3x3 matrix * @param m11 the Y coordinate scaling element of the 3x3 matrix * @param m02 the X coordinate translation element of the 3x3 matrix * @param m12 the Y coordinate translation element of the 3x3 matrix * @since 1.2 */ public TransformW(double m00, double m10, double m01, double m11, double m02, double m12) { this.m00 = m00; this.m10 = m10; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; updateState(); } /** * Constructs a new <code>TransformW</code> from an array of double precision values * representing either the 4 non-translation entries or the 6 specifiable entries of the 3x3 * transformation matrix. The values are retrieved from the array as * { m00 m10 m01 m11 [m02 m12]}. * * @param flatmatrix the double array containing the values to be set in the new * <code>TransformW</code> object. The length of the array is assumed to be at least 4. * If the length of the array is less than 6, only the first 4 values are taken. If the * length of the array is greater than 6, the first 6 values are taken. * @since 1.2 */ public TransformW(double[] flatmatrix) { m00 = flatmatrix[0]; m10 = flatmatrix[1]; m01 = flatmatrix[2]; m11 = flatmatrix[3]; if (flatmatrix.length > 5) { m02 = flatmatrix[4]; m12 = flatmatrix[5]; } updateState(); } /** * Returns a transform representing a translation transformation. The matrix representing the * returned transform is: * * <pre> * [ 1 0 tx ] * [ 0 1 ty ] * [ 0 0 1 ] * </pre> * * @param tx the distance by which coordinates are translated in the X axis direction * @param ty the distance by which coordinates are translated in the Y axis direction * @return an <code>TransformW</code> object that represents a translation transformation, * created with the specified vector. * @since 1.2 */ public static TransformW getTranslateInstance(double tx, double ty) { TransformW Tx = new TransformW(); Tx.setToTranslation(tx, ty); return Tx; } /** * Returns a transform representing a rotation transformation. The matrix representing the * returned transform is: * * <pre> * [ cos(theta) -sin(theta) 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 0 1 ] * </pre> * * Rotating by a positive angle theta rotates points on the positive X axis toward the positive * Y axis. Note also the discussion of <a href="#quadrantapproximation">Handling 90-Degree * Rotations</a> above. * * @param theta the angle of rotation measured in radians * @return an <code>TransformW</code> object that is a rotation transformation, created with the * specified angle of rotation. * @since 1.2 */ public static TransformW getRotateInstance(double theta) { TransformW Tx = new TransformW(); Tx.setToRotation(theta); return Tx; } /** * Returns a transform that rotates coordinates around an anchor point. This operation is * equivalent to translating the coordinates so that the anchor point is at the origin (S1), * then rotating them about the new origin (S2), and finally translating so that the * intermediate origin is restored to the coordinates of the original anchor point (S3). * <p> * This operation is equivalent to the following sequence of calls: * * <pre> * TransformW Tx = new TransformW(); * Tx.translate(anchorx, anchory); // S3: final translation * Tx.rotate(theta); // S2: rotate around anchor * Tx.translate(-anchorx, -anchory); // S1: translate anchor to origin * </pre> * * The matrix representing the returned transform is: * * <pre> * [ cos(theta) -sin(theta) x-x*cos+y*sin ] * [ sin(theta) cos(theta) y-x*sin-y*cos ] * [ 0 0 1 ] * </pre> * * Rotating by a positive angle theta rotates points on the positive X axis toward the positive * Y axis. Note also the discussion of <a href="#quadrantapproximation">Handling 90-Degree * Rotations</a> above. * * @param theta the angle of rotation measured in radians * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @return an <code>TransformW</code> object that rotates coordinates around the specified point * by the specified angle of rotation. * @since 1.2 */ public static TransformW getRotateInstance(double theta, double anchorx, double anchory) { TransformW Tx = new TransformW(); Tx.setToRotation(theta, anchorx, anchory); return Tx; } /** * Returns a transform that rotates coordinates according to a rotation vector. All coordinates * rotate about the origin by the same amount. The amount of rotation is such that coordinates * along the former positive X axis will subsequently align with the vector pointing from the * origin to the specified vector coordinates. If both <code>vecx</code> and <code>vecy</code> * are 0.0, an identity transform is returned. This operation is equivalent to calling: * * <pre> * TransformW.getRotateInstance(Math.atan2(vecy, vecx)); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector * @return an <code>TransformW</code> object that rotates coordinates according to the specified * rotation vector. * @since 1.6 */ public static TransformW getRotateInstance(double vecx, double vecy) { TransformW Tx = new TransformW(); Tx.setToRotation(vecx, vecy); return Tx; } /** * Returns a transform that rotates coordinates around an anchor point accordinate to a rotation * vector. All coordinates rotate about the specified anchor coordinates by the same amount. The * amount of rotation is such that coordinates along the former positive X axis will * subsequently align with the vector pointing from the origin to the specified vector * coordinates. If both <code>vecx</code> and <code>vecy</code> are 0.0, an identity transform * is returned. This operation is equivalent to calling: * * <pre> * TransformW.getRotateInstance(Math.atan2(vecy, vecx), anchorx, anchory); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @return an <code>TransformW</code> object that rotates coordinates around the specified point * according to the specified rotation vector. * @since 1.6 */ public static TransformW getRotateInstance(double vecx, double vecy, double anchorx, double anchory) { TransformW Tx = new TransformW(); Tx.setToRotation(vecx, vecy, anchorx, anchory); return Tx; } /** * Returns a transform that rotates coordinates by the specified number of quadrants. This * operation is equivalent to calling: * * <pre> * TransformW.getRotateInstance(numquadrants * Math.PI / 2.0); * </pre> * * Rotating by a positive number of quadrants rotates points on the positive X axis toward the * positive Y axis. * * @param numquadrants the number of 90 degree arcs to rotate by * @return an <code>TransformW</code> object that rotates coordinates by the specified number of * quadrants. * @since 1.6 */ public static TransformW getQuadrantRotateInstance(int numquadrants) { TransformW Tx = new TransformW(); Tx.setToQuadrantRotation(numquadrants); return Tx; } /** * Returns a transform that rotates coordinates by the specified number of quadrants around the * specified anchor point. This operation is equivalent to calling: * * <pre> * TransformW.getRotateInstance(numquadrants * Math.PI / 2.0, anchorx, anchory); * </pre> * * Rotating by a positive number of quadrants rotates points on the positive X axis toward the * positive Y axis. * * @param numquadrants the number of 90 degree arcs to rotate by * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @return an <code>TransformW</code> object that rotates coordinates by the specified number of * quadrants around the specified anchor point. * @since 1.6 */ public static TransformW getQuadrantRotateInstance(int numquadrants, double anchorx, double anchory) { TransformW Tx = new TransformW(); Tx.setToQuadrantRotation(numquadrants, anchorx, anchory); return Tx; } /** * Returns a transform representing a scaling transformation. The matrix representing the * returned transform is: * * <pre> * [ sx 0 0 ] * [ 0 sy 0 ] * [ 0 0 1 ] * </pre> * * @param sx the factor by which coordinates are scaled along the X axis direction * @param sy the factor by which coordinates are scaled along the Y axis direction * @return an <code>TransformW</code> object that scales coordinates by the specified factors. * @since 1.2 */ public static TransformW getScaleInstance(double sx, double sy) { TransformW Tx = new TransformW(); Tx.setToScale(sx, sy); return Tx; } /** * Returns a transform representing a shearing transformation. The matrix representing the * returned transform is: * * <pre> * [ 1 shx 0 ] * [ shy 1 0 ] * [ 0 0 1 ] * </pre> * * @param shx the multiplier by which coordinates are shifted in the direction of the positive X * axis as a factor of their Y coordinate * @param shy the multiplier by which coordinates are shifted in the direction of the positive Y * axis as a factor of their X coordinate * @return an <code>TransformW</code> object that shears coordinates by the specified * multipliers. * @since 1.2 */ public static TransformW getShearInstance(double shx, double shy) { TransformW Tx = new TransformW(); Tx.setToShear(shx, shy); return Tx; } /** * Retrieves the flag bits describing the conversion properties of this transform. The return * value is either one of the constants TYPE_IDENTITY or TYPE_GENERAL_TRANSFORM, or a * combination of the appriopriate flag bits. A valid combination of flag bits is an exclusive * OR operation that can combine the TYPE_TRANSLATION flag bit in addition to either of the * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits as well as either of the * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits. * * @return the OR combination of any of the indicated flags that apply to this transform * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @since 1.2 */ public int getType() { if (type == TYPE_UNKNOWN) { calculateType(); } return type; } /** * This is the utility function to calculate the flag bits when they have not been cached. * * @see #getType */ private void calculateType() { int ret = TYPE_IDENTITY; boolean sgn0, sgn1; double M0, M1, M2, M3; updateState(); switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): ret = TYPE_TRANSLATION; //$FALL-THROUGH$ case (APPLY_SHEAR | APPLY_SCALE): if ((M0 = m00) * (M2 = m01) + (M3 = m10) * (M1 = m11) != 0) { // Transformed unit vectors are not perpendicular... this.type = TYPE_GENERAL_TRANSFORM; return; } sgn0 = (M0 >= 0.0); sgn1 = (M1 >= 0.0); if (sgn0 == sgn1) { // sgn(M0) == sgn(M1) therefore sgn(M2) == -sgn(M3) // This is the "unflipped" (right-handed) state if (M0 != M1 || M2 != -M3) { ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE); } else if (M0 * M1 - M2 * M3 != 1.0) { ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE); } else { ret |= TYPE_GENERAL_ROTATION; } } else { // sgn(M0) == -sgn(M1) therefore sgn(M2) == sgn(M3) // This is the "flipped" (left-handed) state if (M0 != -M1 || M2 != M3) { ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE); } else if (M0 * M1 - M2 * M3 != 1.0) { ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE); } else { ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP); } } break; case (APPLY_SHEAR | APPLY_TRANSLATE): ret = TYPE_TRANSLATION; //$FALL-THROUGH$ case (APPLY_SHEAR): sgn0 = ((M0 = m01) >= 0.0); sgn1 = ((M1 = m10) >= 0.0); if (sgn0 != sgn1) { // Different signs - simple 90 degree rotation if (M0 != -M1) { ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); } else if (M0 != 1.0 && M0 != -1.0) { ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); } else { ret |= TYPE_QUADRANT_ROTATION; } } else { // Same signs - 90 degree rotation plus an axis flip too if (M0 == M1) { ret |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_UNIFORM_SCALE); } else { ret |= (TYPE_QUADRANT_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE); } } break; case (APPLY_SCALE | APPLY_TRANSLATE): ret = TYPE_TRANSLATION; //$FALL-THROUGH$ case (APPLY_SCALE): sgn0 = ((M0 = m00) >= 0.0); sgn1 = ((M1 = m11) >= 0.0); if (sgn0 == sgn1) { if (sgn0) { // Both scaling factors non-negative - simple scale // Note: APPLY_SCALE implies M0, M1 are not both 1 if (M0 == M1) { ret |= TYPE_UNIFORM_SCALE; } else { ret |= TYPE_GENERAL_SCALE; } } else { // Both scaling factors negative - 180 degree rotation if (M0 != M1) { ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); } else if (M0 != -1.0) { ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); } else { ret |= TYPE_QUADRANT_ROTATION; } } } else { // Scaling factor signs different - flip about some axis if (M0 == -M1) { if (M0 == 1.0 || M0 == -1.0) { ret |= TYPE_FLIP; } else { ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE); } } else { ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE); } } break; case (APPLY_TRANSLATE): ret = TYPE_TRANSLATION; break; case (APPLY_IDENTITY): break; } this.type = ret; } /** * Returns the determinant of the matrix representation of the transform. The determinant is * useful both to determine if the transform can be inverted and to get a single value * representing the combined X and Y scaling of the transform. * <p> * If the determinant is non-zero, then this transform is invertible and the various methods * that depend on the inverse transform do not need to throw a * {@link NoninvertibleTransformException}. If the determinant is zero then this transform can * not be inverted since the transform maps all input coordinates onto a line or a point. If the * determinant is near enough to zero then inverse transform operations might not carry enough * precision to produce meaningful results. * <p> * If this transform represents a uniform scale, as indicated by the <code>getType</code> method * then the determinant also represents the square of the uniform scale factor by which all of * the points are expanded from or contracted towards the origin. If this transform represents a * non-uniform scale or more general transform then the determinant is not likely to represent a * value useful for any purpose other than determining if inverse transforms are possible. * <p> * Mathematically, the determinant is calculated using the formula: * * <pre> * | m00 m01 m02 | * | m10 m11 m12 | = m00 * m11 - m01 * m10 * | 0 0 1 | * </pre> * * @return the determinant of the matrix used to transform the coordinates. * @see #getType * @see #createInverse * @see #inverseTransform * @see #TYPE_UNIFORM_SCALE * @since 1.2 */ public double getDeterminant() { switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): return m00 * m11 - m01 * m10; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): return -(m01 * m10); case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): return m00 * m11; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): return 1.0; } } /** * Manually recalculates the state of the transform when the matrix changes too much to predict * the effects on the state. The following table specifies what the various settings of the * state field say about the values of the corresponding matrix element fields. Note that the * rules governing the SCALE fields are slightly different depending on whether the SHEAR flag * is also set. * * <pre> * SCALE SHEAR TRANSLATE * m00/m11 m01/m10 m02/m12 * * IDENTITY 1.0 0.0 0.0 * TRANSLATE (TR) 1.0 0.0 not both 0.0 * SCALE (SC) not both 1.0 0.0 0.0 * TR | SC not both 1.0 0.0 not both 0.0 * SHEAR (SH) 0.0 not both 0.0 0.0 * TR | SH 0.0 not both 0.0 not both 0.0 * SC | SH not both 0.0 not both 0.0 0.0 * TR | SC | SH not both 0.0 not both 0.0 not both 0.0 * </pre> */ void updateState() { if (m01 == 0.0 && m10 == 0.0) { if (m00 == 1.0 && m11 == 1.0) { if (m02 == 0.0 && m12 == 0.0) { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } else { state = APPLY_TRANSLATE; type = TYPE_TRANSLATION; } } else { if (m02 == 0.0 && m12 == 0.0) { state = APPLY_SCALE; type = TYPE_UNKNOWN; } else { state = (APPLY_SCALE | APPLY_TRANSLATE); type = TYPE_UNKNOWN; } } } else { if (m00 == 0.0 && m11 == 0.0) { if (m02 == 0.0 && m12 == 0.0) { state = APPLY_SHEAR; type = TYPE_UNKNOWN; } else { state = (APPLY_SHEAR | APPLY_TRANSLATE); type = TYPE_UNKNOWN; } } else { if (m02 == 0.0 && m12 == 0.0) { state = (APPLY_SHEAR | APPLY_SCALE); type = TYPE_UNKNOWN; } else { state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE); type = TYPE_UNKNOWN; } } } } /* * Convenience method used internally to throw exceptions when a case was forgotten in a switch * statement. */ private static void stateError() { throw new IllegalStateException("missing case in transform state switch"); } /** * Retrieves the 6 specifiable values in the 3x3 affine transformation matrix and places them * into an array of double precisions values. The values are stored in the array as * { m00 m10 m01 m11 m02 m12 }. An array of 4 doubles can * also be specified, in which case only the first four elements representing the non-transform * parts of the array are retrieved and the values are stored into the array as * { m00 m10 m01 m11 } * * @param flatmatrix the double array used to store the returned values. * @see #getScaleX * @see #getScaleY * @see #getShearX * @see #getShearY * @see #getTranslateX * @see #getTranslateY * @since 1.2 */ public void getMatrix(double[] flatmatrix) { flatmatrix[0] = m00; flatmatrix[1] = m10; flatmatrix[2] = m01; flatmatrix[3] = m11; if (flatmatrix.length > 5) { flatmatrix[4] = m02; flatmatrix[5] = m12; } } /** * Returns the X coordinate scaling element (m00) of the 3x3 affine transformation matrix. * * @return a double value that is the X coordinate of the scaling element of the affine * transformation matrix. * @see #getMatrix * @since 1.2 */ @Override public double getScaleX() { return m00; } /** * Returns the Y coordinate scaling element (m11) of the 3x3 affine transformation matrix. * * @return a double value that is the Y coordinate of the scaling element of the affine * transformation matrix. * @see #getMatrix * @since 1.2 */ @Override public double getScaleY() { return m11; } /** * Returns the X coordinate shearing element (m01) of the 3x3 affine transformation matrix. * * @return a double value that is the X coordinate of the shearing element of the affine * transformation matrix. * @see #getMatrix * @since 1.2 */ @Override public double getShearX() { return m01; } /** * Returns the Y coordinate shearing element (m10) of the 3x3 affine transformation matrix. * * @return a double value that is the Y coordinate of the shearing element of the affine * transformation matrix. * @see #getMatrix * @since 1.2 */ @Override public double getShearY() { return m10; } /** * Returns the X coordinate of the translation element (m02) of the 3x3 affine transformation * matrix. * * @return a double value that is the X coordinate of the translation element of the affine * transformation matrix. * @see #getMatrix * @since 1.2 */ @Override public double getTranslateX() { return m02; } /** * Returns the Y coordinate of the translation element (m12) of the 3x3 affine transformation * matrix. * * @return a double value that is the Y coordinate of the translation element of the affine * transformation matrix. * @see #getMatrix * @since 1.2 */ @Override public double getTranslateY() { return m12; } /** * Concatenates this transform with a translation transformation. This is equivalent to calling * concatenate(T), where T is an <code>TransformW</code> represented by the following matrix: * * <pre> * [ 1 0 tx ] * [ 0 1 ty ] * [ 0 0 1 ] * </pre> * * @param tx the distance by which coordinates are translated in the X axis direction * @param ty the distance by which coordinates are translated in the Y axis direction * @since 1.2 */ @Override public void translate(double tx, double ty) { switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): m02 = tx * m00 + ty * m01 + m02; m12 = tx * m10 + ty * m11 + m12; if (m02 == 0.0 && m12 == 0.0) { state = APPLY_SHEAR | APPLY_SCALE; if (type != TYPE_UNKNOWN) { type -= TYPE_TRANSLATION; } } return; case (APPLY_SHEAR | APPLY_SCALE): m02 = tx * m00 + ty * m01; m12 = tx * m10 + ty * m11; if (m02 != 0.0 || m12 != 0.0) { state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } return; case (APPLY_SHEAR | APPLY_TRANSLATE): m02 = ty * m01 + m02; m12 = tx * m10 + m12; if (m02 == 0.0 && m12 == 0.0) { state = APPLY_SHEAR; if (type != TYPE_UNKNOWN) { type -= TYPE_TRANSLATION; } } return; case (APPLY_SHEAR): m02 = ty * m01; m12 = tx * m10; if (m02 != 0.0 || m12 != 0.0) { state = APPLY_SHEAR | APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } return; case (APPLY_SCALE | APPLY_TRANSLATE): m02 = tx * m00 + m02; m12 = ty * m11 + m12; if (m02 == 0.0 && m12 == 0.0) { state = APPLY_SCALE; if (type != TYPE_UNKNOWN) { type -= TYPE_TRANSLATION; } } return; case (APPLY_SCALE): m02 = tx * m00; m12 = ty * m11; if (m02 != 0.0 || m12 != 0.0) { state = APPLY_SCALE | APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } return; case (APPLY_TRANSLATE): m02 = tx + m02; m12 = ty + m12; if (m02 == 0.0 && m12 == 0.0) { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } return; case (APPLY_IDENTITY): m02 = tx; m12 = ty; if (tx != 0.0 || ty != 0.0) { state = APPLY_TRANSLATE; type = TYPE_TRANSLATION; } return; } } // Utility methods to optimize rotate methods. // These tables translate the flags during predictable quadrant // rotations where the shear and scale values are swapped and negated. private static final int rot90conversion[] = { /* IDENTITY => */APPLY_SHEAR, /* TRANSLATE (TR) => */APPLY_SHEAR | APPLY_TRANSLATE, /* SCALE (SC) => */APPLY_SHEAR, /* SC | TR => */APPLY_SHEAR | APPLY_TRANSLATE, /* SHEAR (SH) => */APPLY_SCALE, /* SH | TR => */APPLY_SCALE | APPLY_TRANSLATE, /* SH | SC => */APPLY_SHEAR | APPLY_SCALE, /* SH | SC | TR => */APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE, }; private final void rotate90() { double M0 = m00; m00 = m01; m01 = -M0; M0 = m10; m10 = m11; m11 = -M0; int state = rot90conversion[this.state]; if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && m00 == 1.0 && m11 == 1.0) { state -= APPLY_SCALE; } this.state = state; type = TYPE_UNKNOWN; } private final void rotate180() { m00 = -m00; m11 = -m11; int state = this.state; if ((state & (APPLY_SHEAR)) != 0) { // If there was a shear, then this rotation has no // effect on the state. m01 = -m01; m10 = -m10; } else { // No shear means the SCALE state may toggle when // m00 and m11 are negated. if (m00 == 1.0 && m11 == 1.0) { this.state = state & ~APPLY_SCALE; } else { this.state = state | APPLY_SCALE; } } type = TYPE_UNKNOWN; } private final void rotate270() { double M0 = m00; m00 = -m01; m01 = M0; M0 = m10; m10 = -m11; m11 = M0; int state = rot90conversion[this.state]; if ((state & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && m00 == 1.0 && m11 == 1.0) { state -= APPLY_SCALE; } this.state = state; type = TYPE_UNKNOWN; } /** * Concatenates this transform with a rotation transformation. This is equivalent to calling * concatenate(R), where R is an <code>TransformW</code> represented by the following matrix: * * <pre> * [ cos(theta) -sin(theta) 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 0 1 ] * </pre> * * Rotating by a positive angle theta rotates points on the positive X axis toward the positive * Y axis. Note also the discussion of <a href="#quadrantapproximation">Handling 90-Degree * Rotations</a> above. * * @param theta the angle of rotation measured in radians * @since 1.2 */ public void rotate(double theta) { double sin = Math.sin(theta); if (sin == 1.0) { rotate90(); } else if (sin == -1.0) { rotate270(); } else { double cos = Math.cos(theta); if (cos == -1.0) { rotate180(); } else if (cos != 1.0) { double M0, M1; M0 = m00; M1 = m01; m00 = cos * M0 + sin * M1; m01 = -sin * M0 + cos * M1; M0 = m10; M1 = m11; m10 = cos * M0 + sin * M1; m11 = -sin * M0 + cos * M1; updateState(); } } } /** * Concatenates this transform with a transform that rotates coordinates around an anchor point. * This operation is equivalent to translating the coordinates so that the anchor point is at * the origin (S1), then rotating them about the new origin (S2), and finally translating so * that the intermediate origin is restored to the coordinates of the original anchor point * (S3). * <p> * This operation is equivalent to the following sequence of calls: * * <pre> * translate(anchorx, anchory); // S3: final translation * rotate(theta); // S2: rotate around anchor * translate(-anchorx, -anchory); // S1: translate anchor to origin * </pre> * * Rotating by a positive angle theta rotates points on the positive X axis toward the positive * Y axis. Note also the discussion of <a href="#quadrantapproximation">Handling 90-Degree * Rotations</a> above. * * @param theta the angle of rotation measured in radians * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @since 1.2 */ public void rotate(double theta, double anchorx, double anchory) { // REMIND: Simple for now - optimize later translate(anchorx, anchory); rotate(theta); translate(-anchorx, -anchory); } /** * Concatenates this transform with a transform that rotates coordinates according to a rotation * vector. All coordinates rotate about the origin by the same amount. The amount of rotation is * such that coordinates along the former positive X axis will subsequently align with the * vector pointing from the origin to the specified vector coordinates. If both * <code>vecx</code> and <code>vecy</code> are 0.0, no additional rotation is added to this * transform. This operation is equivalent to calling: * * <pre> * rotate(Math.atan2(vecy, vecx)); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector * @since 1.6 */ public void rotate(double vecx, double vecy) { if (vecy == 0.0) { if (vecx < 0.0) { rotate180(); } // If vecx > 0.0 - no rotation // If vecx == 0.0 - undefined rotation - treat as no rotation } else if (vecx == 0.0) { if (vecy > 0.0) { rotate90(); } else { // vecy must be < 0.0 rotate270(); } } else { double len = Math.sqrt(vecx * vecx + vecy * vecy); double sin = vecy / len; double cos = vecx / len; double M0, M1; M0 = m00; M1 = m01; m00 = cos * M0 + sin * M1; m01 = -sin * M0 + cos * M1; M0 = m10; M1 = m11; m10 = cos * M0 + sin * M1; m11 = -sin * M0 + cos * M1; updateState(); } } /** * Concatenates this transform with a transform that rotates coordinates around an anchor point * according to a rotation vector. All coordinates rotate about the specified anchor coordinates * by the same amount. The amount of rotation is such that coordinates along the former positive * X axis will subsequently align with the vector pointing from the origin to the specified * vector coordinates. If both <code>vecx</code> and <code>vecy</code> are 0.0, the transform is * not modified in any way. This method is equivalent to calling: * * <pre> * rotate(Math.atan2(vecy, vecx), anchorx, anchory); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @since 1.6 */ public void rotate(double vecx, double vecy, double anchorx, double anchory) { // REMIND: Simple for now - optimize later translate(anchorx, anchory); rotate(vecx, vecy); translate(-anchorx, -anchory); } /** * Concatenates this transform with a transform that rotates coordinates by the specified number * of quadrants. This is equivalent to calling: * * <pre> * rotate(numquadrants * Math.PI / 2.0); * </pre> * * Rotating by a positive number of quadrants rotates points on the positive X axis toward the * positive Y axis. * * @param numquadrants the number of 90 degree arcs to rotate by * @since 1.6 */ public void quadrantRotate(int numquadrants) { switch (numquadrants & 3) { case 0: break; case 1: rotate90(); break; case 2: rotate180(); break; case 3: rotate270(); break; } } /** * Concatenates this transform with a transform that rotates coordinates by the specified number * of quadrants around the specified anchor point. This method is equivalent to calling: * * <pre> * rotate(numquadrants * Math.PI / 2.0, anchorx, anchory); * </pre> * * Rotating by a positive number of quadrants rotates points on the positive X axis toward the * positive Y axis. * * @param numquadrants the number of 90 degree arcs to rotate by * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @since 1.6 */ public void quadrantRotate(int numquadrants, double anchorx, double anchory) { switch (numquadrants & 3) { case 0: return; case 1: m02 += anchorx * (m00 - m01) + anchory * (m01 + m00); m12 += anchorx * (m10 - m11) + anchory * (m11 + m10); rotate90(); break; case 2: m02 += anchorx * (m00 + m00) + anchory * (m01 + m01); m12 += anchorx * (m10 + m10) + anchory * (m11 + m11); rotate180(); break; case 3: m02 += anchorx * (m00 + m01) + anchory * (m01 - m00); m12 += anchorx * (m10 + m11) + anchory * (m11 - m10); rotate270(); break; } if (m02 == 0.0 && m12 == 0.0) { state &= ~APPLY_TRANSLATE; } else { state |= APPLY_TRANSLATE; } } /** * Concatenates this transform with a scaling transformation. This is equivalent to calling * concatenate(S), where S is an <code>TransformW</code> represented by the following matrix: * * <pre> * [ sx 0 0 ] * [ 0 sy 0 ] * [ 0 0 1 ] * </pre> * * @param sx the factor by which coordinates are scaled along the X axis direction * @param sy the factor by which coordinates are scaled along the Y axis direction * @since 1.2 */ @Override public void scale(double sx, double sy) { int state = this.state; switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): m00 *= sx; m11 *= sy; //$FALL-THROUGH$ case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): m01 *= sy; m10 *= sx; if (m01 == 0 && m10 == 0) { state &= APPLY_TRANSLATE; if (m00 == 1.0 && m11 == 1.0) { this.type = (state == APPLY_IDENTITY ? TYPE_IDENTITY : TYPE_TRANSLATION); } else { state |= APPLY_SCALE; this.type = TYPE_UNKNOWN; } this.state = state; } return; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): m00 *= sx; m11 *= sy; if (m00 == 1.0 && m11 == 1.0) { this.state = (state &= APPLY_TRANSLATE); this.type = (state == APPLY_IDENTITY ? TYPE_IDENTITY : TYPE_TRANSLATION); } else { this.type = TYPE_UNKNOWN; } return; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): m00 = sx; m11 = sy; if (sx != 1.0 || sy != 1.0) { this.state = state | APPLY_SCALE; this.type = TYPE_UNKNOWN; } return; } } /** * Concatenates this transform with a shearing transformation. This is equivalent to calling * concatenate(SH), where SH is an <code>TransformW</code> represented by the following matrix: * * <pre> * [ 1 shx 0 ] * [ shy 1 0 ] * [ 0 0 1 ] * </pre> * * @param shx the multiplier by which coordinates are shifted in the direction of the positive X * axis as a factor of their Y coordinate * @param shy the multiplier by which coordinates are shifted in the direction of the positive Y * axis as a factor of their X coordinate * @since 1.2 */ @Override public void shear(double shx, double shy) { int state = this.state; switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): double M0, M1; M0 = m00; M1 = m01; m00 = M0 + M1 * shy; m01 = M0 * shx + M1; M0 = m10; M1 = m11; m10 = M0 + M1 * shy; m11 = M0 * shx + M1; updateState(); return; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): m00 = m01 * shy; m11 = m10 * shx; if (m00 != 0.0 || m11 != 0.0) { this.state = state | APPLY_SCALE; } this.type = TYPE_UNKNOWN; return; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): m01 = m00 * shx; m10 = m11 * shy; if (m01 != 0.0 || m10 != 0.0) { this.state = state | APPLY_SHEAR; } this.type = TYPE_UNKNOWN; return; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): m01 = shx; m10 = shy; if (m01 != 0.0 || m10 != 0.0) { this.state = state | APPLY_SCALE | APPLY_SHEAR; this.type = TYPE_UNKNOWN; } return; } } /** * Resets this transform to the Identity transform. * * @since 1.2 */ public void setToIdentity() { m00 = m11 = 1.0; m10 = m01 = m02 = m12 = 0.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; } /** * Sets this transform to a translation transformation. The matrix representing this transform * becomes: * * <pre> * [ 1 0 tx ] * [ 0 1 ty ] * [ 0 0 1 ] * </pre> * * @param tx the distance by which coordinates are translated in the X axis direction * @param ty the distance by which coordinates are translated in the Y axis direction * @since 1.2 */ public void setToTranslation(double tx, double ty) { m00 = 1.0; m10 = 0.0; m01 = 0.0; m11 = 1.0; m02 = tx; m12 = ty; if (tx != 0.0 || ty != 0.0) { state = APPLY_TRANSLATE; type = TYPE_TRANSLATION; } else { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } } /** * Sets this transform to a rotation transformation. The matrix representing this transform * becomes: * * <pre> * [ cos(theta) -sin(theta) 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 0 1 ] * </pre> * * Rotating by a positive angle theta rotates points on the positive X axis toward the positive * Y axis. Note also the discussion of <a href="#quadrantapproximation">Handling 90-Degree * Rotations</a> above. * * @param theta the angle of rotation measured in radians * @since 1.2 */ public void setToRotation(double theta) { double sin = Math.sin(theta); double cos; if (sin == 1.0 || sin == -1.0) { cos = 0.0; state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; } else { cos = Math.cos(theta); if (cos == -1.0) { sin = 0.0; state = APPLY_SCALE; type = TYPE_QUADRANT_ROTATION; } else if (cos == 1.0) { sin = 0.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; } else { state = APPLY_SHEAR | APPLY_SCALE; type = TYPE_GENERAL_ROTATION; } } m00 = cos; m10 = sin; m01 = -sin; m11 = cos; m02 = 0.0; m12 = 0.0; } /** * Sets this transform to a translated rotation transformation. This operation is equivalent to * translating the coordinates so that the anchor point is at the origin (S1), then rotating * them about the new origin (S2), and finally translating so that the intermediate origin is * restored to the coordinates of the original anchor point (S3). * <p> * This operation is equivalent to the following sequence of calls: * * <pre> * setToTranslation(anchorx, anchory); // S3: final translation * rotate(theta); // S2: rotate around anchor * translate(-anchorx, -anchory); // S1: translate anchor to origin * </pre> * * The matrix representing this transform becomes: * * <pre> * [ cos(theta) -sin(theta) x-x*cos+y*sin ] * [ sin(theta) cos(theta) y-x*sin-y*cos ] * [ 0 0 1 ] * </pre> * * Rotating by a positive angle theta rotates points on the positive X axis toward the positive * Y axis. Note also the discussion of <a href="#quadrantapproximation">Handling 90-Degree * Rotations</a> above. * * @param theta the angle of rotation measured in radians * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @since 1.2 */ public void setToRotation(double theta, double anchorx, double anchory) { setToRotation(theta); double sin = m10; double oneMinusCos = 1.0 - m00; m02 = anchorx * oneMinusCos + anchory * sin; m12 = anchory * oneMinusCos - anchorx * sin; if (m02 != 0.0 || m12 != 0.0) { state |= APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } } /** * Sets this transform to a rotation transformation that rotates coordinates according to a * rotation vector. All coordinates rotate about the origin by the same amount. The amount of * rotation is such that coordinates along the former positive X axis will subsequently align * with the vector pointing from the origin to the specified vector coordinates. If both * <code>vecx</code> and <code>vecy</code> are 0.0, the transform is set to an identity * transform. This operation is equivalent to calling: * * <pre> * setToRotation(Math.atan2(vecy, vecx)); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector * @since 1.6 */ public void setToRotation(double vecx, double vecy) { double sin, cos; if (vecy == 0) { sin = 0.0; if (vecx < 0.0) { cos = -1.0; state = APPLY_SCALE; type = TYPE_QUADRANT_ROTATION; } else { cos = 1.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; } } else if (vecx == 0) { cos = 0.0; sin = (vecy > 0.0) ? 1.0 : -1.0; state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; } else { double len = Math.sqrt(vecx * vecx + vecy * vecy); cos = vecx / len; sin = vecy / len; state = APPLY_SHEAR | APPLY_SCALE; type = TYPE_GENERAL_ROTATION; } m00 = cos; m10 = sin; m01 = -sin; m11 = cos; m02 = 0.0; m12 = 0.0; } /** * Sets this transform to a rotation transformation that rotates coordinates around an anchor * point according to a rotation vector. All coordinates rotate about the specified anchor * coordinates by the same amount. The amount of rotation is such that coordinates along the * former positive X axis will subsequently align with the vector pointing from the origin to * the specified vector coordinates. If both <code>vecx</code> and <code>vecy</code> are 0.0, * the transform is set to an identity transform. This operation is equivalent to calling: * * <pre> * setToTranslation(Math.atan2(vecy, vecx), anchorx, anchory); * </pre> * * @param vecx the X coordinate of the rotation vector * @param vecy the Y coordinate of the rotation vector * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @since 1.6 */ public void setToRotation(double vecx, double vecy, double anchorx, double anchory) { setToRotation(vecx, vecy); double sin = m10; double oneMinusCos = 1.0 - m00; m02 = anchorx * oneMinusCos + anchory * sin; m12 = anchory * oneMinusCos - anchorx * sin; if (m02 != 0.0 || m12 != 0.0) { state |= APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } } /** * Sets this transform to a rotation transformation that rotates coordinates by the specified * number of quadrants. This operation is equivalent to calling: * * <pre> * setToRotation(numquadrants * Math.PI / 2.0); * </pre> * * Rotating by a positive number of quadrants rotates points on the positive X axis toward the * positive Y axis. * * @param numquadrants the number of 90 degree arcs to rotate by * @since 1.6 */ public void setToQuadrantRotation(int numquadrants) { switch (numquadrants & 3) { case 0: m00 = 1.0; m10 = 0.0; m01 = 0.0; m11 = 1.0; m02 = 0.0; m12 = 0.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; break; case 1: m00 = 0.0; m10 = 1.0; m01 = -1.0; m11 = 0.0; m02 = 0.0; m12 = 0.0; state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; break; case 2: m00 = -1.0; m10 = 0.0; m01 = 0.0; m11 = -1.0; m02 = 0.0; m12 = 0.0; state = APPLY_SCALE; type = TYPE_QUADRANT_ROTATION; break; case 3: m00 = 0.0; m10 = -1.0; m01 = 1.0; m11 = 0.0; m02 = 0.0; m12 = 0.0; state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; break; } } /** * Sets this transform to a translated rotation transformation that rotates coordinates by the * specified number of quadrants around the specified anchor point. This operation is equivalent * to calling: * * <pre> * setToRotation(numquadrants * Math.PI / 2.0, anchorx, anchory); * </pre> * * Rotating by a positive number of quadrants rotates points on the positive X axis toward the * positive Y axis. * * @param numquadrants the number of 90 degree arcs to rotate by * @param anchorx the X coordinate of the rotation anchor point * @param anchory the Y coordinate of the rotation anchor point * @since 1.6 */ public void setToQuadrantRotation(int numquadrants, double anchorx, double anchory) { switch (numquadrants & 3) { case 0: m00 = 1.0; m10 = 0.0; m01 = 0.0; m11 = 1.0; m02 = 0.0; m12 = 0.0; state = APPLY_IDENTITY; type = TYPE_IDENTITY; break; case 1: m00 = 0.0; m10 = 1.0; m01 = -1.0; m11 = 0.0; m02 = anchorx + anchory; m12 = anchory - anchorx; if (m02 == 0.0 && m12 == 0.0) { state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; } else { state = APPLY_SHEAR | APPLY_TRANSLATE; type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; } break; case 2: m00 = -1.0; m10 = 0.0; m01 = 0.0; m11 = -1.0; m02 = anchorx + anchorx; m12 = anchory + anchory; if (m02 == 0.0 && m12 == 0.0) { state = APPLY_SCALE; type = TYPE_QUADRANT_ROTATION; } else { state = APPLY_SCALE | APPLY_TRANSLATE; type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; } break; case 3: m00 = 0.0; m10 = -1.0; m01 = 1.0; m11 = 0.0; m02 = anchorx - anchory; m12 = anchory + anchorx; if (m02 == 0.0 && m12 == 0.0) { state = APPLY_SHEAR; type = TYPE_QUADRANT_ROTATION; } else { state = APPLY_SHEAR | APPLY_TRANSLATE; type = TYPE_QUADRANT_ROTATION | TYPE_TRANSLATION; } break; } } /** * Sets this transform to a scaling transformation. The matrix representing this transform * becomes: * * <pre> * [ sx 0 0 ] * [ 0 sy 0 ] * [ 0 0 1 ] * </pre> * * @param sx the factor by which coordinates are scaled along the X axis direction * @param sy the factor by which coordinates are scaled along the Y axis direction * @since 1.2 */ public void setToScale(double sx, double sy) { m00 = sx; m10 = 0.0; m01 = 0.0; m11 = sy; m02 = 0.0; m12 = 0.0; if (sx != 1.0 || sy != 1.0) { state = APPLY_SCALE; type = TYPE_UNKNOWN; } else { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } } /** * Sets this transform to a shearing transformation. The matrix representing this transform * becomes: * * <pre> * [ 1 shx 0 ] * [ shy 1 0 ] * [ 0 0 1 ] * </pre> * * @param shx the multiplier by which coordinates are shifted in the direction of the positive X * axis as a factor of their Y coordinate * @param shy the multiplier by which coordinates are shifted in the direction of the positive Y * axis as a factor of their X coordinate * @since 1.2 */ public void setToShear(double shx, double shy) { m00 = 1.0; m01 = shx; m10 = shy; m11 = 1.0; m02 = 0.0; m12 = 0.0; if (shx != 0.0 || shy != 0.0) { state = (APPLY_SHEAR | APPLY_SCALE); type = TYPE_UNKNOWN; } else { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } } /** * Sets this transform to a copy of the transform in the specified <code>TransformW</code> * object. * * @param Tx the <code>TransformW</code> object from which to copy the transform * @since 1.2 */ public void setTransform(TransformW Tx) { this.m00 = Tx.m00; this.m10 = Tx.m10; this.m01 = Tx.m01; this.m11 = Tx.m11; this.m02 = Tx.m02; this.m12 = Tx.m12; this.state = Tx.state; this.type = Tx.type; } /** * Sets this transform to the matrix specified by the 6 double precision values. * * @param m00 the X coordinate scaling element of the 3x3 matrix * @param m10 the Y coordinate shearing element of the 3x3 matrix * @param m01 the X coordinate shearing element of the 3x3 matrix * @param m11 the Y coordinate scaling element of the 3x3 matrix * @param m02 the X coordinate translation element of the 3x3 matrix * @param m12 the Y coordinate translation element of the 3x3 matrix * @since 1.2 */ public void setTransform(double m00, double m10, double m01, double m11, double m02, double m12) { this.m00 = m00; this.m10 = m10; this.m01 = m01; this.m11 = m11; this.m02 = m02; this.m12 = m12; updateState(); } /** * Concatenates an <code>TransformW</code> <code>Tx</code> to this <code>TransformW</code> Cx in * the most commonly useful way to provide a new user space that is mapped to the former user * space by <code>Tx</code>. Cx is updated to perform the combined transformation. Transforming * a point p by the updated transform Cx' is equivalent to first transforming p by * <code>Tx</code> and then transforming the result by the original transform Cx like this: * Cx'(p) = Cx(Tx(p)) In matrix notation, if this transform Cx is represented by the matrix * [this] and <code>Tx</code> is represented by the matrix [Tx] then this method does the * following: * * <pre> * [this] = [this] x [Tx] * </pre> * * @param Tx the <code>TransformW</code> object to be concatenated with this * <code>TransformW</code> object. * @see #preConcatenate * @since 1.2 */ public void concatenate(TransformW Tx) { double M0, M1; double T00, T01, T10, T11; double T02, T12; int mystate = state; int txstate = Tx.state; switch ((txstate << HI_SHIFT) | mystate) { /* ---------- Tx == IDENTITY cases ---------- */ case (HI_IDENTITY | APPLY_IDENTITY): case (HI_IDENTITY | APPLY_TRANSLATE): case (HI_IDENTITY | APPLY_SCALE): case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): case (HI_IDENTITY | APPLY_SHEAR): case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): return; /* ---------- this == IDENTITY cases ---------- */ case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): m01 = Tx.m01; m10 = Tx.m10; //$FALL-THROUGH$ case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): m00 = Tx.m00; m11 = Tx.m11; //$FALL-THROUGH$ case (HI_TRANSLATE | APPLY_IDENTITY): m02 = Tx.m02; m12 = Tx.m12; state = txstate; type = Tx.type; return; case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY): m01 = Tx.m01; m10 = Tx.m10; //$FALL-THROUGH$ case (HI_SCALE | APPLY_IDENTITY): m00 = Tx.m00; m11 = Tx.m11; state = txstate; type = Tx.type; return; case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY): m02 = Tx.m02; m12 = Tx.m12; //$FALL-THROUGH$ case (HI_SHEAR | APPLY_IDENTITY): m01 = Tx.m01; m10 = Tx.m10; m00 = m11 = 0.0; state = txstate; type = Tx.type; return; /* ---------- Tx == TRANSLATE cases ---------- */ case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_TRANSLATE | APPLY_SHEAR): case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): case (HI_TRANSLATE | APPLY_SCALE): case (HI_TRANSLATE | APPLY_TRANSLATE): translate(Tx.m02, Tx.m12); return; /* ---------- Tx == SCALE cases ---------- */ case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_SCALE | APPLY_SHEAR): case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SCALE | APPLY_SCALE): case (HI_SCALE | APPLY_TRANSLATE): scale(Tx.m00, Tx.m11); return; /* ---------- Tx == SHEAR cases ---------- */ case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): T01 = Tx.m01; T10 = Tx.m10; M0 = m00; m00 = m01 * T10; m01 = M0 * T01; M0 = m10; m10 = m11 * T10; m11 = M0 * T01; type = TYPE_UNKNOWN; return; case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SHEAR): m00 = m01 * Tx.m10; m01 = 0.0; m11 = m10 * Tx.m01; m10 = 0.0; state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); type = TYPE_UNKNOWN; return; case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SCALE): m01 = m00 * Tx.m01; m00 = 0.0; m10 = m11 * Tx.m10; m11 = 0.0; state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); type = TYPE_UNKNOWN; return; case (HI_SHEAR | APPLY_TRANSLATE): m00 = 0.0; m01 = Tx.m01; m10 = Tx.m10; m11 = 0.0; state = APPLY_TRANSLATE | APPLY_SHEAR; type = TYPE_UNKNOWN; return; } // If Tx has more than one attribute, it is not worth optimizing // all of those cases... T00 = Tx.m00; T01 = Tx.m01; T02 = Tx.m02; T10 = Tx.m10; T11 = Tx.m11; T12 = Tx.m12; switch (mystate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE): state = mystate | txstate; //$FALL-THROUGH$ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): M0 = m00; M1 = m01; m00 = T00 * M0 + T10 * M1; m01 = T01 * M0 + T11 * M1; m02 += T02 * M0 + T12 * M1; M0 = m10; M1 = m11; m10 = T00 * M0 + T10 * M1; m11 = T01 * M0 + T11 * M1; m12 += T02 * M0 + T12 * M1; type = TYPE_UNKNOWN; return; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): M0 = m01; m00 = T10 * M0; m01 = T11 * M0; m02 += T12 * M0; M0 = m10; m10 = T00 * M0; m11 = T01 * M0; m12 += T02 * M0; break; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): M0 = m00; m00 = T00 * M0; m01 = T01 * M0; m02 += T02 * M0; M0 = m11; m10 = T10 * M0; m11 = T11 * M0; m12 += T12 * M0; break; case (APPLY_TRANSLATE): m00 = T00; m01 = T01; m02 += T02; m10 = T10; m11 = T11; m12 += T12; state = txstate | APPLY_TRANSLATE; type = TYPE_UNKNOWN; return; } updateState(); } @Override public TransformW createClone() { return new TransformW(this); } }