--- old/modules/graphics/src/main/java/javafx/scene/transform/Transform.java 2016-05-03 23:06:46.000000000 -0700 +++ new/modules/graphics/src/main/java/javafx/scene/transform/Transform.java 2016-05-03 23:06:46.000000000 -0700 @@ -35,10 +35,9 @@ import com.sun.javafx.util.WeakReferenceQueue; import com.sun.javafx.binding.ExpressionHelper; import com.sun.javafx.event.EventHandlerManager; -import com.sun.javafx.geom.transform.Affine2D; import com.sun.javafx.geom.transform.Affine3D; -import com.sun.javafx.geom.transform.AffineBase; import com.sun.javafx.geom.transform.BaseTransform; +import com.sun.javafx.scene.transform.TransformHelper; import com.sun.javafx.scene.transform.TransformUtils; import java.lang.ref.SoftReference; import javafx.beans.InvalidationListener; @@ -50,7 +49,6 @@ import javafx.event.EventHandler; import javafx.event.EventTarget; import javafx.event.EventType; -import javafx.geometry.BoundingBox; import javafx.geometry.Bounds; import javafx.geometry.Point2D; import javafx.geometry.Point3D; @@ -72,6 +70,62 @@ */ public abstract class Transform implements Cloneable, EventTarget { + static { + // This is used by classes in different packages to get access to + // private and package private methods. + TransformHelper.setTransformAccessor(new TransformHelper.TransformAccessor() { + + @Override + public void add(Transform transform, Node node) { + transform.add(node); + } + + @Override + public void remove(Transform transform, Node node) { + transform.remove(node); + } + + @Override + public void apply(Transform transform, Affine3D affine3D) { + transform.apply(affine3D); + } + + @Override + public BaseTransform derive(Transform transform, BaseTransform baseTransform) { + return transform.derive(baseTransform); + } + + @Override + public Transform createImmutableTransform() { + return Transform.createImmutableTransform(); + } + + @Override + public Transform createImmutableTransform( + double mxx, double mxy, double mxz, double tx, + double myx, double myy, double myz, double ty, + double mzx, double mzy, double mzz, double tz) { + return Transform.createImmutableTransform(mxx, mxy, mxz, tx, + myx, myy, myz, ty, mzx, mzy, mzz, tz); + } + + @Override + public Transform createImmutableTransform(Transform transform, + double mxx, double mxy, double mxz, double tx, + double myx, double myy, double myz, double ty, + double mzx, double mzy, double mzz, double tz) { + return Transform.createImmutableTransform(transform, + mxx, mxy, mxz, tx, myx, myy, myz, ty, mzx, mzy, mzz, tz); + } + + @Override + public Transform createImmutableTransform(Transform transform, + Transform left, Transform right) { + return Transform.createImmutableTransform(transform, left, right); + } + }); + } + /* ************************************************************************* * * * Factories * @@ -1991,35 +2045,15 @@ getMzx(); getMzy(); getMzz(); getTz(); } - /** - * @treatAsPrivate implementation detail - * @deprecated This is an internal API that is not intended for use and will be removed in the next version - */ - @Deprecated - public abstract void impl_apply(Affine3D t); + abstract void apply(Affine3D t); - /** - * @treatAsPrivate implementation detail - * @deprecated This is an internal API that is not intended for use and will be removed in the next version - */ - @Deprecated - public abstract BaseTransform impl_derive(BaseTransform t); + abstract BaseTransform derive(BaseTransform t); - /** - * @treatAsPrivate implementation detail - * @deprecated This is an internal API that is not intended for use and will be removed in the next version - */ - @Deprecated - public void impl_add(final Node node) { + private void add(final Node node) { nodes.add(node); } - /** - * @treatAsPrivate implementation detail - * @deprecated This is an internal API that is not intended for use and will be removed in the next version - */ - @Deprecated - public void impl_remove(final Node node) { + private void remove(final Node node) { nodes.remove(node); } @@ -2111,4 +2145,778 @@ inverseCache.clear(); } } + + /************************************************************************** + * ImmutableTransform Class and supporting methods + **************************************************************************/ + + static Transform createImmutableTransform() { + return new ImmutableTransform(); + } + + static Transform createImmutableTransform( + double mxx, double mxy, double mxz, double tx, + double myx, double myy, double myz, double ty, + double mzx, double mzy, double mzz, double tz) { + return new ImmutableTransform( + mxx, mxy, mxz, tx, + myx, myy, myz, ty, + mzx, mzy, mzz, tz); + } + + static Transform createImmutableTransform(Transform transform, + double mxx, double mxy, double mxz, double tx, + double myx, double myy, double myz, double ty, + double mzx, double mzy, double mzz, double tz) { + if (transform == null) { + return createImmutableTransform( + mxx, mxy, mxz, tx, + myx, myy, myz, ty, + mzx, mzy, mzz, tz); + } + ((Transform.ImmutableTransform) transform).setToTransform( + mxx, mxy, mxz, tx, + myx, myy, myz, ty, + mzx, mzy, mzz, tz); + return transform; + } + + static Transform createImmutableTransform(Transform transform, + Transform left, Transform right) { + if (transform == null) { + transform = createImmutableTransform(); + } + ((Transform.ImmutableTransform) transform).setToConcatenation( + (ImmutableTransform) left, (ImmutableTransform) right); + return transform; + } + + /** + * Immutable transformation with performance optimizations based on Affine. + * + * From user's perspective, this transform is immutable. However, we can + * modify it internally. This allows for reusing instances that were + * not handed to users. The caller is responsible for not modifying + * user-visible instances. + * + * Note: can't override Transform's package private methods so they cannot + * be optimized. Currently not a big deal. + */ + static class ImmutableTransform extends Transform { + + private static final int APPLY_IDENTITY = 0; + private static final int APPLY_TRANSLATE = 1; + private static final int APPLY_SCALE = 2; + private static final int APPLY_SHEAR = 4; + private static final int APPLY_NON_3D = 0; + private static final int APPLY_3D_COMPLEX = 4; + private transient int state2d; + private transient int state3d; + + private double xx; + private double xy; + private double xz; + private double yx; + private double yy; + private double yz; + private double zx; + private double zy; + private double zz; + private double xt; + private double yt; + private double zt; + + ImmutableTransform() { + xx = yy = zz = 1.0; + } + + ImmutableTransform(Transform transform) { + this(transform.getMxx(), transform.getMxy(), transform.getMxz(), + transform.getTx(), + transform.getMyx(), transform.getMyy(), transform.getMyz(), + transform.getTy(), + transform.getMzx(), transform.getMzy(), transform.getMzz(), + transform.getTz()); + } + + ImmutableTransform(double mxx, double mxy, double mxz, double tx, + double myx, double myy, double myz, double ty, + double mzx, double mzy, double mzz, double tz) { + xx = mxx; + xy = mxy; + xz = mxz; + xt = tx; + + yx = myx; + yy = myy; + yz = myz; + yt = ty; + + zx = mzx; + zy = mzy; + zz = mzz; + zt = tz; + + updateState(); + } + + // Beware: this is modifying immutable transform! + // It is private and it is there just for the purpose of reusing + // instances not given to users + private void setToTransform(double mxx, double mxy, double mxz, double tx, + double myx, double myy, double myz, double ty, + double mzx, double mzy, double mzz, double tz) + { + xx = mxx; + xy = mxy; + xz = mxz; + xt = tx; + yx = myx; + yy = myy; + yz = myz; + yt = ty; + zx = mzx; + zy = mzy; + zz = mzz; + zt = tz; + updateState(); + } + + // Beware: this is modifying immutable transform! + // It is private and it is there just for the purpose of reusing + // instances not given to users + private void setToConcatenation(ImmutableTransform left, ImmutableTransform right) { + if (left.state3d == APPLY_NON_3D && right.state3d == APPLY_NON_3D) { + xx = left.xx * right.xx + left.xy * right.yx; + xy = left.xx * right.xy + left.xy * right.yy; + xt = left.xx * right.xt + left.xy * right.yt + left.xt; + yx = left.yx * right.xx + left.yy * right.yx; + yy = left.yx * right.xy + left.yy * right.yy; + yt = left.yx * right.xt + left.yy * right.yt + left.yt; + if (state3d != APPLY_NON_3D) { + xz = yz = zx = zy = zt = 0.0; + zz = 1.0; + state3d = APPLY_NON_3D; + } + updateState2D(); + } else { + xx = left.xx * right.xx + left.xy * right.yx + left.xz * right.zx; + xy = left.xx * right.xy + left.xy * right.yy + left.xz * right.zy; + xz = left.xx * right.xz + left.xy * right.yz + left.xz * right.zz; + xt = left.xx * right.xt + left.xy * right.yt + left.xz * right.zt + left.xt; + yx = left.yx * right.xx + left.yy * right.yx + left.yz * right.zx; + yy = left.yx * right.xy + left.yy * right.yy + left.yz * right.zy; + yz = left.yx * right.xz + left.yy * right.yz + left.yz * right.zz; + yt = left.yx * right.xt + left.yy * right.yt + left.yz * right.zt + left.yt; + zx = left.zx * right.xx + left.zy * right.yx + left.zz * right.zx; + zy = left.zx * right.xy + left.zy * right.yy + left.zz * right.zy; + zz = left.zx * right.xz + left.zy * right.yz + left.zz * right.zz; + zt = left.zx * right.xt + left.zy * right.yt + left.zz * right.zt + left.zt; + updateState(); + } + // could be further optimized using the states, but that would + // require a lot of code (see Affine and all its append* methods) + } + + @Override + public double getMxx() { + return xx; + } + + @Override + public double getMxy() { + return xy; + } + + @Override + public double getMxz() { + return xz; + } + + @Override + public double getTx() { + return xt; + } + + @Override + public double getMyx() { + return yx; + } + + @Override + public double getMyy() { + return yy; + } + + @Override + public double getMyz() { + return yz; + } + + @Override + public double getTy() { + return yt; + } + + @Override + public double getMzx() { + return zx; + } + + @Override + public double getMzy() { + return zy; + } + + @Override + public double getMzz() { + return zz; + } + + @Override + public double getTz() { + return zt; + } + + /* ************************************************************************* + * * + * State getters * + * * + **************************************************************************/ + + @Override + public double determinant() { + switch(state3d) { + default: + stateError(); + // cannot reach + case APPLY_NON_3D: + switch (state2d) { + default: + stateError(); + // cannot reach + case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SHEAR | APPLY_SCALE: + return xx * yy - xy * yx; + case APPLY_SHEAR | APPLY_TRANSLATE: + case APPLY_SHEAR: + return -(xy* yx); + case APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SCALE: + return xx * yy; + case APPLY_TRANSLATE: + case APPLY_IDENTITY: + return 1.0; + } + case APPLY_TRANSLATE: + return 1.0; + case APPLY_SCALE: + case APPLY_SCALE | APPLY_TRANSLATE: + return xx * yy * zz; + case APPLY_3D_COMPLEX: + return (xx* (yy * zz - zy * yz) + + xy* (yz * zx - zz * yx) + + xz* (yx * zy - zx * yy)); + } + } + + @Override + public Transform createConcatenation(Transform transform) { + javafx.scene.transform.Affine a = new Affine(this); + a.append(transform); + return a; + } + + @Override + public javafx.scene.transform.Affine createInverse() throws NonInvertibleTransformException { + javafx.scene.transform.Affine t = new Affine(this); + t.invert(); + return t; + } + + @Override + public Transform clone() { + return new ImmutableTransform(this); + } + + /* ************************************************************************* + * * + * Transform, Inverse Transform * + * * + **************************************************************************/ + + @Override + public Point2D transform(double x, double y) { + ensureCanTransform2DPoint(); + + switch (state2d) { + default: + stateError(); + // cannot reach + case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE: + return new Point2D( + xx * x + xy * y + xt, + yx * x + yy * y + yt); + case APPLY_SHEAR | APPLY_SCALE: + return new Point2D( + xx * x + xy * y, + yx * x + yy * y); + case APPLY_SHEAR | APPLY_TRANSLATE: + return new Point2D( + xy * y + xt, + yx * x + yt); + case APPLY_SHEAR: + return new Point2D(xy * y, yx * x); + case APPLY_SCALE | APPLY_TRANSLATE: + return new Point2D( + xx * x + xt, + yy * y + yt); + case APPLY_SCALE: + return new Point2D(xx * x, yy * y); + case APPLY_TRANSLATE: + return new Point2D(x + xt, y + yt); + case APPLY_IDENTITY: + return new Point2D(x, y); + } + } + + @Override + public Point3D transform(double x, double y, double z) { + switch (state3d) { + default: + stateError(); + // cannot reach + case APPLY_NON_3D: + switch (state2d) { + default: + stateError(); + // cannot reach + case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE: + return new Point3D( + xx * x + xy * y + xt, + yx * x + yy * y + yt, z); + case APPLY_SHEAR | APPLY_SCALE: + return new Point3D( + xx * x + xy * y, + yx * x + yy * y, z); + case APPLY_SHEAR | APPLY_TRANSLATE: + return new Point3D( + xy * y + xt, yx * x + yt, + z); + case APPLY_SHEAR: + return new Point3D(xy * y, yx * x, z); + case APPLY_SCALE | APPLY_TRANSLATE: + return new Point3D( + xx * x + xt, yy * y + yt, + z); + case APPLY_SCALE: + return new Point3D(xx * x, yy * y, z); + case APPLY_TRANSLATE: + return new Point3D(x + xt, y + yt, z); + case APPLY_IDENTITY: + return new Point3D(x, y, z); + } + case APPLY_TRANSLATE: + return new Point3D(x + xt, y + yt, z + zt); + case APPLY_SCALE: + return new Point3D(xx * x, yy * y, zz * z); + case APPLY_SCALE | APPLY_TRANSLATE: + return new Point3D( + xx * x + xt, + yy * y + yt, + zz * z + zt); + case APPLY_3D_COMPLEX: + return new Point3D( + xx * x + xy * y + xz * z + xt, + yx * x + yy * y + yz * z + yt, + zx * x + zy * y + zz * z + zt); + } + } + + @Override + public Point2D deltaTransform(double x, double y) { + ensureCanTransform2DPoint(); + + switch (state2d) { + default: + stateError(); + // cannot reach + case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SHEAR | APPLY_SCALE: + return new Point2D( + xx * x + xy * y, + yx * x + yy * y); + case APPLY_SHEAR | APPLY_TRANSLATE: + case APPLY_SHEAR: + return new Point2D(xy * y, yx * x); + case APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SCALE: + return new Point2D(xx * x, yy * y); + case APPLY_TRANSLATE: + case APPLY_IDENTITY: + return new Point2D(x, y); + } + } + + @Override + public Point3D deltaTransform(double x, double y, double z) { + switch (state3d) { + default: + stateError(); + // cannot reach + case APPLY_NON_3D: + switch (state2d) { + default: + stateError(); + // cannot reach + case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SHEAR | APPLY_SCALE: + return new Point3D( + xx * x + xy * y, + yx * x + yy * y, z); + case APPLY_SHEAR | APPLY_TRANSLATE: + case APPLY_SHEAR: + return new Point3D(xy * y, yx * x, z); + case APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SCALE: + return new Point3D(xx * x, yy * y, z); + case APPLY_TRANSLATE: + case APPLY_IDENTITY: + return new Point3D(x, y, z); + } + case APPLY_TRANSLATE: + return new Point3D(x, y, z); + case APPLY_SCALE: + case APPLY_SCALE | APPLY_TRANSLATE: + return new Point3D(xx * x, yy * y, zz * z); + case APPLY_3D_COMPLEX: + return new Point3D( + xx * x + xy * y + xz * z, + yx * x + yy * y + yz * z, + zx * x + zy * y + zz * z); + } + } + + @Override + public Point2D inverseTransform(double x, double y) + throws NonInvertibleTransformException { + ensureCanTransform2DPoint(); + + switch (state2d) { + default: + return super.inverseTransform(x, y); + case APPLY_SHEAR | APPLY_TRANSLATE: + if (xy == 0.0 || yx == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point2D( + (1.0 / yx) * y - yt / yx, + (1.0 / xy) * x - xt / xy); + case APPLY_SHEAR: + if (xy == 0.0 || yx == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point2D((1.0 / yx) * y, (1.0 / xy) * x); + case APPLY_SCALE | APPLY_TRANSLATE: + if (xx == 0.0 || yy == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point2D( + (1.0 / xx) * x - xt / xx, + (1.0 / yy) * y - yt / yy); + case APPLY_SCALE: + if (xx == 0.0 || yy == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point2D((1.0 / xx) * x, (1.0 / yy) * y); + case APPLY_TRANSLATE: + return new Point2D(x - xt, y - yt); + case APPLY_IDENTITY: + return new Point2D(x, y); + } + } + + @Override + public Point3D inverseTransform(double x, double y, double z) + throws NonInvertibleTransformException { + switch(state3d) { + default: + stateError(); + // cannot reach + case APPLY_NON_3D: + switch (state2d) { + default: + return super.inverseTransform(x, y, z); + case APPLY_SHEAR | APPLY_TRANSLATE: + if (xy == 0.0 || yx == 0.0) { + throw new NonInvertibleTransformException( + "Determinant is 0"); + } + return new Point3D( + (1.0 / yx) * y - yt / yx, + (1.0 / xy) * x - xt / xy, z); + case APPLY_SHEAR: + if (xy == 0.0 || yx == 0.0) { + throw new NonInvertibleTransformException( + "Determinant is 0"); + } + return new Point3D( + (1.0 / yx) * y, + (1.0 / xy) * x, z); + case APPLY_SCALE | APPLY_TRANSLATE: + if (xx == 0.0 || yy == 0.0) { + throw new NonInvertibleTransformException( + "Determinant is 0"); + } + return new Point3D( + (1.0 / xx) * x - xt / xx, + (1.0 / yy) * y - yt / yy, z); + case APPLY_SCALE: + if (xx == 0.0 || yy == 0.0) { + throw new NonInvertibleTransformException( + "Determinant is 0"); + } + return new Point3D((1.0 / xx) * x, (1.0 / yy) * y, z); + case APPLY_TRANSLATE: + return new Point3D(x - xt, y - yt, z); + case APPLY_IDENTITY: + return new Point3D(x, y, z); + } + case APPLY_TRANSLATE: + return new Point3D(x - xt, y - yt, z - zt); + case APPLY_SCALE: + if (xx == 0.0 || yy == 0.0 || zz == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point3D( + (1.0 / xx) * x, + (1.0 / yy) * y, + (1.0 / zz) * z); + case APPLY_SCALE | APPLY_TRANSLATE: + if (xx == 0.0 || yy == 0.0 || zz == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point3D( + (1.0 / xx) * x - xt / xx, + (1.0 / yy) * y - yt / yy, + (1.0 / zz) * z - zt / zz); + case APPLY_3D_COMPLEX: + return super.inverseTransform(x, y, z); + } + } + + @Override + public Point2D inverseDeltaTransform(double x, double y) + throws NonInvertibleTransformException { + ensureCanTransform2DPoint(); + + switch (state2d) { + default: + return super.inverseDeltaTransform(x, y); + case APPLY_SHEAR | APPLY_TRANSLATE: + case APPLY_SHEAR: + if (xy == 0.0 || yx == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point2D((1.0 / yx) * y, (1.0 / xy) * x); + case APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SCALE: + if (xx == 0.0 || yy == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point2D((1.0 / xx) * x, (1.0 / yy) * y); + case APPLY_TRANSLATE: + case APPLY_IDENTITY: + return new Point2D(x, y); + } + } + + @Override + public Point3D inverseDeltaTransform(double x, double y, double z) + throws NonInvertibleTransformException { + switch(state3d) { + default: + stateError(); + // cannot reach + case APPLY_NON_3D: + switch (state2d) { + default: + return super.inverseDeltaTransform(x, y, z); + case APPLY_SHEAR | APPLY_TRANSLATE: + case APPLY_SHEAR: + if (xy == 0.0 || yx == 0.0) { + throw new NonInvertibleTransformException( + "Determinant is 0"); + } + return new Point3D( + (1.0 / yx) * y, + (1.0 / xy) * x, z); + case APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SCALE: + if (xx == 0.0 || yy == 0.0) { + throw new NonInvertibleTransformException( + "Determinant is 0"); + } + return new Point3D( + (1.0 / xx) * x, + (1.0 / yy) * y, z); + case APPLY_TRANSLATE: + case APPLY_IDENTITY: + return new Point3D(x, y, z); + } + + case APPLY_TRANSLATE: + return new Point3D(x, y, z); + case APPLY_SCALE | APPLY_TRANSLATE: + case APPLY_SCALE: + if (xx == 0.0 || yy == 0.0 || zz == 0.0) { + throw new NonInvertibleTransformException("Determinant is 0"); + } + return new Point3D( + (1.0 / xx) * x, + (1.0 / yy) * y, + (1.0 / zz) * z); + case APPLY_3D_COMPLEX: + return super.inverseDeltaTransform(x, y, z); + } + } + + /* ************************************************************************* + * * + * Other API * + * * + **************************************************************************/ + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Transform [\n"); + + sb.append("\t").append(xx); + sb.append(", ").append(xy); + sb.append(", ").append(xz); + sb.append(", ").append(xt); + sb.append('\n'); + sb.append("\t").append(yx); + sb.append(", ").append(yy); + sb.append(", ").append(yz); + sb.append(", ").append(yt); + sb.append('\n'); + sb.append("\t").append(zx); + sb.append(", ").append(zy); + sb.append(", ").append(zz); + sb.append(", ").append(zt); + + return sb.append("\n]").toString(); + } + + /* ************************************************************************* + * * + * Internal implementation stuff * + * * + **************************************************************************/ + + private void updateState() { + updateState2D(); + + state3d = APPLY_NON_3D; + + if (xz != 0.0 || + yz != 0.0 || + zx != 0.0 || + zy != 0.0) + { + state3d = APPLY_3D_COMPLEX; + } else { + if ((state2d & APPLY_SHEAR) == 0) { + if (zt != 0.0) { + state3d |= APPLY_TRANSLATE; + } + if (zz != 1.0) { + state3d |= APPLY_SCALE; + } + if (state3d != APPLY_NON_3D) { + state3d |= (state2d & (APPLY_SCALE | APPLY_TRANSLATE)); + } + } else { + if (zz != 1.0 || zt != 0.0) { + state3d = APPLY_3D_COMPLEX; + } + } + } + } + + private void updateState2D() { + if (xy == 0.0 && yx == 0.0) { + if (xx == 1.0 && yy == 1.0) { + if (xt == 0.0 && yt == 0.0) { + state2d = APPLY_IDENTITY; + } else { + state2d = APPLY_TRANSLATE; + } + } else { + if (xt == 0.0 && yt == 0.0) { + state2d = APPLY_SCALE; + } else { + state2d = (APPLY_SCALE | APPLY_TRANSLATE); + } + } + } else { + if (xx == 0.0 && yy == 0.0) { + if (xt == 0.0 && yt == 0.0) { + state2d = APPLY_SHEAR; + } else { + state2d = (APPLY_SHEAR | APPLY_TRANSLATE); + } + } else { + if (xt == 0.0 && yt == 0.0) { + state2d = (APPLY_SHEAR | APPLY_SCALE); + } else { + state2d = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE); + } + } + } + } + + void ensureCanTransform2DPoint() throws IllegalStateException { + if (state3d != APPLY_NON_3D) { + throw new IllegalStateException("Cannot transform 2D point " + + "with a 3D transform"); + } + } + + private static void stateError() { + throw new InternalError("missing case in a switch"); + } + + + @Override + void apply(final Affine3D trans) { + trans.concatenate(xx, xy, xz, xt, + yx, yy, yz, yt, + zx, zy, zz, zt); + } + + @Override + BaseTransform derive(final BaseTransform trans) { + return trans.deriveWithConcatenation(xx, xy, xz, xt, + yx, yy, yz, yt, + zx, zy, zz, zt); + } + + /** + * Used only by tests to check the 2d matrix state + */ + int getState2d() { + return state2d; + } + + /** + * Used only by tests to check the 3d matrix state + */ + int getState3d() { + return state3d; + } + + } + }