/* * 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;
}
}
}