/* * Copyright (c) 2009, 2015, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.javafx.experiments.utils3d.geom.transform; import com.javafx.experiments.utils3d.geom.*; /** * */ public abstract class AffineBase extends BaseTransform { /** * 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 #APPLY_3D * @see #state */ protected 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 #APPLY_3D * @see #state */ protected 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 #APPLY_3D * @see #state */ protected 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 #APPLY_3D * @see #state */ protected static final int APPLY_SHEAR = 4; /** * This constant is used for the internal state variable to indicate * that the 3D (Z) components of the matrix (m*z and mz*) need * to be factored in to complete the transformation equation of this * transform. * @see #APPLY_IDENTITY * @see #APPLY_TRANSLATE * @see #APPLY_SCALE * @see #APPLY_SHEAR * @see #state */ protected static final int APPLY_3D = 8; /* * The following mask can be used to extract the 2D state constants from * a state variable for cases where we know we can ignore the 3D matrix * elements (such as in the 2D coordinate transform methods). */ protected static final int APPLY_2D_MASK = (APPLY_TRANSLATE | APPLY_SCALE | APPLY_SHEAR); protected static final int APPLY_2D_DELTA_MASK = (APPLY_SCALE | APPLY_SHEAR); /* * 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). */ protected static final int HI_SHIFT = 4; protected static final int HI_IDENTITY = APPLY_IDENTITY << HI_SHIFT; protected static final int HI_TRANSLATE = APPLY_TRANSLATE << HI_SHIFT; protected static final int HI_SCALE = APPLY_SCALE << HI_SHIFT; protected static final int HI_SHEAR = APPLY_SHEAR << HI_SHIFT; protected static final int HI_3D = APPLY_3D << HI_SHIFT; /** * The X coordinate scaling element of the 3x3 * affine transformation matrix. */ protected double mxx; /** * The Y coordinate shearing element of the 3x3 * affine transformation matrix. */ protected double myx; /** * The X coordinate shearing element of the 3x3 * affine transformation matrix. */ protected double mxy; /** * The Y coordinate scaling element of the 3x3 * affine transformation matrix. */ protected double myy; /** * The X coordinate of the translation element of the * 3x3 affine transformation matrix. */ protected double mxt; /** * The Y coordinate of the translation element of the * 3x3 affine transformation matrix. */ protected double myt; /** * 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 * @see #APPLY_3D */ protected 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 */ protected transient int type; /* * Convenience method used internally to throw exceptions when * a case was forgotten in a switch statement. */ protected static void stateError() { throw new InternalError("missing case in transform state switch"); } /** * 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. *
     *                     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
     * 
*/ protected void updateState() { updateState2D(); } /* * This variant of the method is for cases where we know the 3D elements * are set to identity... */ protected void updateState2D() { if (mxy == 0.0 && myx == 0.0) { if (mxx == 1.0 && myy == 1.0) { if (mxt == 0.0 && myt == 0.0) { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } else { state = APPLY_TRANSLATE; type = TYPE_TRANSLATION; } } else { if (mxt == 0.0 && myt == 0.0) { state = APPLY_SCALE; } else { state = (APPLY_SCALE | APPLY_TRANSLATE); } type = TYPE_UNKNOWN; } } else { if (mxx == 0.0 && myy == 0.0) { if (mxt == 0.0 && myt == 0.0) { state = APPLY_SHEAR; } else { state = (APPLY_SHEAR | APPLY_TRANSLATE); } } else { if (mxt == 0.0 && myt == 0.0) { state = (APPLY_SHEAR | APPLY_SCALE); } else { state = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE); } } type = TYPE_UNKNOWN; } } public int getType() { if (type == TYPE_UNKNOWN) { updateState(); // TODO: Is this really needed? (RT-26884) if (type == TYPE_UNKNOWN) { type = calculateType(); } } return type; } protected int calculateType() { int ret = ((state & APPLY_3D) == 0) ? TYPE_IDENTITY : TYPE_AFFINE_3D; boolean sgn0, sgn1; switch (state & APPLY_2D_MASK) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): ret |= TYPE_TRANSLATION; /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE): if (mxx * mxy + myx * myy != 0) { // Transformed unit vectors are not perpendicular... ret |= TYPE_GENERAL_TRANSFORM; break; } sgn0 = (mxx >= 0.0); sgn1 = (myy >= 0.0); if (sgn0 == sgn1) { // sgn(mxx) == sgn(myy) therefore sgn(mxy) == -sgn(myx) // This is the "unflipped" (right-handed) state if (mxx != myy || mxy != -myx) { ret |= (TYPE_GENERAL_ROTATION | TYPE_GENERAL_SCALE); } else if (mxx * myy - mxy * myx != 1.0) { ret |= (TYPE_GENERAL_ROTATION | TYPE_UNIFORM_SCALE); } else { ret |= TYPE_GENERAL_ROTATION; } } else { // sgn(mxx) == -sgn(myy) therefore sgn(mxy) == sgn(myx) // This is the "flipped" (left-handed) state if (mxx != -myy || mxy != myx) { ret |= (TYPE_GENERAL_ROTATION | TYPE_FLIP | TYPE_GENERAL_SCALE); } else if (mxx * myy - mxy * myx != 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 = (mxy >= 0.0); sgn1 = (myx >= 0.0); if (sgn0 != sgn1) { // Different signs - simple 90 degree rotation if (mxy != -myx) { ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); } else if (mxy != 1.0 && mxy != -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 (mxy == myx) { 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 = (mxx >= 0.0); sgn1 = (myy >= 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 (mxx == myy) { ret |= TYPE_UNIFORM_SCALE; } else { ret |= TYPE_GENERAL_SCALE; } } else { // Both scaling factors negative - 180 degree rotation if (mxx != myy) { ret |= (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_SCALE); } else if (mxx != -1.0) { ret |= (TYPE_QUADRANT_ROTATION | TYPE_UNIFORM_SCALE); } else { ret |= TYPE_QUADRANT_ROTATION; } } } else { // Scaling factor signs different - flip about some axis if (mxx == -myy) { if (mxx == 1.0 || mxx == -1.0) { ret |= TYPE_FLIP; } else { ret |= (TYPE_FLIP | TYPE_UNIFORM_SCALE); } } else { ret |= (TYPE_FLIP | TYPE_GENERAL_SCALE); } } break; case (APPLY_TRANSLATE): ret |= TYPE_TRANSLATION; break; case (APPLY_IDENTITY): break; } return ret; } /** * Returns the X coordinate scaling element (mxx) 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 */ @Override public double getMxx() { return mxx; } /** * Returns the Y coordinate scaling element (myy) 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 */ @Override public double getMyy() { return myy; } /** * Returns the X coordinate shearing element (mxy) 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 */ @Override public double getMxy() { return mxy; } /** * Returns the Y coordinate shearing element (myx) 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 */ @Override public double getMyx() { return myx; } /** * Returns the X coordinate of the translation element (mxt) 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 */ @Override public double getMxt() { return mxt; } /** * Returns the Y coordinate of the translation element (myt) 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 */ @Override public double getMyt() { return myt; } /** * Returns true if this Affine2D is * an identity transform. * @return true if this Affine2D is * an identity transform; false otherwise. */ public boolean isIdentity() { return (state == APPLY_IDENTITY || (getType() == TYPE_IDENTITY)); } @Override public boolean isTranslateOrIdentity() { return (state <= APPLY_TRANSLATE || (getType() <= TYPE_TRANSLATION)); } @Override public boolean is2D() { return (state < APPLY_3D || getType() <= TYPE_AFFINE2D_MASK); } /** * 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. *

* 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. *

* If this transform represents a uniform scale, as indicated by * the getType 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. *

* Mathematically, the determinant is calculated using the formula: *

     *      |  mxx  mxy  mxt  |
     *      |  myx  myy  myt  |  =  mxx * myy - mxy * myx
     *      |   0    0    1   |
     * 
* * @return the determinant of the matrix used to transform the * coordinates. * @see #getType * @see #createInverse * @see #inverseTransform * @see #TYPE_UNIFORM_SCALE */ public double getDeterminant() { // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): return mxx * myy - mxy * myx; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): return -(mxy * myx); case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): return mxx * myy; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): return 1.0; } } /** * Resets the 3D (Z) components of the matrix to identity settings * (if they are present). * This is a NOP unless the transform is Affine3D in which case it * needs to reset its added fields. */ protected abstract void reset3Delements(); /** * Resets this transform to the Identity transform. */ public void setToIdentity() { mxx = myy = 1.0; myx = mxy = mxt = myt = 0.0; reset3Delements(); state = APPLY_IDENTITY; type = TYPE_IDENTITY; } /** * Sets this transform to the matrix specified by the 6 * double precision values. * * @param mxx the X coordinate scaling element of the 3x3 matrix * @param myx the Y coordinate shearing element of the 3x3 matrix * @param mxy the X coordinate shearing element of the 3x3 matrix * @param myy the Y coordinate scaling element of the 3x3 matrix * @param mxt the X coordinate translation element of the 3x3 matrix * @param myt the Y coordinate translation element of the 3x3 matrix */ public void setTransform(double mxx, double myx, double mxy, double myy, double mxt, double myt) { this.mxx = mxx; this.myx = myx; this.mxy = mxy; this.myy = myy; this.mxt = mxt; this.myt = myt; reset3Delements(); updateState2D(); } /** * Sets this transform to a shearing transformation. * The matrix representing this transform becomes: *
     *      [   1   shx   0   ]
     *      [  shy   1    0   ]
     *      [   0    0    1   ]
     * 
* @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) { mxx = 1.0; mxy = shx; myx = shy; myy = 1.0; mxt = 0.0; myt = 0.0; reset3Delements(); if (shx != 0.0 || shy != 0.0) { state = (APPLY_SHEAR | APPLY_SCALE); type = TYPE_UNKNOWN; } else { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } } public Point2D transform(Point2D pt) { return transform(pt, pt); } /** * Transforms the specified ptSrc and stores the result * in ptDst. * If ptDst is null, a new {@link com.javafx.experiments.utils3d.geom.Point2D} * object is allocated and then the result of the transformation is * stored in this object. * In either case, ptDst, which contains the * transformed point, is returned for convenience. * If ptSrc and ptDst are the same * object, the input point is correctly overwritten with * the transformed point. * @param ptSrc the specified Point2D to be transformed * @param ptDst the specified Point2D that stores the * result of transforming ptSrc * @return the ptDst after transforming * ptSrc and stroring the result in ptDst. */ public Point2D transform(Point2D ptSrc, Point2D ptDst) { if (ptDst == null) { ptDst = new Point2D(); } // Copy source coords into local variables in case src == dst double x = ptSrc.x; double y = ptSrc.y; // double z = 0.0 // Note that this method also works for 3D transforms since the // mxz and myz matrix elements get multiplied by z (0.0) and the // mzx, mzy, mzz, and mzt elements only get used to calculate // the resulting Z coordinate, which we drop (ignore). switch (state & APPLY_2D_MASK) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): ptDst.setLocation((float)(x * mxx + y * mxy + mxt), (float)(x * myx + y * myy + myt)); return ptDst; case (APPLY_SHEAR | APPLY_SCALE): ptDst.setLocation((float)(x * mxx + y * mxy), (float)(x * myx + y * myy)); return ptDst; case (APPLY_SHEAR | APPLY_TRANSLATE): ptDst.setLocation((float)(y * mxy + mxt), (float)(x * myx + myt)); return ptDst; case (APPLY_SHEAR): ptDst.setLocation((float)(y * mxy), (float)(x * myx)); return ptDst; case (APPLY_SCALE | APPLY_TRANSLATE): ptDst.setLocation((float)(x * mxx + mxt), (float)(y * myy + myt)); return ptDst; case (APPLY_SCALE): ptDst.setLocation((float)(x * mxx), (float)(y * myy)); return ptDst; case (APPLY_TRANSLATE): ptDst.setLocation((float)(x + mxt), (float)(y + myt)); return ptDst; case (APPLY_IDENTITY): ptDst.setLocation((float) x, (float) y); return ptDst; } /* NOTREACHED */ } public Vec3d transform(Vec3d src, Vec3d dst) { if (dst == null) { dst = new Vec3d(); } // Copy source coords into local variables in case src == dst double x = src.x; double y = src.y; double z = src.z; // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): dst.x = x * mxx + y * mxy + mxt; dst.y = x * myx + y * myy + myt; dst.z = z; return dst; case (APPLY_SHEAR | APPLY_SCALE): dst.x = x * mxx + y * mxy; dst.y = x * myx + y * myy; dst.z = z; return dst; case (APPLY_SHEAR | APPLY_TRANSLATE): dst.x = y * mxy + mxt; dst.y = x * myx + myt; dst.z = z; return dst; case (APPLY_SHEAR): dst.x = y * mxy; dst.y = x * myx; dst.z = z; return dst; case (APPLY_SCALE | APPLY_TRANSLATE): dst.x = x * mxx + mxt; dst.y = y * myy + myt; dst.z = z; return dst; case (APPLY_SCALE): dst.x = x * mxx; dst.y = y * myy; dst.z = z; return dst; case (APPLY_TRANSLATE): dst.x = x + mxt; dst.y = y + myt; dst.z = z; return dst; case (APPLY_IDENTITY): dst.x = x; dst.y = y; dst.z = z; return dst; } /* NOTREACHED */ } /** * Transforms the specified src vector and stores the result * in dst vector, without applying the translation elements. * If dst is null, a new {@link com.javafx.experiments.utils3d.geom.Vec3d} * object is allocated and then the result of the transformation is * stored in this object. * In either case, dst, which contains the * transformed vector, is returned for convenience. * If src and dst are the same * object, the input vector is correctly overwritten with * the transformed vector. * @param src the specified Vec3d to be transformed * @param dst the specified Vec3d that stores the * result of transforming src * @return the dst vector after transforming * src and storing the result in dst. * @since JavaFX 8.0 */ public Vec3d deltaTransform(Vec3d src, Vec3d dst) { if (dst == null) { dst = new Vec3d(); } // Copy source coords into local variables in case src == dst double x = src.x; double y = src.y; double z = src.z; // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): dst.x = x * mxx + y * mxy ; dst.y = x * myx + y * myy; dst.z = z; return dst; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): dst.x = y * mxy; dst.y = x * myx; dst.z = z; return dst; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): dst.x = x * mxx; dst.y = y * myy; dst.z = z; return dst; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): dst.x = x; dst.y = y; dst.z = z; return dst; } /* NOTREACHED */ } private BaseBounds transform2DBounds(RectBounds src, RectBounds dst) { switch (state & APPLY_2D_MASK) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE): double x1 = src.getMinX(); double y1 = src.getMinY(); double x2 = src.getMaxX(); double y2 = src.getMaxY(); dst.setBoundsAndSort((float) (x1 * mxx + y1 * mxy), (float) (x1 * myx + y1 * myy), (float) (x2 * mxx + y2 * mxy), (float) (x2 * myx + y2 * myy)); dst.add((float) (x1 * mxx + y2 * mxy), (float) (x1 * myx + y2 * myy)); dst.add((float) (x2 * mxx + y1 * mxy), (float) (x2 * myx + y1 * myy)); dst.setBounds((float) (dst.getMinX() + mxt), (float) (dst.getMinY() + myt), (float) (dst.getMaxX() + mxt), (float) (dst.getMaxY() + myt)); break; case (APPLY_SHEAR | APPLY_TRANSLATE): dst.setBoundsAndSort((float) (src.getMinY() * mxy + mxt), (float) (src.getMinX() * myx + myt), (float) (src.getMaxY() * mxy + mxt), (float) (src.getMaxX() * myx + myt)); break; case (APPLY_SHEAR): dst.setBoundsAndSort((float) (src.getMinY() * mxy), (float) (src.getMinX() * myx), (float) (src.getMaxY() * mxy), (float) (src.getMaxX() * myx)); break; case (APPLY_SCALE | APPLY_TRANSLATE): dst.setBoundsAndSort((float) (src.getMinX() * mxx + mxt), (float) (src.getMinY() * myy + myt), (float) (src.getMaxX() * mxx + mxt), (float) (src.getMaxY() * myy + myt)); break; case (APPLY_SCALE): dst.setBoundsAndSort((float) (src.getMinX() * mxx), (float) (src.getMinY() * myy), (float) (src.getMaxX() * mxx), (float) (src.getMaxY() * myy)); break; case (APPLY_TRANSLATE): dst.setBounds((float) (src.getMinX() + mxt), (float) (src.getMinY() + myt), (float) (src.getMaxX() + mxt), (float) (src.getMaxY() + myt)); break; case (APPLY_IDENTITY): if (src != dst) { dst.setBounds(src); } break; } return dst; } // Note: Only use this method if src or dst is a 3D bounds private BaseBounds transform3DBounds(BaseBounds src, BaseBounds dst) { switch (state & APPLY_2D_MASK) { default: stateError(); /* NOTREACHED */ // Note: Assuming mxz = myz = mzx = mzy = mzt 0 case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE): double x1 = src.getMinX(); double y1 = src.getMinY(); double z1 = src.getMinZ(); double x2 = src.getMaxX(); double y2 = src.getMaxY(); double z2 = src.getMaxZ(); dst.setBoundsAndSort((float) (x1 * mxx + y1 * mxy), (float) (x1 * myx + y1 * myy), (float) z1, (float) (x2 * mxx + y2 * mxy), (float) (x2 * myx + y2 * myy), (float) z2); dst.add((float) (x1 * mxx + y2 * mxy), (float) (x1 * myx + y2 * myy), 0); dst.add((float) (x2 * mxx + y1 * mxy), (float) (x2 * myx + y1 * myy), 0); dst.deriveWithNewBounds((float) (dst.getMinX() + mxt), (float) (dst.getMinY() + myt), (float) dst.getMinZ(), (float) (dst.getMaxX() + mxt), (float) (dst.getMaxY() + myt), (float) dst.getMaxZ()); break; case (APPLY_SHEAR | APPLY_TRANSLATE): dst = dst.deriveWithNewBoundsAndSort((float) (src.getMinY() * mxy + mxt), (float) (src.getMinX() * myx + myt), (float) src.getMinZ(), (float) (src.getMaxY() * mxy + mxt), (float) (src.getMaxX() * myx + myt), (float) src.getMaxZ()); break; case (APPLY_SHEAR): dst = dst.deriveWithNewBoundsAndSort((float) (src.getMinY() * mxy), (float) (src.getMinX() * myx), (float) src.getMinZ(), (float) (src.getMaxY() * mxy), (float) (src.getMaxX() * myx), (float) src.getMaxZ()); break; case (APPLY_SCALE | APPLY_TRANSLATE): dst = dst.deriveWithNewBoundsAndSort((float) (src.getMinX() * mxx + mxt), (float) (src.getMinY() * myy + myt), (float) src.getMinZ(), (float) (src.getMaxX() * mxx + mxt), (float) (src.getMaxY() * myy + myt), (float) src.getMaxZ()); break; case (APPLY_SCALE): dst = dst.deriveWithNewBoundsAndSort((float) (src.getMinX() * mxx), (float) (src.getMinY() * myy), (float) src.getMinZ(), (float) (src.getMaxX() * mxx), (float) (src.getMaxY() * myy), (float) src.getMaxZ()); break; case (APPLY_TRANSLATE): dst = dst.deriveWithNewBounds((float) (src.getMinX() + mxt), (float) (src.getMinY() + myt), (float) src.getMinZ(), (float) (src.getMaxX() + mxt), (float) (src.getMaxY() + myt), (float) src.getMaxZ()); break; case (APPLY_IDENTITY): if (src != dst) { dst = dst.deriveWithNewBounds(src); } break; } return dst; } public BaseBounds transform(BaseBounds src, BaseBounds dst) { // assert(APPLY_3D was dealt with at a higher level) if (!src.is2D() || !dst.is2D()) { return transform3DBounds(src, dst); } return transform2DBounds((RectBounds)src, (RectBounds)dst); } public void transform(Rectangle src, Rectangle dst) { // assert(APPLY_3D was dealt with at a higher level) switch (state & APPLY_2D_MASK) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): RectBounds b = new RectBounds(src); //TODO: Need to verify that this is a safe cast ... (RT-26885) b = (RectBounds) transform(b, b); dst.setBounds(b); return; case (APPLY_TRANSLATE): Translate2D.transform(src, dst, mxt, myt); return; case (APPLY_IDENTITY): if (dst != src) { dst.setBounds(src); } return; } } /** * 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 [x0, y0, x1, y1, ..., xn, yn]. * @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) { doTransform(srcPts, srcOff, dstPts, dstOff, numPts, (this.state & APPLY_2D_MASK)); } /** * 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: *
     *  [  x' ]   [  m00  m01 (m02) ] [  x  ]   [ m00x + m01y ]
     *  [  y' ] = [  m10  m11 (m12) ] [  y  ] = [ m10x + m11y ]
     *  [ (1) ]   [  (0)  (0) ( 1 ) ] [ (1) ]   [     (1)     ]
     * 
* 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 [x0, y0, x1, y1, ..., xn, yn]. * @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(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) { doTransform(srcPts, srcOff, dstPts, dstOff, numPts, (this.state & APPLY_2D_DELTA_MASK)); } private void doTransform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts, int thestate) { double Mxx, Mxy, Mxt, Myx, Myy, Myt; // 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; } // Note that this method also works for 3D transforms since the // mxz and myz matrix elements get multiplied by z (0.0) and the // mzx, mzy, mzz, and mzt elements only get used to calculate // the resulting Z coordinate, which we drop (ignore). switch (thestate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxy = mxy; Mxt = mxt; Myx = myx; Myy = myy; Myt = myt; while (--numPts >= 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = (float) (Mxx * x + Mxy * y + Mxt); dstPts[dstOff++] = (float) (Myx * x + Myy * y + Myt); } return; case (APPLY_SHEAR | APPLY_SCALE): Mxx = mxx; Mxy = mxy; Myx = myx; Myy = myy; while (--numPts >= 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = (float) (Mxx * x + Mxy * y); dstPts[dstOff++] = (float) (Myx * x + Myy * y); } return; case (APPLY_SHEAR | APPLY_TRANSLATE): Mxy = mxy; Mxt = mxt; Myx = myx; Myt = myt; while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = (float) (Mxy * srcPts[srcOff++] + Mxt); dstPts[dstOff++] = (float) (Myx * x + Myt); } return; case (APPLY_SHEAR): Mxy = mxy; Myx = myx; while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = (float) (Mxy * srcPts[srcOff++]); dstPts[dstOff++] = (float) (Myx * x); } return; case (APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxt = mxt; Myy = myy; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = (float) (Mxx * srcPts[srcOff++] + Mxt); dstPts[dstOff++] = (float) (Myy * srcPts[srcOff++] + Myt); } return; case (APPLY_SCALE): Mxx = mxx; Myy = myy; while (--numPts >= 0) { dstPts[dstOff++] = (float) (Mxx * srcPts[srcOff++]); dstPts[dstOff++] = (float) (Myy * srcPts[srcOff++]); } return; case (APPLY_TRANSLATE): Mxt = mxt; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = (float) (srcPts[srcOff++] + Mxt); dstPts[dstOff++] = (float) (srcPts[srcOff++] + Myt); } 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 [x0, y0, x1, y1, ..., xn, yn]. * @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) { doTransform(srcPts, srcOff, dstPts, dstOff, numPts, (this.state & APPLY_2D_MASK)); } /** * 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: *
     *  [  x' ]   [  m00  m01 (m02) ] [  x  ]   [ m00x + m01y ]
     *  [  y' ] = [  m10  m11 (m12) ] [  y  ] = [ m10x + m11y ]
     *  [ (1) ]   [  (0)  (0) ( 1 ) ] [ (1) ]   [     (1)     ]
     * 
* 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 [x0, y0, x1, y1, ..., xn, yn]. * @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) { doTransform(srcPts, srcOff, dstPts, dstOff, numPts, (this.state & APPLY_2D_DELTA_MASK)); } private void doTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts, int thestate) { double Mxx, Mxy, Mxt, Myx, Myy, Myt; // 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; } // Note that this method also works for 3D transforms since the // mxz and myz matrix elements get multiplied by z (0.0) and the // mzx, mzy, mzz, and mzt elements only get used to calculate // the resulting Z coordinate, which we drop (ignore). switch (thestate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxy = mxy; Mxt = mxt; Myx = myx; Myy = myy; Myt = myt; while (--numPts >= 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = Mxx * x + Mxy * y + Mxt; dstPts[dstOff++] = Myx * x + Myy * y + Myt; } return; case (APPLY_SHEAR | APPLY_SCALE): Mxx = mxx; Mxy = mxy; Myx = myx; Myy = myy; while (--numPts >= 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = Mxx * x + Mxy * y; dstPts[dstOff++] = Myx * x + Myy * y; } return; case (APPLY_SHEAR | APPLY_TRANSLATE): Mxy = mxy; Mxt = mxt; Myx = myx; Myt = myt; while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = Mxy * srcPts[srcOff++] + Mxt; dstPts[dstOff++] = Myx * x + Myt; } return; case (APPLY_SHEAR): Mxy = mxy; Myx = myx; while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = Mxy * srcPts[srcOff++]; dstPts[dstOff++] = Myx * x; } return; case (APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxt = mxt; Myy = myy; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = Mxx * srcPts[srcOff++] + Mxt; dstPts[dstOff++] = Myy * srcPts[srcOff++] + Myt; } return; case (APPLY_SCALE): Mxx = mxx; Myy = myy; while (--numPts >= 0) { dstPts[dstOff++] = Mxx * srcPts[srcOff++]; dstPts[dstOff++] = Myy * srcPts[srcOff++]; } return; case (APPLY_TRANSLATE): Mxt = mxt; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = srcPts[srcOff++] + Mxt; dstPts[dstOff++] = srcPts[srcOff++] + Myt; } 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 [x0, y0, x1, y1, ..., xn, yn]. * @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 Mxx, Mxy, Mxt, Myx, Myy, Myt; // For caching // Note that this method also works for 3D transforms since the // mxz and myz matrix elements get multiplied by z (0.0) and the // mzx, mzy, mzz, and mzt elements only get used to calculate // the resulting Z coordinate, which we drop (ignore). switch (state & APPLY_2D_MASK) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxy = mxy; Mxt = mxt; Myx = myx; Myy = myy; Myt = myt; while (--numPts >= 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = Mxx * x + Mxy * y + Mxt; dstPts[dstOff++] = Myx * x + Myy * y + Myt; } return; case (APPLY_SHEAR | APPLY_SCALE): Mxx = mxx; Mxy = mxy; Myx = myx; Myy = myy; while (--numPts >= 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = Mxx * x + Mxy * y; dstPts[dstOff++] = Myx * x + Myy * y; } return; case (APPLY_SHEAR | APPLY_TRANSLATE): Mxy = mxy; Mxt = mxt; Myx = myx; Myt = myt; while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = Mxy * srcPts[srcOff++] + Mxt; dstPts[dstOff++] = Myx * x + Myt; } return; case (APPLY_SHEAR): Mxy = mxy; Myx = myx; while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = Mxy * srcPts[srcOff++]; dstPts[dstOff++] = Myx * x; } return; case (APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxt = mxt; Myy = myy; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = Mxx * srcPts[srcOff++] + Mxt; dstPts[dstOff++] = Myy * srcPts[srcOff++] + Myt; } return; case (APPLY_SCALE): Mxx = mxx; Myy = myy; while (--numPts >= 0) { dstPts[dstOff++] = Mxx * srcPts[srcOff++]; dstPts[dstOff++] = Myy * srcPts[srcOff++]; } return; case (APPLY_TRANSLATE): Mxt = mxt; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = srcPts[srcOff++] + Mxt; dstPts[dstOff++] = srcPts[srcOff++] + Myt; } 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 [x0, y0, x1, y1, ..., xn, yn]. * @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 Mxx, Mxy, Mxt, Myx, Myy, Myt; // For caching // Note that this method also works for 3D transforms since the // mxz and myz matrix elements get multiplied by z (0.0) and the // mzx, mzy, mzz, and mzt elements only get used to calculate // the resulting Z coordinate, which we drop (ignore). switch (state & APPLY_2D_MASK) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxy = mxy; Mxt = mxt; Myx = myx; Myy = myy; Myt = myt; while (--numPts >= 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = (float) (Mxx * x + Mxy * y + Mxt); dstPts[dstOff++] = (float) (Myx * x + Myy * y + Myt); } return; case (APPLY_SHEAR | APPLY_SCALE): Mxx = mxx; Mxy = mxy; Myx = myx; Myy = myy; while (--numPts >= 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; dstPts[dstOff++] = (float) (Mxx * x + Mxy * y); dstPts[dstOff++] = (float) (Myx * x + Myy * y); } return; case (APPLY_SHEAR | APPLY_TRANSLATE): Mxy = mxy; Mxt = mxt; Myx = myx; Myt = myt; while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = (float) (Mxy * srcPts[srcOff++] + Mxt); dstPts[dstOff++] = (float) (Myx * x + Myt); } return; case (APPLY_SHEAR): Mxy = mxy; Myx = myx; while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = (float) (Mxy * srcPts[srcOff++]); dstPts[dstOff++] = (float) (Myx * x); } return; case (APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxt = mxt; Myy = myy; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = (float) (Mxx * srcPts[srcOff++] + Mxt); dstPts[dstOff++] = (float) (Myy * srcPts[srcOff++] + Myt); } return; case (APPLY_SCALE): Mxx = mxx; Myy = myy; while (--numPts >= 0) { dstPts[dstOff++] = (float) (Mxx * srcPts[srcOff++]); dstPts[dstOff++] = (float) (Myy * srcPts[srcOff++]); } return; case (APPLY_TRANSLATE): Mxt = mxt; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = (float) (srcPts[srcOff++] + Mxt); dstPts[dstOff++] = (float) (srcPts[srcOff++] + Myt); } return; case (APPLY_IDENTITY): while (--numPts >= 0) { dstPts[dstOff++] = (float) (srcPts[srcOff++]); dstPts[dstOff++] = (float) (srcPts[srcOff++]); } return; } /* NOTREACHED */ } /** * Inverse transforms the specified ptSrc and stores the * result in ptDst. * If ptDst is null, a new * Point2D object is allocated and then the result of the * transform is stored in this object. * In either case, ptDst, which contains the transformed * point, is returned for convenience. * If ptSrc and ptDst 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 ptDst, 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) { ptDst = new Point2D(); } // Copy source coords into local variables in case src == dst double x = ptSrc.x; double y = ptSrc.y; // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): x -= mxt; y -= myt; /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE): double det = mxx * myy - mxy * myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } ptDst.setLocation((float)((x * myy - y * mxy) / det), (float)((y * mxx - x * myx) / det)); return ptDst; case (APPLY_SHEAR | APPLY_TRANSLATE): x -= mxt; y -= myt; /* NOBREAK */ case (APPLY_SHEAR): if (mxy == 0.0 || myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } ptDst.setLocation((float)(y / myx), (float)(x / mxy)); return ptDst; case (APPLY_SCALE | APPLY_TRANSLATE): x -= mxt; y -= myt; /* NOBREAK */ case (APPLY_SCALE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } ptDst.setLocation((float)(x / mxx), (float)(y / myy)); return ptDst; case (APPLY_TRANSLATE): ptDst.setLocation((float)(x - mxt), (float)(y - myt)); return ptDst; case (APPLY_IDENTITY): ptDst.setLocation((float) x, (float) y); return ptDst; } /* NOTREACHED */ } /** * Inverse transforms the specified src and stores the * result in dst. * If dst is null, a new * Vec3d object is allocated and then the result of the * transform is stored in this object. * In either case, dst, which contains the transformed * point, is returned for convenience. * If src and dst are the same * object, the input point is correctly overwritten with the * transformed point. * @param src the point to be inverse transformed * @param dst the resulting transformed point * @return dst, which contains the result of the * inverse transform. * @exception NoninvertibleTransformException if the matrix cannot be * inverted. */ @Override public Vec3d inverseTransform(Vec3d src, Vec3d dst) throws NoninvertibleTransformException { if (dst == null) { dst = new Vec3d(); } // Copy source coords into local variables in case src == dst double x = src.x; double y = src.y; double z = src.z; // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): x -= mxt; y -= myt; /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE): double det = mxx * myy - mxy * myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } dst.set(((x * myy - y * mxy) / det), ((y * mxx - x * myx) / det), z); return dst; case (APPLY_SHEAR | APPLY_TRANSLATE): x -= mxt; y -= myt; /* NOBREAK */ case (APPLY_SHEAR): if (mxy == 0.0 || myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst.set((y / myx), (x / mxy), z); return dst; case (APPLY_SCALE | APPLY_TRANSLATE): x -= mxt; y -= myt; /* NOBREAK */ case (APPLY_SCALE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst.set((x / mxx), (y / myy), z); return dst; case (APPLY_TRANSLATE): dst.set((x - mxt), (y - myt), z); return dst; case (APPLY_IDENTITY): dst.set(x, y, z); return dst; } /* NOTREACHED */ } /** * Inverse transforms the specified src vector and stores the * result in dst vector (without applying the translation * elements). * If dst is null, a new * Vec3d object is allocated and then the result of the * transform is stored in this object. * In either case, dst, which contains the transformed * vector, is returned for convenience. * If src and dst are the same * object, the input vector is correctly overwritten with the * transformed vector. * @param src the vector to be inverse transformed * @param dst the resulting transformed vector * @return dst, which contains the result of the * inverse transform. * @exception NoninvertibleTransformException if the matrix cannot be * inverted. * @since JavaFX 8.0 */ @Override public Vec3d inverseDeltaTransform(Vec3d src, Vec3d dst) throws NoninvertibleTransformException { if (dst == null) { dst = new Vec3d(); } // Copy source coords into local variables in case src == dst double x = src.x; double y = src.y; double z = src.z; // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): double det = mxx * myy - mxy * myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } dst.set(((x * myy - y * mxy) / det), ((y * mxx - x * myx) / det), z); return dst; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): if (mxy == 0.0 || myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst.set((y / myx), (x / mxy), z); return dst; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst.set((x / mxx), (y / myy), z); return dst; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): dst.set(x, y, z); return dst; } /* NOTREACHED */ } private BaseBounds inversTransform2DBounds(RectBounds src, RectBounds dst) throws NoninvertibleTransformException { switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE): double det = mxx * myy - mxy * myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } double x1 = src.getMinX() - mxt; double y1 = src.getMinY() - myt; double x2 = src.getMaxX() - mxt; double y2 = src.getMaxY() - myt; dst.setBoundsAndSort((float) ((x1 * myy - y1 * mxy) / det), (float) ((y1 * mxx - x1 * myx) / det), (float) ((x2 * myy - y2 * mxy) / det), (float) ((y2 * mxx - x2 * myx) / det)); dst.add((float) ((x2 * myy - y1 * mxy) / det), (float) ((y1 * mxx - x2 * myx) / det)); dst.add((float) ((x1 * myy - y2 * mxy) / det), (float) ((y2 * mxx - x1 * myx) / det)); return dst; case (APPLY_SHEAR | APPLY_TRANSLATE): if (mxy == 0.0 || myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst.setBoundsAndSort((float) ((src.getMinY() - myt) / myx), (float) ((src.getMinX() - mxt) / mxy), (float) ((src.getMaxY() - myt) / myx), (float) ((src.getMaxX() - mxt) / mxy)); break; case (APPLY_SHEAR): if (mxy == 0.0 || myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst.setBoundsAndSort((float) (src.getMinY() / myx), (float) (src.getMinX() / mxy), (float) (src.getMaxY() / myx), (float) (src.getMaxX() / mxy)); break; case (APPLY_SCALE | APPLY_TRANSLATE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst.setBoundsAndSort((float) ((src.getMinX() - mxt) / mxx), (float) ((src.getMinY() - myt) / myy), (float) ((src.getMaxX() - mxt) / mxx), (float) ((src.getMaxY() - myt) / myy)); break; case (APPLY_SCALE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst.setBoundsAndSort((float) (src.getMinX() / mxx), (float) (src.getMinY() / myy), (float) (src.getMaxX() / mxx), (float) (src.getMaxY() / myy)); break; case (APPLY_TRANSLATE): dst.setBounds((float) (src.getMinX() - mxt), (float) (src.getMinY() - myt), (float) (src.getMaxX() - mxt), (float) (src.getMaxY() - myt)); break; case (APPLY_IDENTITY): if (dst != src) { ((RectBounds) dst).setBounds((RectBounds) src); } break; } return dst; } // Note: Only use this method if src or dst is a 3D bounds private BaseBounds inversTransform3DBounds(BaseBounds src, BaseBounds dst) throws NoninvertibleTransformException { switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE): /* NOBREAK */ case (APPLY_SHEAR | APPLY_TRANSLATE): /* NOBREAK */ case (APPLY_SHEAR): double det = mxx * myy - mxy * myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is " + det); } double x1 = src.getMinX() - mxt; double y1 = src.getMinY() - myt; double z1 = src.getMinZ(); double x2 = src.getMaxX() - mxt; double y2 = src.getMaxY() - myt; double z2 = src.getMaxZ(); dst = dst.deriveWithNewBoundsAndSort( (float) ((x1 * myy - y1 * mxy) / det), (float) ((y1 * mxx - x1 * myx) / det), (float) (z1 / det), (float) ((x2 * myy - y2 * mxy) / det), (float) ((y2 * mxx - x2 * myx) / det), (float) (z2 / det)); dst.add((float) ((x2 * myy - y1 * mxy) / det), (float) ((y1 * mxx - x2 * myx) / det), 0); dst.add((float) ((x1 * myy - y2 * mxy) / det), (float) ((y2 * mxx - x1 * myx) / det), 0); return dst; case (APPLY_SCALE | APPLY_TRANSLATE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst = dst.deriveWithNewBoundsAndSort((float) ((src.getMinX() - mxt) / mxx), (float) ((src.getMinY() - myt) / myy), (float) src.getMinZ(), (float) ((src.getMaxX() - mxt) / mxx), (float) ((src.getMaxY() - myt) / myy), (float) src.getMaxZ()); break; case (APPLY_SCALE): if (mxx == 0.0 || myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } dst = dst.deriveWithNewBoundsAndSort((float) (src.getMinX() / mxx), (float) (src.getMinY() / myy), (float) src.getMinZ(), (float) (src.getMaxX() / mxx), (float) (src.getMaxY() / myy), (float) src.getMaxZ()); break; case (APPLY_TRANSLATE): dst = dst.deriveWithNewBounds((float) (src.getMinX() - mxt), (float) (src.getMinY() - myt), (float) src.getMinZ(), (float) (src.getMaxX() - mxt), (float) (src.getMaxY() - myt), (float) src.getMaxZ()); break; case (APPLY_IDENTITY): if (dst != src) { dst = dst.deriveWithNewBounds(src); } break; } return dst; } public BaseBounds inverseTransform(BaseBounds src, BaseBounds dst) throws NoninvertibleTransformException { // assert(APPLY_3D was dealt with at a higher level) if (!src.is2D() || !dst.is2D()) { return inversTransform3DBounds(src, dst); } return inversTransform2DBounds((RectBounds)src, (RectBounds)dst); } public void inverseTransform(Rectangle src, Rectangle dst) throws NoninvertibleTransformException { // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): RectBounds b = new RectBounds(src); //TODO: Need to verify this casting is safe .... (RT-26885) b = (RectBounds) inverseTransform(b, b); dst.setBounds(b); return; case (APPLY_TRANSLATE): Translate2D.transform(src, dst, -mxt, -myt); return; case (APPLY_IDENTITY): if (dst != src) { dst.setBounds(src); } return; } } /** * Inverse transforms an array of single 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 [x0, y0, x1, y1, ..., xn, yn]. * @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(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) throws NoninvertibleTransformException { doInverseTransform(srcPts, srcOff, dstPts, dstOff, numPts, state); } /** * Inverse transforms an array of single precision relative 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 [x0, y0, x1, y1, ..., xn, yn]. * @param srcPts the array containing the relative source coordinates. * Each point is stored as a pair of x, y coordinates. * @param dstPts the array into which the relative 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 inverseDeltaTransform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) throws NoninvertibleTransformException { doInverseTransform(srcPts, srcOff, dstPts, dstOff, numPts, state & ~APPLY_TRANSLATE); } /** * Inverse transforms an array of single precision coordinates by * this transform using the specified state type. */ private void doInverseTransform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts, int thestate) throws NoninvertibleTransformException { double Mxx, Mxy, Mxt, Myx, Myy, Myt; // 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; } // assert(APPLY_3D was dealt with at a higher level) switch (thestate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxy = mxy; Mxt = mxt; Myx = myx; Myy = myy; Myt = myt; det = Mxx * Myy - Mxy * Myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } while (--numPts >= 0) { double x = srcPts[srcOff++] - Mxt; double y = srcPts[srcOff++] - Myt; dstPts[dstOff++] = (float) ((x * Myy - y * Mxy) / det); dstPts[dstOff++] = (float) ((y * Mxx - x * Myx) / det); } return; case (APPLY_SHEAR | APPLY_SCALE): Mxx = mxx; Mxy = mxy; Myx = myx; Myy = myy; det = Mxx * Myy - Mxy * Myx; if (det == 0 || 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++] = (float) ((x * Myy - y * Mxy) / det); dstPts[dstOff++] = (float) ((y * Mxx - x * Myx) / det); } return; case (APPLY_SHEAR | APPLY_TRANSLATE): Mxy = mxy; Mxt = mxt; Myx = myx; Myt = myt; if (Mxy == 0.0 || Myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } while (--numPts >= 0) { double x = srcPts[srcOff++] - Mxt; dstPts[dstOff++] = (float) ((srcPts[srcOff++] - Myt) / Myx); dstPts[dstOff++] = (float) (x / Mxy); } return; case (APPLY_SHEAR): Mxy = mxy; Myx = myx; if (Mxy == 0.0 || Myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = (float) (srcPts[srcOff++] / Myx); dstPts[dstOff++] = (float) (x / Mxy); } return; case (APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxt = mxt; Myy = myy; Myt = myt; if (Mxx == 0.0 || Myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } while (--numPts >= 0) { dstPts[dstOff++] = (float) ((srcPts[srcOff++] - Mxt) / Mxx); dstPts[dstOff++] = (float) ((srcPts[srcOff++] - Myt) / Myy); } return; case (APPLY_SCALE): Mxx = mxx; Myy = myy; if (Mxx == 0.0 || Myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } while (--numPts >= 0) { dstPts[dstOff++] = (float) (srcPts[srcOff++] / Mxx); dstPts[dstOff++] = (float) (srcPts[srcOff++] / Myy); } return; case (APPLY_TRANSLATE): Mxt = mxt; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = (float) (srcPts[srcOff++] - Mxt); dstPts[dstOff++] = (float) (srcPts[srcOff++] - Myt); } return; case (APPLY_IDENTITY): if (srcPts != dstPts || srcOff != dstOff) { System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); } return; } /* 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 [x0, y0, x1, y1, ..., xn, yn]. * @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 Mxx, Mxy, Mxt, Myx, Myy, Myt; // 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; } // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxy = mxy; Mxt = mxt; Myx = myx; Myy = myy; Myt = myt; det = Mxx * Myy - Mxy * Myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } while (--numPts >= 0) { double x = srcPts[srcOff++] - Mxt; double y = srcPts[srcOff++] - Myt; dstPts[dstOff++] = (x * Myy - y * Mxy) / det; dstPts[dstOff++] = (y * Mxx - x * Myx) / det; } return; case (APPLY_SHEAR | APPLY_SCALE): Mxx = mxx; Mxy = mxy; Myx = myx; Myy = myy; det = Mxx * Myy - Mxy * Myx; if (det == 0 || 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 * Myy - y * Mxy) / det; dstPts[dstOff++] = (y * Mxx - x * Myx) / det; } return; case (APPLY_SHEAR | APPLY_TRANSLATE): Mxy = mxy; Mxt = mxt; Myx = myx; Myt = myt; if (Mxy == 0.0 || Myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } while (--numPts >= 0) { double x = srcPts[srcOff++] - Mxt; dstPts[dstOff++] = (srcPts[srcOff++] - Myt) / Myx; dstPts[dstOff++] = x / Mxy; } return; case (APPLY_SHEAR): Mxy = mxy; Myx = myx; if (Mxy == 0.0 || Myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } while (--numPts >= 0) { double x = srcPts[srcOff++]; dstPts[dstOff++] = srcPts[srcOff++] / Myx; dstPts[dstOff++] = x / Mxy; } return; case (APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxt = mxt; Myy = myy; Myt = myt; if (Mxx == 0.0 || Myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } while (--numPts >= 0) { dstPts[dstOff++] = (srcPts[srcOff++] - Mxt) / Mxx; dstPts[dstOff++] = (srcPts[srcOff++] - Myt) / Myy; } return; case (APPLY_SCALE): Mxx = mxx; Myy = myy; if (Mxx == 0.0 || Myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } while (--numPts >= 0) { dstPts[dstOff++] = srcPts[srcOff++] / Mxx; dstPts[dstOff++] = srcPts[srcOff++] / Myy; } return; case (APPLY_TRANSLATE): Mxt = mxt; Myt = myt; while (--numPts >= 0) { dstPts[dstOff++] = srcPts[srcOff++] - Mxt; dstPts[dstOff++] = srcPts[srcOff++] - Myt; } return; case (APPLY_IDENTITY): if (srcPts != dstPts || srcOff != dstOff) { System.arraycopy(srcPts, srcOff, dstPts, dstOff, numPts * 2); } return; } /* NOTREACHED */ } /** * Concatenates this transform with a translation transformation. * This is equivalent to calling concatenate(T), where T is an * Affine2D represented by the following matrix: *
     *      [   1    0    tx  ]
     *      [   0    1    ty  ]
     *      [   0    0    1   ]
     * 
* @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) { // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): mxt = tx * mxx + ty * mxy + mxt; myt = tx * myx + ty * myy + myt; if (mxt == 0.0 && myt == 0.0) { state = APPLY_SHEAR | APPLY_SCALE; if (type != TYPE_UNKNOWN) { type &= ~TYPE_TRANSLATION; } } return; case (APPLY_SHEAR | APPLY_SCALE): mxt = tx * mxx + ty * mxy; myt = tx * myx + ty * myy; if (mxt != 0.0 || myt != 0.0) { state = APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } return; case (APPLY_SHEAR | APPLY_TRANSLATE): mxt = ty * mxy + mxt; myt = tx * myx + myt; if (mxt == 0.0 && myt == 0.0) { state = APPLY_SHEAR; if (type != TYPE_UNKNOWN) { type &= ~TYPE_TRANSLATION; } } return; case (APPLY_SHEAR): mxt = ty * mxy; myt = tx * myx; if (mxt != 0.0 || myt != 0.0) { state = APPLY_SHEAR | APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } return; case (APPLY_SCALE | APPLY_TRANSLATE): mxt = tx * mxx + mxt; myt = ty * myy + myt; if (mxt == 0.0 && myt == 0.0) { state = APPLY_SCALE; if (type != TYPE_UNKNOWN) { type &= ~TYPE_TRANSLATION; } } return; case (APPLY_SCALE): mxt = tx * mxx; myt = ty * myy; if (mxt != 0.0 || myt != 0.0) { state = APPLY_SCALE | APPLY_TRANSLATE; type |= TYPE_TRANSLATION; } return; case (APPLY_TRANSLATE): mxt = tx + mxt; myt = ty + myt; if (mxt == 0.0 && myt == 0.0) { state = APPLY_IDENTITY; type = TYPE_IDENTITY; } return; case (APPLY_IDENTITY): mxt = tx; myt = ty; if (tx != 0.0 || ty != 0.0) { state = APPLY_TRANSLATE; type = TYPE_TRANSLATION; } return; } } // Utility methods to optimize rotate methods. // These tables translate the flags during predictable quadrant // rotations where the shear and scale values are swapped and negated. private static final int rot90conversion[] = { /* IDENTITY => */ APPLY_SHEAR, /* TRANSLATE (TR) => */ APPLY_SHEAR | APPLY_TRANSLATE, /* SCALE (SC) => */ APPLY_SHEAR, /* SC | TR => */ APPLY_SHEAR | APPLY_TRANSLATE, /* SHEAR (SH) => */ APPLY_SCALE, /* SH | TR => */ APPLY_SCALE | APPLY_TRANSLATE, /* SH | SC => */ APPLY_SHEAR | APPLY_SCALE, /* SH | SC | TR => */ APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE, }; protected final void rotate90() { double M0 = mxx; mxx = mxy; mxy = -M0; M0 = myx; myx = myy; myy = -M0; int newstate = rot90conversion[this.state]; if ((newstate & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && mxx == 1.0 && myy == 1.0) { newstate -= APPLY_SCALE; } this.state = newstate; type = TYPE_UNKNOWN; } protected final void rotate180() { mxx = -mxx; myy = -myy; int oldstate = this.state; if ((oldstate & (APPLY_SHEAR)) != 0) { // If there was a shear, then this rotation has no // effect on the state. mxy = -mxy; myx = -myx; } else { // No shear means the SCALE state may toggle when // m00 and m11 are negated. if (mxx == 1.0 && myy == 1.0) { this.state = oldstate & ~APPLY_SCALE; } else { this.state = oldstate | APPLY_SCALE; } } type = TYPE_UNKNOWN; } protected final void rotate270() { double M0 = mxx; mxx = -mxy; mxy = M0; M0 = myx; myx = -myy; myy = M0; int newstate = rot90conversion[this.state]; if ((newstate & (APPLY_SHEAR | APPLY_SCALE)) == APPLY_SCALE && mxx == 1.0 && myy == 1.0) { newstate -= APPLY_SCALE; } this.state = newstate; type = TYPE_UNKNOWN; } /** * Concatenates this transform with a rotation transformation. * This is equivalent to calling concatenate(R), where R is an * Affine2D represented by the following matrix: *
     *      [   cos(theta)    -sin(theta)    0   ]
     *      [   sin(theta)     cos(theta)    0   ]
     *      [       0              0         1   ]
     * 
* Rotating by a positive angle theta rotates points on the positive * X axis toward the positive Y axis. * Note also the discussion of * Handling 90-Degree Rotations * above. * @param theta the angle of rotation measured in radians */ public void rotate(double theta) { // assert(APPLY_3D was dealt with at a higher level) double sin = Math.sin(theta); if (sin == 1.0) { rotate90(); } else if (sin == -1.0) { rotate270(); } else { double cos = Math.cos(theta); if (cos == -1.0) { rotate180(); } else if (cos != 1.0) { double M0, M1; M0 = mxx; M1 = mxy; mxx = cos * M0 + sin * M1; mxy = -sin * M0 + cos * M1; M0 = myx; M1 = myy; myx = cos * M0 + sin * M1; myy = -sin * M0 + cos * M1; updateState2D(); } } } /** * Concatenates this transform with a scaling transformation. * This is equivalent to calling concatenate(S), where S is an * Affine2D represented by the following matrix: *
     *      [   sx   0    0   ]
     *      [   0    sy   0   ]
     *      [   0    0    1   ]
     * 
* @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 mystate = this.state; // assert(APPLY_3D was dealt with at a higher level) switch (mystate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): mxx *= sx; myy *= sy; /* NOBREAK */ case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): mxy *= sy; myx *= sx; if (mxy == 0 && myx == 0) { mystate &= APPLY_TRANSLATE; if (mxx == 1.0 && myy == 1.0) { this.type = (mystate == APPLY_IDENTITY ? TYPE_IDENTITY : TYPE_TRANSLATION); } else { mystate |= APPLY_SCALE; this.type = TYPE_UNKNOWN; } this.state = mystate; } return; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): mxx *= sx; myy *= sy; if (mxx == 1.0 && myy == 1.0) { this.state = (mystate &= APPLY_TRANSLATE); this.type = (mystate == APPLY_IDENTITY ? TYPE_IDENTITY : TYPE_TRANSLATION); } else { this.type = TYPE_UNKNOWN; } return; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): mxx = sx; myy = sy; if (sx != 1.0 || sy != 1.0) { this.state = mystate | 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 * Affine2D represented by the following matrix: *
     *      [   1   shx   0   ]
     *      [  shy   1    0   ]
     *      [   0    0    1   ]
     * 
* @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 mystate = this.state; // assert(APPLY_3D was dealt with at a higher level) switch (mystate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SHEAR | APPLY_SCALE): double M0, M1; M0 = mxx; M1 = mxy; mxx = M0 + M1 * shy; mxy = M0 * shx + M1; M0 = myx; M1 = myy; myx = M0 + M1 * shy; myy = M0 * shx + M1; updateState2D(); return; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): mxx = mxy * shy; myy = myx * shx; if (mxx != 0.0 || myy != 0.0) { this.state = mystate | APPLY_SCALE; } this.type = TYPE_UNKNOWN; return; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): mxy = mxx * shx; myx = myy * shy; if (mxy != 0.0 || myx != 0.0) { this.state = mystate | APPLY_SHEAR; } this.type = TYPE_UNKNOWN; return; case (APPLY_TRANSLATE): case (APPLY_IDENTITY): mxy = shx; myx = shy; if (mxy != 0.0 || myx != 0.0) { this.state = mystate | APPLY_SCALE | APPLY_SHEAR; this.type = TYPE_UNKNOWN; } return; } } /** * Concatenates a BaseTransform Tx to * this Affine2D Cx in the most commonly useful * way to provide a new user space * that is mapped to the former user space by Tx. * Cx is updated to perform the combined transformation. * Transforming a point p by the updated transform Cx' is * equivalent to first transforming p by Tx 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 Tx is represented * by the matrix [Tx] then this method does the following: *
     *      [this] = [this] x [Tx]
     * 
* @param Tx the BaseTransform object to be * concatenated with this Affine2D object. * @see #preConcatenate */ public void concatenate(BaseTransform Tx) { switch (Tx.getDegree()) { case IDENTITY: return; case TRANSLATE_2D: translate(Tx.getMxt(), Tx.getMyt()); return; case AFFINE_2D: break; default: if (!Tx.is2D()) { degreeError(Degree.AFFINE_2D); } // TODO: Optimize - we need an AffineBase below due to the cast // For now, there is no other kind of transform that will get // here so we are already essentially optimal, but if we have // a different type of transform that reaches here we should // try to avoid this garbage... (RT-26884) if (!(Tx instanceof AffineBase)) { Tx = new Affine2D(Tx); } break; } double M0, M1; double Txx, Txy, Tyx, Tyy; double Txt, Tyt; int mystate = state; AffineBase at = (AffineBase) Tx; int txstate = at.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): mxy = at.mxy; myx = at.myx; /* NOBREAK */ case (HI_SCALE | HI_TRANSLATE | APPLY_IDENTITY): mxx = at.mxx; myy = at.myy; /* NOBREAK */ case (HI_TRANSLATE | APPLY_IDENTITY): mxt = at.mxt; myt = at.myt; state = txstate; type = at.type; return; case (HI_SHEAR | HI_SCALE | APPLY_IDENTITY): mxy = at.mxy; myx = at.myx; /* NOBREAK */ case (HI_SCALE | APPLY_IDENTITY): mxx = at.mxx; myy = at.myy; state = txstate; type = at.type; return; case (HI_SHEAR | HI_TRANSLATE | APPLY_IDENTITY): mxt = at.mxt; myt = at.myt; /* NOBREAK */ case (HI_SHEAR | APPLY_IDENTITY): mxy = at.mxy; myx = at.myx; mxx = myy = 0.0; state = txstate; type = at.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(at.mxt, at.myt); 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(at.mxx, at.myy); return; /* ---------- Tx == SHEAR cases ---------- */ case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SHEAR | APPLY_SCALE): Txy = at.mxy; Tyx = at.myx; M0 = mxx; mxx = mxy * Tyx; mxy = M0 * Txy; M0 = myx; myx = myy * Tyx; myy = M0 * Txy; type = TYPE_UNKNOWN; return; case (HI_SHEAR | APPLY_SHEAR | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SHEAR): mxx = mxy * at.myx; mxy = 0.0; myy = myx * at.mxy; myx = 0.0; state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); type = TYPE_UNKNOWN; return; case (HI_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): case (HI_SHEAR | APPLY_SCALE): mxy = mxx * at.mxy; mxx = 0.0; myx = myy * at.myx; myy = 0.0; state = mystate ^ (APPLY_SHEAR | APPLY_SCALE); type = TYPE_UNKNOWN; return; case (HI_SHEAR | APPLY_TRANSLATE): mxx = 0.0; mxy = at.mxy; myx = at.myx; myy = 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... Txx = at.mxx; Txy = at.mxy; Txt = at.mxt; Tyx = at.myx; Tyy = at.myy; Tyt = at.myt; switch (mystate) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE): state = mystate | txstate; /* NOBREAK */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): M0 = mxx; M1 = mxy; mxx = Txx * M0 + Tyx * M1; mxy = Txy * M0 + Tyy * M1; mxt += Txt * M0 + Tyt * M1; M0 = myx; M1 = myy; myx = Txx * M0 + Tyx * M1; myy = Txy * M0 + Tyy * M1; myt += Txt * M0 + Tyt * M1; type = TYPE_UNKNOWN; return; case (APPLY_SHEAR | APPLY_TRANSLATE): case (APPLY_SHEAR): M0 = mxy; mxx = Tyx * M0; mxy = Tyy * M0; mxt += Tyt * M0; M0 = myx; myx = Txx * M0; myy = Txy * M0; myt += Txt * M0; break; case (APPLY_SCALE | APPLY_TRANSLATE): case (APPLY_SCALE): M0 = mxx; mxx = Txx * M0; mxy = Txy * M0; mxt += Txt * M0; M0 = myy; myx = Tyx * M0; myy = Tyy * M0; myt += Tyt * M0; break; case (APPLY_TRANSLATE): mxx = Txx; mxy = Txy; mxt += Txt; myx = Tyx; myy = Tyy; myt += Tyt; state = txstate | APPLY_TRANSLATE; type = TYPE_UNKNOWN; return; } updateState2D(); } /** * Similar to {@link #concatenate(com.javafx.experiments.utils3d.geom.transform.BaseTransform)}, * passing the individual elements of the transformation. */ public void concatenate(double Txx, double Txy, double Txt, double Tyx, double Tyy, double Tyt) { double rxx = (mxx * Txx + mxy * Tyx /* + mxt * 0.0 */); double rxy = (mxx * Txy + mxy * Tyy /* + mxt * 0.0 */); double rxt = (mxx * Txt + mxy * Tyt + mxt /* * 1.0 */); double ryx = (myx * Txx + myy * Tyx /* + myt * 0.0 */); double ryy = (myx * Txy + myy * Tyy /* + myt * 0.0 */); double ryt = (myx * Txt + myy * Tyt + myt /* * 1.0 */); this.mxx = rxx; this.mxy = rxy; this.mxt = rxt; this.myx = ryx; this.myy = ryy; this.myt = ryt; updateState(); } /** * Sets this transform to the inverse of itself. * 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)). *

* 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 getDeterminant method can be used to determine if this * transform has no inverse, in which case an exception will be * thrown if the invert method is called. * @see #getDeterminant * @exception NoninvertibleTransformException * if the matrix cannot be inverted. */ public void invert() throws NoninvertibleTransformException { double Mxx, Mxy, Mxt; double Myx, Myy, Myt; double det; // assert(APPLY_3D was dealt with at a higher level) switch (state) { default: stateError(); /* NOTREACHED */ case (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxy = mxy; Mxt = mxt; Myx = myx; Myy = myy; Myt = myt; det = Mxx * Myy - Mxy * Myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } mxx = Myy / det; myx = -Myx / det; mxy = -Mxy / det; myy = Mxx / det; mxt = (Mxy * Myt - Myy * Mxt) / det; myt = (Myx * Mxt - Mxx * Myt) / det; break; case (APPLY_SHEAR | APPLY_SCALE): Mxx = mxx; Mxy = mxy; Myx = myx; Myy = myy; det = Mxx * Myy - Mxy * Myx; if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) { throw new NoninvertibleTransformException("Determinant is "+ det); } mxx = Myy / det; myx = -Myx / det; mxy = -Mxy / det; myy = Mxx / det; // m02 = 0.0; // m12 = 0.0; break; case (APPLY_SHEAR | APPLY_TRANSLATE): Mxy = mxy; Mxt = mxt; Myx = myx; Myt = myt; if (Mxy == 0.0 || Myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } // m00 = 0.0; myx = 1.0 / Mxy; mxy = 1.0 / Myx; // m11 = 0.0; mxt = -Myt / Myx; myt = -Mxt / Mxy; break; case (APPLY_SHEAR): Mxy = mxy; Myx = myx; if (Mxy == 0.0 || Myx == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } // m00 = 0.0; myx = 1.0 / Mxy; mxy = 1.0 / Myx; // m11 = 0.0; // m02 = 0.0; // m12 = 0.0; break; case (APPLY_SCALE | APPLY_TRANSLATE): Mxx = mxx; Mxt = mxt; Myy = myy; Myt = myt; if (Mxx == 0.0 || Myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } mxx = 1.0 / Mxx; // m10 = 0.0; // m01 = 0.0; myy = 1.0 / Myy; mxt = -Mxt / Mxx; myt = -Myt / Myy; break; case (APPLY_SCALE): Mxx = mxx; Myy = myy; if (Mxx == 0.0 || Myy == 0.0) { throw new NoninvertibleTransformException("Determinant is 0"); } mxx = 1.0 / Mxx; // m10 = 0.0; // m01 = 0.0; myy = 1.0 / Myy; // m02 = 0.0; // m12 = 0.0; break; case (APPLY_TRANSLATE): // m00 = 1.0; // m10 = 0.0; // m01 = 0.0; // m11 = 1.0; mxt = -mxt; myt = -myt; break; case (APPLY_IDENTITY): // m00 = 1.0; // m10 = 0.0; // m01 = 0.0; // m11 = 1.0; // m02 = 0.0; // m12 = 0.0; break; } } }