//package com.chemhack.jsMolEditor.client.jre.emulation.java.awt.geom; // //import com.chemhack.jsMolEditor.client.jre.emulation.java.lang.InternalError; ///** // * The <code>AffineTransform</code> class represents a 2D affine transform // * that performs a linear mapping from 2D coordinates to other 2D // * coordinates that preserves the "straightness" and // * "parallelness" of lines. Affine transformations can be constructed // * using sequences of translations, scales, flips, rotations, and shears. // * <p> // * Such a coordinate transformation can be represented by a 3 row by // * 3 column matrix with an implied last row of [ 0 0 1 ]. This matrix // * transforms source coordinates <code>(x, y)</code> into // * destination coordinates <code>(x', y')</code> by considering // * them to be a column vector and multiplying the coordinate vector // * by the matrix according to the following process: // * <pre> // * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ] // * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ] // * [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ] // * </pre> // * // * @version 1.71, 12/19/03 // * @author Jim Graham // */ //public class AffineTransform implements Cloneable { // /* // * 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 // */ // public static final int TYPE_IDENTITY = 0; // // /** // * This flag bit indicates that the transform defined by this object // * performs a translation in addition to the conversions indicated // * by other flag bits. // * A translation moves the coordinates by a constant amount in x // * and y without changing the length or angle of vectors. // * @see #TYPE_IDENTITY // * @see #TYPE_UNIFORM_SCALE // * @see #TYPE_GENERAL_SCALE // * @see #TYPE_FLIP // * @see #TYPE_QUADRANT_ROTATION // * @see #TYPE_GENERAL_ROTATION // * @see #TYPE_GENERAL_TRANSFORM // * @see #getType // */ // public static final int TYPE_TRANSLATION = 1; // // /** // * This flag bit indicates that the transform defined by this object // * performs a uniform scale in addition to the conversions indicated // * by other flag bits. // * A uniform scale multiplies the length of vectors by the same amount // * in both the x and y directions without changing the angle between // * vectors. // * This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag. // * @see #TYPE_IDENTITY // * @see #TYPE_TRANSLATION // * @see #TYPE_GENERAL_SCALE // * @see #TYPE_FLIP // * @see #TYPE_QUADRANT_ROTATION // * @see #TYPE_GENERAL_ROTATION // * @see #TYPE_GENERAL_TRANSFORM // * @see #getType // */ // public static final int TYPE_UNIFORM_SCALE = 2; // // /** // * This flag bit indicates that the transform defined by this object // * performs a general scale in addition to the conversions indicated // * by other flag bits. // * A general scale multiplies the length of vectors by different // * amounts in the x and y directions without changing the angle // * between perpendicular vectors. // * This flag bit is mutually exclusive with the TYPE_UNIFORM_SCALE flag. // * @see #TYPE_IDENTITY // * @see #TYPE_TRANSLATION // * @see #TYPE_UNIFORM_SCALE // * @see #TYPE_FLIP // * @see #TYPE_QUADRANT_ROTATION // * @see #TYPE_GENERAL_ROTATION // * @see #TYPE_GENERAL_TRANSFORM // * @see #getType // */ // public static final int TYPE_GENERAL_SCALE = 4; // // /** // * This constant is a bit mask for any of the scale flag bits. // * @see #TYPE_UNIFORM_SCALE // * @see #TYPE_GENERAL_SCALE // */ // public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE | // TYPE_GENERAL_SCALE); // // /** // * This flag bit indicates that the transform defined by this object // * performs a mirror image flip about some axis which changes the // * normally right handed coordinate system into a left handed // * system in addition to the conversions indicated by other flag bits. // * A right handed coordinate system is one where the positive X // * axis rotates counterclockwise to overlay the positive Y axis // * similar to the direction that the fingers on your right hand // * curl when you stare end on at your thumb. // * A left handed coordinate system is one where the positive X // * axis rotates clockwise to overlay the positive Y axis similar // * to the direction that the fingers on your left hand curl. // * There is no mathematical way to determine the angle of the // * original flipping or mirroring transformation since all angles // * of flip are identical given an appropriate adjusting rotation. // * @see #TYPE_IDENTITY // * @see #TYPE_TRANSLATION // * @see #TYPE_UNIFORM_SCALE // * @see #TYPE_GENERAL_SCALE // * @see #TYPE_QUADRANT_ROTATION // * @see #TYPE_GENERAL_ROTATION // * @see #TYPE_GENERAL_TRANSFORM // * @see #getType // */ // public static final int TYPE_FLIP = 64; // /* NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public // * circulation and the flag bits could no longer be conveniently // * renumbered without introducing binary incompatibility in outside // * code. // */ // // /** // * This flag bit indicates that the transform defined by this object // * performs a quadrant rotation by some multiple of 90 degrees in // * addition to the conversions indicated by other flag bits. // * A rotation changes the angles of vectors by the same amount // * regardless of the original direction of the vector and without // * changing the length of the vector. // * This flag bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag. // * @see #TYPE_IDENTITY // * @see #TYPE_TRANSLATION // * @see #TYPE_UNIFORM_SCALE // * @see #TYPE_GENERAL_SCALE // * @see #TYPE_FLIP // * @see #TYPE_GENERAL_ROTATION // * @see #TYPE_GENERAL_TRANSFORM // * @see #getType // */ // public static final int TYPE_QUADRANT_ROTATION = 8; // // /** // * This flag bit indicates that the transform defined by this object // * performs a rotation by an arbitrary angle in addition to the // * conversions indicated by other flag bits. // * A rotation changes the angles of vectors by the same amount // * regardless of the original direction of the vector and without // * changing the length of the vector. // * This flag bit is mutually exclusive with the // * TYPE_QUADRANT_ROTATION flag. // * @see #TYPE_IDENTITY // * @see #TYPE_TRANSLATION // * @see #TYPE_UNIFORM_SCALE // * @see #TYPE_GENERAL_SCALE // * @see #TYPE_FLIP // * @see #TYPE_QUADRANT_ROTATION // * @see #TYPE_GENERAL_TRANSFORM // * @see #getType // */ // public static final int TYPE_GENERAL_ROTATION = 16; // // /** // * This constant is a bit mask for any of the rotation flag bits. // * @see #TYPE_QUADRANT_ROTATION // * @see #TYPE_GENERAL_ROTATION // */ // public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION | // TYPE_GENERAL_ROTATION); // // /** // * This constant indicates that the transform defined by this object // * performs an arbitrary conversion of the input coordinates. // * If this transform can be classified by any of the above constants, // * the type will either be the constant TYPE_IDENTITY or a // * combination of the appropriate flag bits for the various coordinate // * conversions that this transform performs. // * @see #TYPE_IDENTITY // * @see #TYPE_TRANSLATION // * @see #TYPE_UNIFORM_SCALE // * @see #TYPE_GENERAL_SCALE // * @see #TYPE_FLIP // * @see #TYPE_QUADRANT_ROTATION // * @see #TYPE_GENERAL_ROTATION // * @see #getType // */ // public static final int TYPE_GENERAL_TRANSFORM = 32; // // /** // * This constant is used for the internal state variable to indicate // * that no calculations need to be performed and that the source // * coordinates only need to be copied to their destinations to // * complete the transformation equation of this transform. // * @see #APPLY_TRANSLATE // * @see #APPLY_SCALE // * @see #APPLY_SHEAR // * @see #state // */ // static final int APPLY_IDENTITY = 0; // // /** // * This constant is used for the internal state variable to indicate // * that the translation components of the matrix (m02 and m12) need // * to be added to complete the transformation equation of this transform. // * @see #APPLY_IDENTITY // * @see #APPLY_SCALE // * @see #APPLY_SHEAR // * @see #state // */ // static final int APPLY_TRANSLATE = 1; // // /** // * This constant is used for the internal state variable to indicate // * that the scaling components of the matrix (m00 and m11) need // * to be factored in to complete the transformation equation of // * this transform. If the APPLY_SHEAR bit is also set then it // * indicates that the scaling components are not both 0.0. If the // * APPLY_SHEAR bit is not also set then it indicates that the // * scaling components are not both 1.0. If neither the APPLY_SHEAR // * nor the APPLY_SCALE bits are set then the scaling components // * are both 1.0, which means that the x and y components contribute // * to the transformed coordinate, but they are not multiplied by // * any scaling factor. // * @see #APPLY_IDENTITY // * @see #APPLY_TRANSLATE // * @see #APPLY_SHEAR // * @see #state // */ // static final int APPLY_SCALE = 2; // // /** // * This constant is used for the internal state variable to indicate // * that the shearing components of the matrix (m01 and m10) need // * to be factored in to complete the transformation equation of this // * transform. The presence of this bit in the state variable changes // * the interpretation of the APPLY_SCALE bit as indicated in its // * documentation. // * @see #APPLY_IDENTITY // * @see #APPLY_TRANSLATE // * @see #APPLY_SCALE // * @see #state // */ // static final int APPLY_SHEAR = 4; // // /* // * For methods which combine together the state of two separate // * transforms and dispatch based upon the combination, these constants // * specify how far to shift one of the states so that the two states // * are mutually non-interfering and provide constants for testing the // * bits of the shifted (HI) state. The methods in this class use // * the convention that the state of "this" transform is unshifted and // * the state of the "other" or "argument" transform is shifted (HI). // */ // private static final int HI_SHIFT = 3; // private static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT; // private static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT; // private static final int HI_SCALE = APPLY_SCALE << HI_SHIFT; // private static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT; // // /** // * The X coordinate scaling element of the 3x3 // * affine transformation matrix. // * // * @serial // */ // double m00; // // /** // * The Y coordinate shearing element of the 3x3 // * affine transformation matrix. // * // * @serial // */ // double m10; // // /** // * The X coordinate shearing element of the 3x3 // * affine transformation matrix. // * // * @serial // */ // double m01; // // /** // * The Y coordinate scaling element of the 3x3 // * affine transformation matrix. // * // * @serial // */ // double m11; // // /** // * The X coordinate of the translation element of the // * 3x3 affine transformation matrix. // * // * @serial // */ // double m02; // // /** // * The Y coordinate of the translation element of the // * 3x3 affine transformation matrix. // * // * @serial // */ // double m12; // // /** // * This field keeps track of which components of the matrix need to // * be applied when performing a transformation. // * @see #APPLY_IDENTITY // * @see #APPLY_TRANSLATE // * @see #APPLY_SCALE // * @see #APPLY_SHEAR // */ // transient int state; // // /** // * 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; // // private AffineTransform(double m00, double m10, // double m01, double m11, // double m02, double m12, // int state) { // this.m00 = m00; // this.m10 = m10; // this.m01 = m01; // this.m11 = m11; // this.m02 = m02; // this.m12 = m12; // this.state = state; // this.type = TYPE_UNKNOWN; // } // // /** // * Constructs a new <code>AffineTransform</code> representing the // * Identity transformation. // */ // public AffineTransform() { // m00 = m11 = 1.0; // // m01 = m10 = m02 = m12 = 0.0; /* Not needed. */ // // state = APPLY_IDENTITY; /* Not needed. */ // // type = TYPE_IDENTITY; /* Not needed. */ // } // // /** // * Constructs a new <code>AffineTransform</code> that is a copy of // * the specified <code>AffineTransform</code> object. // * @param Tx the <code>AffineTransform</code> object to copy // */ // public AffineTransform(AffineTransform Tx) { // this.m00 = Tx.m00; // this.m10 = Tx.m10; // this.m01 = Tx.m01; // this.m11 = Tx.m11; // this.m02 = Tx.m02; // this.m12 = Tx.m12; // this.state = Tx.state; // this.type = Tx.type; // } // // /** // * Constructs a new <code>AffineTransform</code> from 6 floating point // * values representing the 6 specifiable entries of the 3x3 // * transformation matrix. // * @param m00, m01, m02, m10, m11, m12 the // * 6 floating point values that compose the 3x3 transformation matrix // */ // public AffineTransform(float m00, float m10, // float m01, float m11, // float m02, float m12) { // this.m00 = m00; // this.m10 = m10; // this.m01 = m01; // this.m11 = m11; // this.m02 = m02; // this.m12 = m12; // updateState(); // } // // /** // * Constructs a new <code>AffineTransform</code> from an array of // * floating point values representing either the 4 non-translation // * enries or the 6 specifiable entries of the 3x3 transformation // * matrix. The values are retrieved from the array as // * { m00 m10 m01 m11 [m02 m12]}. // * @param flatmatrix the float array containing the values to be set // * in the new <code>AffineTransform</code> object. The length of the // * array is assumed to be at least 4. If the length of the array is // * less than 6, only the first 4 values are taken. If the length of // * the array is greater than 6, the first 6 values are taken. // */ // public AffineTransform(float[] flatmatrix) { // m00 = flatmatrix[0]; // m10 = flatmatrix[1]; // m01 = flatmatrix[2]; // m11 = flatmatrix[3]; // if (flatmatrix.length > 5) { // m02 = flatmatrix[4]; // m12 = flatmatrix[5]; // } // updateState(); // } // // /** // * Constructs a new <code>AffineTransform</code> from 6 double // * precision values representing the 6 specifiable entries of the 3x3 // * transformation matrix. // * @param m00, m01, m02, m10, m11, m12 the // * 6 floating point values that compose the 3x3 transformation matrix // */ // public AffineTransform(double m00, double m10, // double m01, double m11, // double m02, double m12) { // this.m00 = m00; // this.m10 = m10; // this.m01 = m01; // this.m11 = m11; // this.m02 = m02; // this.m12 = m12; // updateState(); // } // // /** // * Constructs a new <code>AffineTransform</code> from an array of // * double precision values representing either the 4 non-translation // * entries or the 6 specifiable entries of the 3x3 transformation // * matrix. The values are retrieved from the array as // * { m00 m10 m01 m11 [m02 m12]}. // * @param flatmatrix the double array containing the values to be set // * in the new <code>AffineTransform</code> object. The length of the // * array is assumed to be at least 4. If the length of the array is // * less than 6, only the first 4 values are taken. If the length of // * the array is greater than 6, the first 6 values are taken. // */ // public AffineTransform(double[] flatmatrix) { // m00 = flatmatrix[0]; // m10 = flatmatrix[1]; // m01 = flatmatrix[2]; // m11 = flatmatrix[3]; // if (flatmatrix.length > 5) { // m02 = flatmatrix[4]; // m12 = flatmatrix[5]; // } // updateState(); // } // // /** // * Returns a transform representing a translation transformation. // * The matrix representing the returned transform is: // * <pre> // * [ 1 0 tx ] // * [ 0 1 ty ] // * [ 0 0 1 ] // * </pre> // * @param tx the distance by which coordinates are translated in the // * X axis direction // * @param ty the distance by which coordinates are translated in the // * Y axis direction // * @return an <code>AffineTransform</code> object that represents a // * translation transformation, created with the specified vector. // */ // public static AffineTransform getTranslateInstance(double tx, double ty) { // AffineTransform Tx = new AffineTransform(); // Tx.setToTranslation(tx, ty); // return Tx; // } // // /** // * Returns a transform representing a rotation transformation. // * The matrix representing the returned transform is: // * <pre> // * [ cos(theta) -sin(theta) 0 ] // * [ sin(theta) cos(theta) 0 ] // * [ 0 0 1 ] // * </pre> // * Rotating with a positive angle theta rotates points on the positive // * x axis toward the positive y axis. // * @param theta the angle of rotation in radians // * @return an <code>AffineTransform</code> object that is a rotation // * transformation, created with the specified angle of rotation. // */ // public static AffineTransform getRotateInstance(double theta) { // AffineTransform Tx = new AffineTransform(); // Tx.setToRotation(theta); // return Tx; // } // // /** // * Returns a transform that rotates coordinates around an anchor point. // * This operation is equivalent to translating the coordinates so // * that the anchor point is at the origin (S1), then rotating them // * about the new origin (S2), and finally translating so that the // * intermediate origin is restored to the coordinates of the original // * anchor point (S3). // * <p> // * This operation is equivalent to the following sequence of calls: // * <pre> // * AffineTransform Tx = new AffineTransform(); // * Tx.setToTranslation(x, y); // S3: final translation // * Tx.rotate(theta); // S2: rotate around anchor // * Tx.translate(-x, -y); // S1: translate anchor to origin // * </pre> // * The matrix representing the returned transform is: // * <pre> // * [ cos(theta) -sin(theta) x-x*cos+y*sin ] // * [ sin(theta) cos(theta) y-x*sin-y*cos ] // * [ 0 0 1 ] // * </pre> // * Rotating with a positive angle theta rotates points on the positive // * x axis toward the positive y axis. // * @param theta the angle of rotation in radians // * @param x, y the coordinates of the anchor point of the // * rotation // * @return an <code>AffineTransform</code> object that rotates // * coordinates around the specified point by the specified angle of // * rotation. // */ // public static AffineTransform getRotateInstance(double theta, // double x, double y) { // AffineTransform Tx = new AffineTransform(); // Tx.setToRotation(theta, x, y); // return Tx; // } // // /** // * Returns a transform representing a scaling transformation. // * The matrix representing the returned transform is: // * <pre> // * [ sx 0 0 ] // * [ 0 sy 0 ] // * [ 0 0 1 ] // * </pre> // * @param sx the factor by which coordinates are scaled along the // * X axis direction // * @param sy the factor by which coordinates are scaled along the // * Y axis direction // * @return an <code>AffineTransform</code> object that scales // * coordinates by the specified factors. // */ // public static AffineTransform getScaleInstance(double sx, double sy) { // AffineTransform Tx = new AffineTransform(); // Tx.setToScale(sx, sy); // return Tx; // } // // /** // * Returns a transform representing a shearing transformation. // * The matrix representing the returned transform is: // * <pre> // * [ 1 shx 0 ] // * [ shy 1 0 ] // * [ 0 0 1 ] // * </pre> // * @param shx the multiplier by which coordinates are shifted in the // * direction of the positive X axis as a factor of their Y coordinate // * @param shy the multiplier by which coordinates are shifted in the // * direction of the positive Y axis as a factor of their X coordinate // * @return an <code>AffineTransform</code> object that shears // * coordinates by the specified multipliers. // */ // public static AffineTransform getShearInstance(double shx, double shy) { // AffineTransform Tx = new AffineTransform(); // Tx.setToShear(shx, shy); // return Tx; // } // // /** // * Retrieves the flag bits describing the conversion properties of // * this transform. // * The return value is either one of the constants TYPE_IDENTITY // * or TYPE_GENERAL_TRANSFORM, or a combination of the // * appriopriate flag bits. // * A valid combination of flag bits is an exclusive OR operation // * that can combine // * the TYPE_TRANSLATION flag bit // * in addition to either of the // * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits // * as well as either of the // * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits. // * @return the OR combination of any of the indicated flags that // * apply to this transform // * @see #TYPE_IDENTITY // * @see #TYPE_TRANSLATION // * @see #TYPE_UNIFORM_SCALE // * @see #TYPE_GENERAL_SCALE // * @see #TYPE_QUADRANT_ROTATION // * @see #TYPE_GENERAL_ROTATION // * @see #TYPE_GENERAL_TRANSFORM // */ // public int getType() { // 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; // /* NOBREAK */ // 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; // /* NOBREAK */ // case (APPLY_SHEAR): // sgn0 = ((M0 = m01) >= 0.0); // sgn1 = ((M1 = m10) >= 0.0); // if (sgn0 != sgn1) { // // Different signs - simple 90 degree rotation // if (M0 != -M1) { // ret |= (TYPE_QUADRANT_ROTATION | TYPE_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; // /* NOBREAK */ // case (APPLY_SCALE): // sgn0 = ((M0 = m00) >= 0.0); // sgn1 = ((M1 = m11) >= 0.0); // if (sgn0 == sgn1) { // if (sgn0) { // // Both scaling factors non-negative - simple scale // // 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 // */ // 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 void stateError() { // throw new InternalError("missing case in transform state switch"); // } // // /** // * Retrieves the 6 specifiable values in the 3x3 affine transformation // * matrix and places them into an array of double precisions values. // * The values are stored in the array as // * { m00 m10 m01 m11 m02 m12 }. // * An array of 4 doubles can also be specified, in which case only the // * first four elements representing the non-transform // * parts of the array are retrieved and the values are stored into // * the array as { m00 m10 m01 m11 } // * @param flatmatrix the double array used to store the returned // * values. // * @see #getScaleX // * @see #getScaleY // * @see #getShearX // * @see #getShearY // * @see #getTranslateX // * @see #getTranslateY // */ // public void getMatrix(double[] flatmatrix) { // flatmatrix[0] = m00; // flatmatrix[1] = m10; // flatmatrix[2] = m01; // flatmatrix[3] = m11; // if (flatmatrix.length > 5) { // flatmatrix[4] = m02; // flatmatrix[5] = m12; // } // } // // /** // * Returns the X coordinate scaling element (m00) of the 3x3 // * affine transformation matrix. // * @return a double value that is the X coordinate of the scaling // * element of the affine transformation matrix. // * @see #getMatrix // */ // public double getScaleX() { // return m00; // } // // /** // * Returns the Y coordinate scaling element (m11) of the 3x3 // * affine transformation matrix. // * @return a double value that is the Y coordinate of the scaling // * element of the affine transformation matrix. // * @see #getMatrix // */ // public double getScaleY() { // return m11; // } // // /** // * Returns the X coordinate shearing element (m01) of the 3x3 // * affine transformation matrix. // * @return a double value that is the X coordinate of the shearing // * element of the affine transformation matrix. // * @see #getMatrix // */ // public double getShearX() { // return m01; // } // // /** // * Returns the Y coordinate shearing element (m10) of the 3x3 // * affine transformation matrix. // * @return a double value that is the Y coordinate of the shearing // * element of the affine transformation matrix. // * @see #getMatrix // */ // public double getShearY() { // return m10; // } // // /** // * Returns the X coordinate of the translation element (m02) of the // * 3x3 affine transformation matrix. // * @return a double value that is the X coordinate of the translation // * element of the affine transformation matrix. // * @see #getMatrix // */ // public double getTranslateX() { // return m02; // } // // /** // * Returns the Y coordinate of the translation element (m12) of the // * 3x3 affine transformation matrix. // * @return a double value that is the Y coordinate of the translation // * element of the affine transformation matrix. // * @see #getMatrix // */ // public double getTranslateY() { // return m12; // } // // /** // * Concatenates this transform with a translation transformation. // * This is equivalent to calling concatenate(T), where T is an // * <code>AffineTransform</code> represented by the following matrix: // * <pre> // * [ 1 0 tx ] // * [ 0 1 ty ] // * [ 0 0 1 ] // * </pre> // * @param tx the distance by which coordinates are translated in the // * X axis direction // * @param ty the distance by which coordinates are translated in the // * Y axis direction // */ // public void translate(double tx, double ty) { // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // m02 = tx * m00 + ty * m01 + m02; // m12 = tx * m10 + ty * m11 + m12; // 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; // } // } // // /** // * Concatenates this transform with a rotation transformation. // * This is equivalent to calling concatenate(R), where R is an // * <code>AffineTransform</code> represented by the following matrix: // * <pre> // * [ cos(theta) -sin(theta) 0 ] // * [ sin(theta) cos(theta) 0 ] // * [ 0 0 1 ] // * </pre> // * Rotating with a positive angle theta rotates points on the positive // * x axis toward the positive y axis. // * @param theta the angle of rotation in radians // */ // public void rotate(double theta) { // double sin = Math.sin(theta); // double cos = Math.cos(theta); // if (Math.abs(sin) < 1E-15) { // if (cos < 0.0) { // m00 = -m00; // m11 = -m11; // int state = this.state; // if ((state & (APPLY_SHEAR)) != 0) { // // If there was a shear, then this rotation has no // // effect on the state. // m01 = -m01; // m10 = -m10; // } else { // // No shear means the SCALE state may toggle when // // m00 and m11 are negated. // if (m00 == 1.0 && m11 == 1.0) { // this.state = state & ~APPLY_SCALE; // } else { // this.state = state | APPLY_SCALE; // } // } // type = TYPE_UNKNOWN; // } // return; // } // if (Math.abs(cos) < 1E-15) { // if (sin < 0.0) { // double M0 = m00; // m00 = -m01; // m01 = M0; // M0 = m10; // m10 = -m11; // m11 = M0; // } else { // double M0 = m00; // m00 = m01; // m01 = -M0; // M0 = m10; // m10 = m11; // m11 = -M0; // } // 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; // return; // } // double M0, M1; // M0 = m00; // M1 = m01; // m00 = cos * M0 + sin * M1; // m01 = -sin * M0 + cos * M1; // M0 = m10; // M1 = m11; // m10 = cos * M0 + sin * M1; // m11 = -sin * M0 + cos * M1; // updateState(); // } // private static int rot90conversion[] = { // APPLY_SHEAR, APPLY_SHEAR | APPLY_TRANSLATE, // APPLY_SHEAR, APPLY_SHEAR | APPLY_TRANSLATE, // APPLY_SCALE, APPLY_SCALE | APPLY_TRANSLATE, // APPLY_SHEAR | APPLY_SCALE, // APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE, // }; // // /** // * Concatenates this transform with a transform that rotates // * coordinates around an anchor point. // * This operation is equivalent to translating the coordinates so // * that the anchor point is at the origin (S1), then rotating them // * about the new origin (S2), and finally translating so that the // * intermediate origin is restored to the coordinates of the original // * anchor point (S3). // * <p> // * This operation is equivalent to the following sequence of calls: // * <pre> // * translate(x, y); // S3: final translation // * rotate(theta); // S2: rotate around anchor // * translate(-x, -y); // S1: translate anchor to origin // * </pre> // * Rotating with a positive angle theta rotates points on the positive // * x axis toward the positive y axis. // * @param theta the angle of rotation in radians // * @param x, y the coordinates of the anchor point of the // * rotation // */ // public void rotate(double theta, double x, double y) { // // REMIND: Simple for now - optimize later // translate(x, y); // rotate(theta); // translate(-x, -y); // } // // /** // * Concatenates this transform with a scaling transformation. // * This is equivalent to calling concatenate(S), where S is an // * <code>AffineTransform</code> represented by the following matrix: // * <pre> // * [ sx 0 0 ] // * [ 0 sy 0 ] // * [ 0 0 1 ] // * </pre> // * @param sx the factor by which coordinates are scaled along the // * X axis direction // * @param sy the factor by which coordinates are scaled along the // * Y axis direction // */ // public void scale(double sx, double sy) { // int state = this.state; // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // case (APPLY_SHEAR | APPLY_SCALE): // m00 *= sx; // m11 *= sy; // /* NOBREAK */ // case (APPLY_SHEAR | APPLY_TRANSLATE): // case (APPLY_SHEAR): // m01 *= sy; // m10 *= sx; // if (m01 == 0 && m10 == 0) { // this.state = state - APPLY_SHEAR; // // REMIND: Is it possible for m00 and m11 to be both 1.0? // } // this.type = TYPE_UNKNOWN; // 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>AffineTransform</code> represented by the following matrix: // * <pre> // * [ 1 shx 0 ] // * [ shy 1 0 ] // * [ 0 0 1 ] // * </pre> // * @param shx the multiplier by which coordinates are shifted in the // * direction of the positive X axis as a factor of their Y coordinate // * @param shy the multiplier by which coordinates are shifted in the // * direction of the positive Y axis as a factor of their X coordinate // */ // public void shear(double shx, double shy) { // int state = this.state; // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // case (APPLY_SHEAR | APPLY_SCALE): // double M0, M1; // M0 = m00; // M1 = m01; // m00 = M0 + M1 * shy; // m01 = M0 * shx + M1; // // M0 = m10; // M1 = m11; // m10 = M0 + M1 * shy; // m11 = M0 * shx + M1; // 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. // */ // 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 // */ // 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 with a positive angle theta rotates points on the positive // * x axis toward the positive y axis. // * @param theta the angle of rotation in radians // */ // public void setToRotation(double theta) { // m02 = 0.0; // m12 = 0.0; // double sin = Math.sin(theta); // double cos = Math.cos(theta); // if (Math.abs(sin) < 1E-15) { // m01 = m10 = 0.0; // if (cos < 0) { // m00 = m11 = -1.0; // state = APPLY_SCALE; // type = TYPE_QUADRANT_ROTATION; // } else { // m00 = m11 = 1.0; // state = APPLY_IDENTITY; // type = TYPE_IDENTITY; // } // return; // } // if (Math.abs(cos) < 1E-15) { // m00 = m11 = 0.0; // if (sin < 0.0) { // m01 = 1.0; // m10 = -1.0; // } else { // m01 = -1.0; // m10 = 1.0; // } // state = APPLY_SHEAR; // type = TYPE_QUADRANT_ROTATION; // return; // } // m00 = cos; // m01 = -sin; // m10 = sin; // m11 = cos; // state = APPLY_SHEAR | APPLY_SCALE; // type = TYPE_GENERAL_ROTATION; // return; // } // // /** // * Sets this transform to a translated rotation transformation. // * This operation is equivalent to translating the coordinates so // * that the anchor point is at the origin (S1), then rotating them // * about the new origin (S2), and finally translating so that the // * intermediate origin is restored to the coordinates of the original // * anchor point (S3). // * <p> // * This operation is equivalent to the following sequence of calls: // * <pre> // * setToTranslation(x, y); // S3: final translation // * rotate(theta); // S2: rotate around anchor // * translate(-x, -y); // S1: translate anchor to origin // * </pre> // * The matrix representing this transform becomes: // * <pre> // * [ cos(theta) -sin(theta) x-x*cos+y*sin ] // * [ sin(theta) cos(theta) y-x*sin-y*cos ] // * [ 0 0 1 ] // * </pre> // * Rotating with a positive angle theta rotates points on the positive // * x axis toward the positive y axis. // * @param theta the angle of rotation in radians // * @param x, y the coordinates of the anchor point of the // * rotation // */ // public void setToRotation(double theta, double x, double y) { // setToRotation(theta); // double sin = m10; // double oneMinusCos = 1.0 - m00; // m02 = x * oneMinusCos + y * sin; // m12 = y * oneMinusCos - x * sin; // if (m02 != 0.0 || m12 != 0.0) { // state |= APPLY_TRANSLATE; // type |= TYPE_TRANSLATION; // } // return; // } // // /** // * Sets this transform to a scaling transformation. // * The matrix representing this transform becomes: // * <pre> // * [ sx 0 0 ] // * [ 0 sy 0 ] // * [ 0 0 1 ] // * </pre> // * @param sx the factor by which coordinates are scaled along the // * X axis direction // * @param sy the factor by which coordinates are scaled along the // * Y axis direction // */ // public void setToScale(double sx, double sy) { // m00 = sx; // m10 = 0.0; // m01 = 0.0; // m11 = sy; // m02 = 0.0; // m12 = 0.0; // 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 // */ // 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>AffineTransform</code> object. // * @param Tx the <code>AffineTransform</code> object from which to // * copy the transform // */ // public void setTransform(AffineTransform Tx) { // this.m00 = Tx.m00; // this.m10 = Tx.m10; // this.m01 = Tx.m01; // this.m11 = Tx.m11; // this.m02 = Tx.m02; // this.m12 = Tx.m12; // this.state = Tx.state; // this.type = Tx.type; // } // // /** // * Sets this transform to the matrix specified by the 6 // * double precision values. // * @param m00, m01, m02, m10, m11, m12 the // * 6 floating point values that compose the 3x3 transformation matrix // */ // public void setTransform(double m00, double m10, // double m01, double m11, // double m02, double m12) { // this.m00 = m00; // this.m10 = m10; // this.m01 = m01; // this.m11 = m11; // this.m02 = m02; // this.m12 = m12; // updateState(); // } // // /** // * Concatenates an <code>AffineTransform</code> <code>Tx</code> to // * this <code>AffineTransform</code> Cx in the most commonly useful // * way to provide a new user space // * that is mapped to the former user space by <code>Tx</code>. // * Cx is updated to perform the combined transformation. // * Transforming a point p by the updated transform Cx' is // * equivalent to first transforming p by <code>Tx</code> and then // * transforming the result by the original transform Cx like this: // * Cx'(p) = Cx(Tx(p)) // * In matrix notation, if this transform Cx is // * represented by the matrix [this] and <code>Tx</code> is represented // * by the matrix [Tx] then this method does the following: // * <pre> // * [this] = [this] x [Tx] // * </pre> // * @param Tx the <code>AffineTransform</code> object to be // * concatenated with this <code>AffineTransform</code> object. // * @see #preConcatenate // */ // public void concatenate(AffineTransform Tx) { // double M0, M1; // double T00, T01, T10, T11; // double T02, T12; // int mystate = state; // int txstate = Tx.state; // switch ((txstate << HI_SHIFT) | mystate) { // // /* ---------- Tx == IDENTITY cases ---------- */ // case (HI_IDENTITY | APPLY_IDENTITY): // case (HI_IDENTITY | APPLY_TRANSLATE): // case (HI_IDENTITY | APPLY_SCALE): // case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): // case (HI_IDENTITY | APPLY_SHEAR): // case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): // case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): // case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // return; // // /* ---------- this == IDENTITY cases ---------- */ // case (HI_SHEAR | HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): // m01 = Tx.m01; // m10 = Tx.m10; // /* NOBREAK */ // case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): // m00 = Tx.m00; // m11 = Tx.m11; // /* NOBREAK */ // case (HI_TRANSLATE | APPLY_IDENTITY): // m02 = Tx.m02; // m12 = Tx.m12; // state = txstate; // type = Tx.type; // return; // case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY): // m01 = Tx.m01; // m10 = Tx.m10; // /* NOBREAK */ // case (HI_SCALE | APPLY_IDENTITY): // m00 = Tx.m00; // m11 = Tx.m11; // state = txstate; // type = Tx.type; // return; // case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY): // m02 = Tx.m02; // m12 = Tx.m12; // /* NOBREAK */ // case (HI_SHEAR | APPLY_IDENTITY): // m01 = Tx.m01; // m10 = Tx.m10; // m00 = m11 = 0.0; // state = txstate; // 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; // /* NOBREAK */ // 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(); // } // // /** // * Concatenates an <code>AffineTransform</code> <code>Tx</code> to // * this <code>AffineTransform</code> Cx // * in a less commonly used way such that <code>Tx</code> modifies the // * coordinate transformation relative to the absolute pixel // * space rather than relative to the existing user space. // * Cx is updated to perform the combined transformation. // * Transforming a point p by the updated transform Cx' is // * equivalent to first transforming p by the original transform // * Cx and then transforming the result by // * <code>Tx</code> like this: // * Cx'(p) = Tx(Cx(p)) // * In matrix notation, if this transform Cx // * is represented by the matrix [this] and <code>Tx</code> is // * represented by the matrix [Tx] then this method does the // * following: // * <pre> // * [this] = [Tx] x [this] // * </pre> // * @param Tx the <code>AffineTransform</code> object to be // * concatenated with this <code>AffineTransform</code> object. // * @see #concatenate // */ // public void preConcatenate(AffineTransform Tx) { // double M0, M1; // double T00, T01, T10, T11; // double T02, T12; // int mystate = state; // int txstate = Tx.state; // switch ((txstate << HI_SHIFT) | mystate) { // case (HI_IDENTITY | APPLY_IDENTITY): // case (HI_IDENTITY | APPLY_TRANSLATE): // case (HI_IDENTITY | APPLY_SCALE): // case (HI_IDENTITY | APPLY_SCALE | APPLY_TRANSLATE): // case (HI_IDENTITY | APPLY_SHEAR): // case (HI_IDENTITY | APPLY_SHEAR | APPLY_TRANSLATE): // case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE): // case (HI_IDENTITY | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // // Tx is IDENTITY... // return; // // case (HI_TRANSLATE | APPLY_IDENTITY): // case (HI_TRANSLATE | APPLY_SCALE): // case (HI_TRANSLATE | APPLY_SHEAR): // case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE): // // Tx is TRANSLATE, this has no TRANSLATE // m02 = Tx.m02; // m12 = Tx.m12; // state = mystate | APPLY_TRANSLATE; // type |= TYPE_TRANSLATION; // return; // // case (HI_TRANSLATE | APPLY_TRANSLATE): // case (HI_TRANSLATE | APPLY_SCALE | APPLY_TRANSLATE): // case (HI_TRANSLATE | APPLY_SHEAR | APPLY_TRANSLATE): // case (HI_TRANSLATE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // // Tx is TRANSLATE, this has one too // m02 = m02 + Tx.m02; // m12 = m12 + Tx.m12; // return; // // case (HI_SCALE | APPLY_TRANSLATE): // case (HI_SCALE | APPLY_IDENTITY): // // Only these two existing states need a new state // state = mystate | APPLY_SCALE; // /* NOBREAK */ // case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // case (HI_SCALE | APPLY_SHEAR | APPLY_SCALE): // case (HI_SCALE | APPLY_SHEAR | APPLY_TRANSLATE): // case (HI_SCALE | APPLY_SHEAR): // case (HI_SCALE | APPLY_SCALE | APPLY_TRANSLATE): // case (HI_SCALE | APPLY_SCALE): // // Tx is SCALE, this is anything // T00 = Tx.m00; // T11 = Tx.m11; // if ((mystate & APPLY_SHEAR) != 0) { // m01 = m01 * T00; // m10 = m10 * T11; // if ((mystate & APPLY_SCALE) != 0) { // m00 = m00 * T00; // m11 = m11 * T11; // } // } else { // m00 = m00 * T00; // m11 = m11 * T11; // } // if ((mystate & APPLY_TRANSLATE) != 0) { // m02 = m02 * T00; // m12 = m12 * T11; // } // type = TYPE_UNKNOWN; // return; // case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): // case (HI_SHEAR | APPLY_SHEAR): // mystate = mystate | APPLY_SCALE; // /* NOBREAK */ // case (HI_SHEAR | APPLY_TRANSLATE): // case (HI_SHEAR | APPLY_IDENTITY): // case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // case (HI_SHEAR | APPLY_SCALE): // state = mystate ^ APPLY_SHEAR; // /* NOBREAK */ // case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): // // Tx is SHEAR, this is anything // T01 = Tx.m01; // T10 = Tx.m10; // // M0 = m00; // m00 = m10 * T01; // m10 = M0 * T10; // // M0 = m01; // m01 = m11 * T01; // m11 = M0 * T10; // // M0 = m02; // m02 = m12 * T01; // m12 = M0 * T10; // 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 | APPLY_TRANSLATE): // M0 = m02; // M1 = m12; // T02 += M0 * T00 + M1 * T01; // T12 += M0 * T10 + M1 * T11; // // /* NOBREAK */ // case (APPLY_SHEAR | APPLY_SCALE): // m02 = T02; // m12 = T12; // // M0 = m00; // M1 = m10; // m00 = M0 * T00 + M1 * T01; // m10 = M0 * T10 + M1 * T11; // // M0 = m01; // M1 = m11; // m01 = M0 * T00 + M1 * T01; // m11 = M0 * T10 + M1 * T11; // break; // // case (APPLY_SHEAR | APPLY_TRANSLATE): // M0 = m02; // M1 = m12; // T02 += M0 * T00 + M1 * T01; // T12 += M0 * T10 + M1 * T11; // // /* NOBREAK */ // case (APPLY_SHEAR): // m02 = T02; // m12 = T12; // // M0 = m10; // m00 = M0 * T01; // m10 = M0 * T11; // // M0 = m01; // m01 = M0 * T00; // m11 = M0 * T10; // break; // // case (APPLY_SCALE | APPLY_TRANSLATE): // M0 = m02; // M1 = m12; // T02 += M0 * T00 + M1 * T01; // T12 += M0 * T10 + M1 * T11; // // /* NOBREAK */ // case (APPLY_SCALE): // m02 = T02; // m12 = T12; // // M0 = m00; // m00 = M0 * T00; // m10 = M0 * T10; // // M0 = m11; // m01 = M0 * T01; // m11 = M0 * T11; // break; // // case (APPLY_TRANSLATE): // M0 = m02; // M1 = m12; // T02 += M0 * T00 + M1 * T01; // T12 += M0 * T10 + M1 * T11; // // /* NOBREAK */ // case (APPLY_IDENTITY): // m02 = T02; // m12 = T12; // // m00 = T00; // m10 = T10; // // m01 = T01; // m11 = T11; // // state = mystate | txstate; // type = TYPE_UNKNOWN; // return; // } // updateState(); // } // // /** // * Returns an <code>AffineTransform</code> object representing the // * inverse transformation. // * The inverse transform Tx' of this transform Tx // * maps coordinates transformed by Tx back // * to their original coordinates. // * In other words, Tx'(Tx(p)) = p = Tx(Tx'(p)). // * <p> // * If this transform maps all coordinates onto a point or a line // * then it will not have an inverse, since coordinates that do // * not lie on the destination point or line will not have an inverse // * mapping. // * The <code>getDeterminant</code> method can be used to determine if this // * transform has no inverse, in which case an exception will be // * thrown if the <code>createInverse</code> method is called. // * @return a new <code>AffineTransform</code> object representing the // * inverse transformation. // * @see #getDeterminant // * @exception NoninvertibleTransformException // * if the matrix cannot be inverted. // */ // public AffineTransform createInverse() // throws NoninvertibleTransformException // { // double det; // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // det = m00 * m11 - m01 * m10; // if (Math.abs(det) <= Double.MIN_VALUE) { // throw new NoninvertibleTransformException("Determinant is "+ // det); // } // return new AffineTransform( m11 / det, -m10 / det, // -m01 / det, m00 / det, // (m01 * m12 - m11 * m02) / det, // (m10 * m02 - m00 * m12) / det, // (APPLY_SHEAR | // APPLY_SCALE | // APPLY_TRANSLATE)); // case (APPLY_SHEAR | APPLY_SCALE): // det = m00 * m11 - m01 * m10; // if (Math.abs(det) <= Double.MIN_VALUE) { // throw new NoninvertibleTransformException("Determinant is "+ // det); // } // return new AffineTransform( m11 / det, -m10 / det, // -m01 / det, m00 / det, // 0.0, 0.0, // (APPLY_SHEAR | APPLY_SCALE)); // case (APPLY_SHEAR | APPLY_TRANSLATE): // if (m01 == 0.0 || m10 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // return new AffineTransform( 0.0, 1.0 / m01, // 1.0 / m10, 0.0, // -m12 / m10, -m02 / m01, // (APPLY_SHEAR | APPLY_TRANSLATE)); // case (APPLY_SHEAR): // if (m01 == 0.0 || m10 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // return new AffineTransform(0.0, 1.0 / m01, // 1.0 / m10, 0.0, // 0.0, 0.0, // (APPLY_SHEAR)); // case (APPLY_SCALE | APPLY_TRANSLATE): // if (m00 == 0.0 || m11 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // return new AffineTransform( 1.0 / m00, 0.0, // 0.0, 1.0 / m11, // -m02 / m00, -m12 / m11, // (APPLY_SCALE | APPLY_TRANSLATE)); // case (APPLY_SCALE): // if (m00 == 0.0 || m11 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // return new AffineTransform(1.0 / m00, 0.0, // 0.0, 1.0 / m11, // 0.0, 0.0, // (APPLY_SCALE)); // case (APPLY_TRANSLATE): // return new AffineTransform( 1.0, 0.0, // 0.0, 1.0, // -m02, -m12, // (APPLY_TRANSLATE)); // case (APPLY_IDENTITY): // return new AffineTransform(); // } // // /* NOTREACHED */ // } // // /** // * Transforms the specified <code>ptSrc</code> and stores the result // * in <code>ptDst</code>. // * If <code>ptDst</code> is <code>null</code>, a new {@link Point2D} // * object is allocated and then the result of the transformation is // * stored in this object. // * In either case, <code>ptDst</code>, which contains the // * transformed point, is returned for convenience. // * If <code>ptSrc</code> and <code>ptDst</code> are the same // * object, the input point is correctly overwritten with // * the transformed point. // * @param ptSrc the specified <code>Point2D</code> to be transformed // * @param ptDst the specified <code>Point2D</code> that stores the // * result of transforming <code>ptSrc</code> // * @return the <code>ptDst</code> after transforming // * <code>ptSrc</code> and stroring the result in <code>ptDst</code>. // */ // public Point2D transform(Point2D ptSrc, Point2D ptDst) { // if (ptDst == null) { // if (ptSrc instanceof Point2D.Double) { // ptDst = new Point2D.Double(); // } else { // ptDst = new Point2D.Float(); // } // } // // Copy source coords into local variables in case src == dst // double x = ptSrc.getX(); // double y = ptSrc.getY(); // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // ptDst.setLocation(x * m00 + y * m01 + m02, // x * m10 + y * m11 + m12); // return ptDst; // case (APPLY_SHEAR | APPLY_SCALE): // ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11); // return ptDst; // case (APPLY_SHEAR | APPLY_TRANSLATE): // ptDst.setLocation(y * m01 + m02, x * m10 + m12); // return ptDst; // case (APPLY_SHEAR): // ptDst.setLocation(y * m01, x * m10); // return ptDst; // case (APPLY_SCALE | APPLY_TRANSLATE): // ptDst.setLocation(x * m00 + m02, y * m11 + m12); // return ptDst; // case (APPLY_SCALE): // ptDst.setLocation(x * m00, y * m11); // return ptDst; // case (APPLY_TRANSLATE): // ptDst.setLocation(x + m02, y + m12); // return ptDst; // case (APPLY_IDENTITY): // ptDst.setLocation(x, y); // return ptDst; // } // // /* NOTREACHED */ // } // // /** // * Transforms an array of point objects by this transform. // * If any element of the <code>ptDst</code> array is // * <code>null</code>, a new <code>Point2D</code> object is allocated // * and stored into that element before storing the results of the // * transformation. // * <p> // * Note that this method does not take any precautions to // * avoid problems caused by storing results into <code>Point2D</code> // * objects that will be used as the source for calculations // * further down the source array. // * This method does guarantee that if a specified <code>Point2D</code> // * object is both the source and destination for the same single point // * transform operation then the results will not be stored until // * the calculations are complete to avoid storing the results on // * top of the operands. // * If, however, the destination <code>Point2D</code> object for one // * operation is the same object as the source <code>Point2D</code> // * object for another operation further down the source array then // * the original coordinates in that point are overwritten before // * they can be converted. // * @param ptSrc the array containing the source point objects // * @param ptDst the array into which the transform point objects are // * returned // * @param srcOff the offset to the first point object to be // * transformed in the source array // * @param dstOff the offset to the location of the first // * transformed point object that is stored in the destination array // * @param numPts the number of point objects to be transformed // */ // public void transform(Point2D[] ptSrc, int srcOff, // Point2D[] ptDst, int dstOff, // int numPts) { // int state = this.state; // while (--numPts >= 0) { // // Copy source coords into local variables in case src == dst // Point2D src = ptSrc[srcOff++]; // double x = src.getX(); // double y = src.getY(); // Point2D dst = ptDst[dstOff++]; // if (dst == null) { // if (src instanceof Point2D.Double) { // dst = new Point2D.Double(); // } else { // dst = new Point2D.Float(); // } // ptDst[dstOff - 1] = dst; // } // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // dst.setLocation(x * m00 + y * m01 + m02, // x * m10 + y * m11 + m12); // break; // case (APPLY_SHEAR | APPLY_SCALE): // dst.setLocation(x * m00 + y * m01, x * m10 + y * m11); // break; // case (APPLY_SHEAR | APPLY_TRANSLATE): // dst.setLocation(y * m01 + m02, x * m10 + m12); // break; // case (APPLY_SHEAR): // dst.setLocation(y * m01, x * m10); // break; // case (APPLY_SCALE | APPLY_TRANSLATE): // dst.setLocation(x * m00 + m02, y * m11 + m12); // break; // case (APPLY_SCALE): // dst.setLocation(x * m00, y * m11); // break; // case (APPLY_TRANSLATE): // dst.setLocation(x + m02, y + m12); // break; // case (APPLY_IDENTITY): // dst.setLocation(x, y); // break; // } // } // // /* NOTREACHED */ // } // // /** // * Transforms an array of floating point coordinates by this transform. // * The two coordinate array sections can be exactly the same or // * can be overlapping sections of the same array without affecting the // * validity of the results. // * This method ensures that no source coordinates are overwritten by a // * previous operation before they can be transformed. // * The coordinates are stored in the arrays starting at the specified // * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. // * @param srcPts the array containing the source point coordinates. // * Each point is stored as a pair of x, y coordinates. // * @param dstPts the array into which the transformed point coordinates // * are returned. Each point is stored as a pair of x, y // * coordinates. // * @param srcOff the offset to the first point to be transformed // * in the source array // * @param dstOff the offset to the location of the first // * transformed point that is stored in the destination array // * @param numPts the number of points to be transformed // */ // public void transform(float[] srcPts, int srcOff, // float[] dstPts, int dstOff, // int numPts) { // double M00, M01, M02, M10, M11, M12; // For caching // if (dstPts == srcPts && // dstOff > srcOff && dstOff < srcOff + numPts * 2) // { // // If the arrays overlap partially with the destination higher // // than the source and we transform the coordinates normally // // we would overwrite some of the later source coordinates // // with results of previous transformations. // // To get around this we use arraycopy to copy the points // // to their final destination with correct overwrite // // handling and then transform them in place in the new // // safer location. // System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); // // srcPts = dstPts; // They are known to be equal. // srcOff = dstOff; // } // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M01 = m01; M02 = m02; // M10 = m10; M11 = m11; M12 = m12; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02); // dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12); // } // return; // case (APPLY_SHEAR | APPLY_SCALE): // M00 = m00; M01 = m01; // M10 = m10; M11 = m11; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = (float) (M00 * x + M01 * y); // dstPts[dstOff++] = (float) (M10 * x + M11 * y); // } // return; // case (APPLY_SHEAR | APPLY_TRANSLATE): // M01 = m01; M02 = m02; // M10 = m10; M12 = m12; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02); // dstPts[dstOff++] = (float) (M10 * x + M12); // } // return; // case (APPLY_SHEAR): // M01 = m01; M10 = m10; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]); // dstPts[dstOff++] = (float) (M10 * x); // } // return; // case (APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M02 = m02; // M11 = m11; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02); // dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12); // } // return; // case (APPLY_SCALE): // M00 = m00; M11 = m11; // while (--numPts >= 0) { // dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]); // dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]); // } // return; // case (APPLY_TRANSLATE): // M02 = m02; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02); // dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12); // } // return; // case (APPLY_IDENTITY): // if (srcPts != dstPts || srcOff != dstOff) { // System.arraycopy(srcPts, srcOff, dstPts, dstOff, // numPts * 2); // } // return; // } // // /* NOTREACHED */ // } // // /** // * Transforms an array of double precision coordinates by this transform. // * The two coordinate array sections can be exactly the same or // * can be overlapping sections of the same array without affecting the // * validity of the results. // * This method ensures that no source coordinates are // * overwritten by a previous operation before they can be transformed. // * The coordinates are stored in the arrays starting at the indicated // * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. // * @param srcPts the array containing the source point coordinates. // * Each point is stored as a pair of x, y coordinates. // * @param dstPts the array into which the transformed point // * coordinates are returned. Each point is stored as a pair of // * x, y coordinates. // * @param srcOff the offset to the first point to be transformed // * in the source array // * @param dstOff the offset to the location of the first // * transformed point that is stored in the destination array // * @param numPts the number of point objects to be transformed // */ // public void transform(double[] srcPts, int srcOff, // double[] dstPts, int dstOff, // int numPts) { // double M00, M01, M02, M10, M11, M12; // For caching // if (dstPts == srcPts && // dstOff > srcOff && dstOff < srcOff + numPts * 2) // { // // If the arrays overlap partially with the destination higher // // than the source and we transform the coordinates normally // // we would overwrite some of the later source coordinates // // with results of previous transformations. // // To get around this we use arraycopy to copy the points // // to their final destination with correct overwrite // // handling and then transform them in place in the new // // safer location. // System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); // // srcPts = dstPts; // They are known to be equal. // srcOff = dstOff; // } // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M01 = m01; M02 = m02; // M10 = m10; M11 = m11; M12 = m12; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = M00 * x + M01 * y + M02; // dstPts[dstOff++] = M10 * x + M11 * y + M12; // } // return; // case (APPLY_SHEAR | APPLY_SCALE): // M00 = m00; M01 = m01; // M10 = m10; M11 = m11; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = M00 * x + M01 * y; // dstPts[dstOff++] = M10 * x + M11 * y; // } // return; // case (APPLY_SHEAR | APPLY_TRANSLATE): // M01 = m01; M02 = m02; // M10 = m10; M12 = m12; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02; // dstPts[dstOff++] = M10 * x + M12; // } // return; // case (APPLY_SHEAR): // M01 = m01; M10 = m10; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = M01 * srcPts[srcOff++]; // dstPts[dstOff++] = M10 * x; // } // return; // case (APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M02 = m02; // M11 = m11; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02; // dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12; // } // return; // case (APPLY_SCALE): // M00 = m00; M11 = m11; // while (--numPts >= 0) { // dstPts[dstOff++] = M00 * srcPts[srcOff++]; // dstPts[dstOff++] = M11 * srcPts[srcOff++]; // } // return; // case (APPLY_TRANSLATE): // M02 = m02; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = srcPts[srcOff++] + M02; // dstPts[dstOff++] = srcPts[srcOff++] + M12; // } // return; // case (APPLY_IDENTITY): // if (srcPts != dstPts || srcOff != dstOff) { // System.arraycopy(srcPts, srcOff, dstPts, dstOff, // numPts * 2); // } // return; // } // // /* NOTREACHED */ // } // // /** // * Transforms an array of floating point coordinates by this transform // * and stores the results into an array of doubles. // * The coordinates are stored in the arrays starting at the specified // * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. // * @param srcPts the array containing the source point coordinates. // * Each point is stored as a pair of x, y coordinates. // * @param dstPts the array into which the transformed point coordinates // * are returned. Each point is stored as a pair of x, y // * coordinates. // * @param srcOff the offset to the first point to be transformed // * in the source array // * @param dstOff the offset to the location of the first // * transformed point that is stored in the destination array // * @param numPts the number of points to be transformed // */ // public void transform(float[] srcPts, int srcOff, // double[] dstPts, int dstOff, // int numPts) { // double M00, M01, M02, M10, M11, M12; // For caching // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M01 = m01; M02 = m02; // M10 = m10; M11 = m11; M12 = m12; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = M00 * x + M01 * y + M02; // dstPts[dstOff++] = M10 * x + M11 * y + M12; // } // return; // case (APPLY_SHEAR | APPLY_SCALE): // M00 = m00; M01 = m01; // M10 = m10; M11 = m11; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = M00 * x + M01 * y; // dstPts[dstOff++] = M10 * x + M11 * y; // } // return; // case (APPLY_SHEAR | APPLY_TRANSLATE): // M01 = m01; M02 = m02; // M10 = m10; M12 = m12; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = M01 * srcPts[srcOff++] + M02; // dstPts[dstOff++] = M10 * x + M12; // } // return; // case (APPLY_SHEAR): // M01 = m01; M10 = m10; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = M01 * srcPts[srcOff++]; // dstPts[dstOff++] = M10 * x; // } // return; // case (APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M02 = m02; // M11 = m11; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = M00 * srcPts[srcOff++] + M02; // dstPts[dstOff++] = M11 * srcPts[srcOff++] + M12; // } // return; // case (APPLY_SCALE): // M00 = m00; M11 = m11; // while (--numPts >= 0) { // dstPts[dstOff++] = M00 * srcPts[srcOff++]; // dstPts[dstOff++] = M11 * srcPts[srcOff++]; // } // return; // case (APPLY_TRANSLATE): // M02 = m02; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = srcPts[srcOff++] + M02; // dstPts[dstOff++] = srcPts[srcOff++] + M12; // } // return; // case (APPLY_IDENTITY): // while (--numPts >= 0) { // dstPts[dstOff++] = srcPts[srcOff++]; // dstPts[dstOff++] = srcPts[srcOff++]; // } // return; // } // // /* NOTREACHED */ // } // // /** // * Transforms an array of double precision coordinates by this transform // * and stores the results into an array of floats. // * The coordinates are stored in the arrays starting at the specified // * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. // * @param srcPts the array containing the source point coordinates. // * Each point is stored as a pair of x, y coordinates. // * @param dstPts the array into which the transformed point // * coordinates are returned. Each point is stored as a pair of // * x, y coordinates. // * @param srcOff the offset to the first point to be transformed // * in the source array // * @param dstOff the offset to the location of the first // * transformed point that is stored in the destination array // * @param numPts the number of point objects to be transformed // */ // public void transform(double[] srcPts, int srcOff, // float[] dstPts, int dstOff, // int numPts) { // double M00, M01, M02, M10, M11, M12; // For caching // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M01 = m01; M02 = m02; // M10 = m10; M11 = m11; M12 = m12; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = (float) (M00 * x + M01 * y + M02); // dstPts[dstOff++] = (float) (M10 * x + M11 * y + M12); // } // return; // case (APPLY_SHEAR | APPLY_SCALE): // M00 = m00; M01 = m01; // M10 = m10; M11 = m11; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = (float) (M00 * x + M01 * y); // dstPts[dstOff++] = (float) (M10 * x + M11 * y); // } // return; // case (APPLY_SHEAR | APPLY_TRANSLATE): // M01 = m01; M02 = m02; // M10 = m10; M12 = m12; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++] + M02); // dstPts[dstOff++] = (float) (M10 * x + M12); // } // return; // case (APPLY_SHEAR): // M01 = m01; M10 = m10; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = (float) (M01 * srcPts[srcOff++]); // dstPts[dstOff++] = (float) (M10 * x); // } // return; // case (APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M02 = m02; // M11 = m11; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++] + M02); // dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++] + M12); // } // return; // case (APPLY_SCALE): // M00 = m00; M11 = m11; // while (--numPts >= 0) { // dstPts[dstOff++] = (float) (M00 * srcPts[srcOff++]); // dstPts[dstOff++] = (float) (M11 * srcPts[srcOff++]); // } // return; // case (APPLY_TRANSLATE): // M02 = m02; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = (float) (srcPts[srcOff++] + M02); // dstPts[dstOff++] = (float) (srcPts[srcOff++] + M12); // } // return; // case (APPLY_IDENTITY): // while (--numPts >= 0) { // dstPts[dstOff++] = (float) (srcPts[srcOff++]); // dstPts[dstOff++] = (float) (srcPts[srcOff++]); // } // return; // } // // /* NOTREACHED */ // } // // /** // * Inverse transforms the specified <code>ptSrc</code> and stores the // * result in <code>ptDst</code>. // * If <code>ptDst</code> is <code>null</code>, a new // * <code>Point2D</code> object is allocated and then the result of the // * transform is stored in this object. // * In either case, <code>ptDst</code>, which contains the transformed // * point, is returned for convenience. // * If <code>ptSrc</code> and <code>ptDst</code> are the same // * object, the input point is correctly overwritten with the // * transformed point. // * @param ptSrc the point to be inverse transformed // * @param ptDst the resulting transformed point // * @return <code>ptDst</code>, which contains the result of the // * inverse transform. // * @exception NoninvertibleTransformException if the matrix cannot be // * inverted. // */ // public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst) // throws NoninvertibleTransformException // { // if (ptDst == null) { // if (ptSrc instanceof Point2D.Double) { // ptDst = new Point2D.Double(); // } else { // ptDst = new Point2D.Float(); // } // } // // Copy source coords into local variables in case src == dst // double x = ptSrc.getX(); // double y = ptSrc.getY(); // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // x -= m02; // y -= m12; // /* NOBREAK */ // case (APPLY_SHEAR | APPLY_SCALE): // double det = m00 * m11 - m01 * m10; // if (Math.abs(det) <= Double.MIN_VALUE) { // throw new NoninvertibleTransformException("Determinant is "+ // det); // } // ptDst.setLocation((x * m11 - y * m01) / det, // (y * m00 - x * m10) / det); // return ptDst; // case (APPLY_SHEAR | APPLY_TRANSLATE): // x -= m02; // y -= m12; // /* NOBREAK */ // case (APPLY_SHEAR): // if (m01 == 0.0 || m10 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // ptDst.setLocation(y / m10, x / m01); // return ptDst; // case (APPLY_SCALE | APPLY_TRANSLATE): // x -= m02; // y -= m12; // /* NOBREAK */ // case (APPLY_SCALE): // if (m00 == 0.0 || m11 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // ptDst.setLocation(x / m00, y / m11); // return ptDst; // case (APPLY_TRANSLATE): // ptDst.setLocation(x - m02, y - m12); // return ptDst; // case (APPLY_IDENTITY): // ptDst.setLocation(x, y); // return ptDst; // } // // /* NOTREACHED */ // } // // /** // * Inverse transforms an array of double precision coordinates by // * this transform. // * The two coordinate array sections can be exactly the same or // * can be overlapping sections of the same array without affecting the // * validity of the results. // * This method ensures that no source coordinates are // * overwritten by a previous operation before they can be transformed. // * The coordinates are stored in the arrays starting at the specified // * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. // * @param srcPts the array containing the source point coordinates. // * Each point is stored as a pair of x, y coordinates. // * @param dstPts the array into which the transformed point // * coordinates are returned. Each point is stored as a pair of // * x, y coordinates. // * @param srcOff the offset to the first point to be transformed // * in the source array // * @param dstOff the offset to the location of the first // * transformed point that is stored in the destination array // * @param numPts the number of point objects to be transformed // * @exception NoninvertibleTransformException if the matrix cannot be // * inverted. // */ // public void inverseTransform(double[] srcPts, int srcOff, // double[] dstPts, int dstOff, // int numPts) // throws NoninvertibleTransformException // { // double M00, M01, M02, M10, M11, M12; // For caching // double det; // if (dstPts == srcPts && // dstOff > srcOff && dstOff < srcOff + numPts * 2) // { // // If the arrays overlap partially with the destination higher // // than the source and we transform the coordinates normally // // we would overwrite some of the later source coordinates // // with results of previous transformations. // // To get around this we use arraycopy to copy the points // // to their final destination with correct overwrite // // handling and then transform them in place in the new // // safer location. // System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); // // srcPts = dstPts; // They are known to be equal. // srcOff = dstOff; // } // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M01 = m01; M02 = m02; // M10 = m10; M11 = m11; M12 = m12; // det = M00 * M11 - M01 * M10; // if (Math.abs(det) <= Double.MIN_VALUE) { // throw new NoninvertibleTransformException("Determinant is "+ // det); // } // while (--numPts >= 0) { // double x = srcPts[srcOff++] - M02; // double y = srcPts[srcOff++] - M12; // dstPts[dstOff++] = (x * M11 - y * M01) / det; // dstPts[dstOff++] = (y * M00 - x * M10) / det; // } // return; // case (APPLY_SHEAR | APPLY_SCALE): // M00 = m00; M01 = m01; // M10 = m10; M11 = m11; // det = M00 * M11 - M01 * M10; // if (Math.abs(det) <= Double.MIN_VALUE) { // throw new NoninvertibleTransformException("Determinant is "+ // det); // } // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = (x * M11 - y * M01) / det; // dstPts[dstOff++] = (y * M00 - x * M10) / det; // } // return; // case (APPLY_SHEAR | APPLY_TRANSLATE): // M01 = m01; M02 = m02; // M10 = m10; M12 = m12; // if (M01 == 0.0 || M10 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // while (--numPts >= 0) { // double x = srcPts[srcOff++] - M02; // dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M10; // dstPts[dstOff++] = x / M01; // } // return; // case (APPLY_SHEAR): // M01 = m01; M10 = m10; // if (M01 == 0.0 || M10 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = srcPts[srcOff++] / M10; // dstPts[dstOff++] = x / M01; // } // return; // case (APPLY_SCALE | APPLY_TRANSLATE): // M00 = m00; M02 = m02; // M11 = m11; M12 = m12; // if (M00 == 0.0 || M11 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // while (--numPts >= 0) { // dstPts[dstOff++] = (srcPts[srcOff++] - M02) / M00; // dstPts[dstOff++] = (srcPts[srcOff++] - M12) / M11; // } // return; // case (APPLY_SCALE): // M00 = m00; M11 = m11; // if (M00 == 0.0 || M11 == 0.0) { // throw new NoninvertibleTransformException("Determinant is 0"); // } // while (--numPts >= 0) { // dstPts[dstOff++] = srcPts[srcOff++] / M00; // dstPts[dstOff++] = srcPts[srcOff++] / M11; // } // return; // case (APPLY_TRANSLATE): // M02 = m02; M12 = m12; // while (--numPts >= 0) { // dstPts[dstOff++] = srcPts[srcOff++] - M02; // dstPts[dstOff++] = srcPts[srcOff++] - M12; // } // return; // case (APPLY_IDENTITY): // if (srcPts != dstPts || srcOff != dstOff) { // System.arraycopy(srcPts, srcOff, dstPts, dstOff, // numPts * 2); // } // return; // } // // /* NOTREACHED */ // } // // /** // * Transforms the relative distance vector specified by // * <code>ptSrc</code> and stores the result in <code>ptDst</code>. // * A relative distance vector is transformed without applying the // * translation components of the affine transformation matrix // * using the following equations: // * <pre> // * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] // * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] // * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] // * </pre> // * If <code>ptDst</code> is <code>null</code>, a new // * <code>Point2D</code> object is allocated and then the result of the // * transform is stored in this object. // * In either case, <code>ptDst</code>, which contains the // * transformed point, is returned for convenience. // * If <code>ptSrc</code> and <code>ptDst</code> are the same object, // * the input point is correctly overwritten with the transformed // * point. // * @param ptSrc the distance vector to be delta transformed // * @param ptDst the resulting transformed distance vector // * @return <code>ptDst</code>, which contains the result of the // * transformation. // */ // public Point2D deltaTransform(Point2D ptSrc, Point2D ptDst) { // if (ptDst == null) { // if (ptSrc instanceof Point2D.Double) { // ptDst = new Point2D.Double(); // } else { // ptDst = new Point2D.Float(); // } // } // // Copy source coords into local variables in case src == dst // double x = ptSrc.getX(); // double y = ptSrc.getY(); // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // case (APPLY_SHEAR | APPLY_SCALE): // ptDst.setLocation(x * m00 + y * m01, x * m10 + y * m11); // return ptDst; // case (APPLY_SHEAR | APPLY_TRANSLATE): // case (APPLY_SHEAR): // ptDst.setLocation(y * m01, x * m10); // return ptDst; // case (APPLY_SCALE | APPLY_TRANSLATE): // case (APPLY_SCALE): // ptDst.setLocation(x * m00, y * m11); // return ptDst; // case (APPLY_TRANSLATE): // case (APPLY_IDENTITY): // ptDst.setLocation(x, y); // return ptDst; // } // // /* NOTREACHED */ // } // // /** // * Transforms an array of relative distance vectors by this // * transform. // * A relative distance vector is transformed without applying the // * translation components of the affine transformation matrix // * using the following equations: // * <pre> // * [ x' ] [ m00 m01 (m02) ] [ x ] [ m00x + m01y ] // * [ y' ] = [ m10 m11 (m12) ] [ y ] = [ m10x + m11y ] // * [ (1) ] [ (0) (0) ( 1 ) ] [ (1) ] [ (1) ] // * </pre> // * The two coordinate array sections can be exactly the same or // * can be overlapping sections of the same array without affecting the // * validity of the results. // * This method ensures that no source coordinates are // * overwritten by a previous operation before they can be transformed. // * The coordinates are stored in the arrays starting at the indicated // * offset in the order <code>[x0, y0, x1, y1, ..., xn, yn]</code>. // * @param srcPts the array containing the source distance vectors. // * Each vector is stored as a pair of relative x, y coordinates. // * @param dstPts the array into which the transformed distance vectors // * are returned. Each vector is stored as a pair of relative // * x, y coordinates. // * @param srcOff the offset to the first vector to be transformed // * in the source array // * @param dstOff the offset to the location of the first // * transformed vector that is stored in the destination array // * @param numPts the number of vector coordinate pairs to be // * transformed // */ // public void deltaTransform(double[] srcPts, int srcOff, // double[] dstPts, int dstOff, // int numPts) { // double M00, M01, M10, M11; // For caching // if (dstPts == srcPts && // dstOff > srcOff && dstOff < srcOff + numPts * 2) // { // // If the arrays overlap partially with the destination higher // // than the source and we transform the coordinates normally // // we would overwrite some of the later source coordinates // // with results of previous transformations. // // To get around this we use arraycopy to copy the points // // to their final destination with correct overwrite // // handling and then transform them in place in the new // // safer location. // System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); // // srcPts = dstPts; // They are known to be equal. // srcOff = dstOff; // } // switch (state) { // default: // stateError(); // /* NOTREACHED */ // case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): // case (APPLY_SHEAR | APPLY_SCALE): // M00 = m00; M01 = m01; // M10 = m10; M11 = m11; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // double y = srcPts[srcOff++]; // dstPts[dstOff++] = x * M00 + y * M01; // dstPts[dstOff++] = x * M10 + y * M11; // } // return; // case (APPLY_SHEAR | APPLY_TRANSLATE): // case (APPLY_SHEAR): // M01 = m01; M10 = m10; // while (--numPts >= 0) { // double x = srcPts[srcOff++]; // dstPts[dstOff++] = srcPts[srcOff++] * M01; // dstPts[dstOff++] = x * M10; // } // return; // case (APPLY_SCALE | APPLY_TRANSLATE): // case (APPLY_SCALE): // M00 = m00; M11 = m11; // while (--numPts >= 0) { // dstPts[dstOff++] = srcPts[srcOff++] * M00; // dstPts[dstOff++] = srcPts[srcOff++] * M11; // } // return; // case (APPLY_TRANSLATE): // case (APPLY_IDENTITY): // if (srcPts != dstPts || srcOff != dstOff) { // System.arraycopy(srcPts, srcOff, dstPts, dstOff, // numPts * 2); // } // return; // } // // /* NOTREACHED */ // } // //// //// /** //// * Returns a new {@link Shape} object defined by the geometry of the //// * specified <code>Shape</code> after it has been transformed by //// * this transform. //// * @param pSrc the specified <code>Shape</code> object to be //// * transformed by this transform. //// * @return a new <code>Shape</code> object that defines the geometry //// * of the transformed <code>Shape</code>. //// */ //// public Shape createTransformedShape(Shape pSrc) { //// if (pSrc == null) { //// return null; //// } //// //// if (pSrc instanceof GeneralPath) { //// return ((GeneralPath)pSrc).createTransformedShape(this); //// } else { //// PathIterator pi = pSrc.getPathIterator(this); //// GeneralPath gp = new GeneralPath(pi.getWindingRule()); //// gp.append(pi, false); //// return gp; //// } //// //// /* NOTREACHED */ //// } // // // Round values to sane precision for printing // // Note that Math.sin(Math.PI) has an error of about 10^-16 // private static double _matround(double matval) { // return Math.rint(matval * 1E15) / 1E15; // } // // /** // * Returns a <code>String</code> that represents the value of this // * {@link Object}. // * @return a <code>String</code> representing the value of this // * <code>Object</code>. // */ // public String toString() { // return ("AffineTransform[[" // + _matround(m00) + ", " // + _matround(m01) + ", " // + _matround(m02) + "], [" // + _matround(m10) + ", " // + _matround(m11) + ", " // + _matround(m12) + "]]"); // } // // /** // * Returns <code>true</code> if this <code>AffineTransform</code> is // * an identity transform. // * @return <code>true</code> if this <code>AffineTransform</code> is // * an identity transform; <code>false</code> otherwise. // */ // public boolean isIdentity() { // return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY)); // } // //// /** //// * Returns a copy of this <code>AffineTransform</code> object. //// * @return an <code>Object</code> that is a copy of this //// * <code>AffineTransform</code> object. //// */ //// public Object clone() { //// try { //// return super.clone(); //// } catch (CloneNotSupportedException e) { //// // this shouldn't happen, since we are Cloneable //// throw new InternalError(); //// } //// } // // /** // * Returns the hashcode for this transform. // * @return a hash code for this transform. // */ // public int hashCode() { // long bits = Double.doubleToLongBits(m00); // bits = bits * 31 + Double.doubleToLongBits(m01); // bits = bits * 31 + Double.doubleToLongBits(m02); // bits = bits * 31 + Double.doubleToLongBits(m10); // bits = bits * 31 + Double.doubleToLongBits(m11); // bits = bits * 31 + Double.doubleToLongBits(m12); // return (((int) bits) ^ ((int) (bits >> 32))); // } // // /** // * Returns <code>true</code> if this <code>AffineTransform</code> // * represents the same affine coordinate transform as the specified // * argument. // * @param obj the <code>Object</code> to test for equality with this // * <code>AffineTransform</code> // * @return <code>true</code> if <code>obj</code> equals this // * <code>AffineTransform</code> object; <code>false</code> otherwise. // */ // public boolean equals(Object obj) { // if (!(obj instanceof AffineTransform)) { // return false; // } // // AffineTransform a = (AffineTransform)obj; // // return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) && // (m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12)); // } //}