/* * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.javafx.geom.transform; import com.sun.javafx.geom.BaseBounds; import com.sun.javafx.geom.Point2D; import com.sun.javafx.geom.Rectangle; import com.sun.javafx.geom.Shape; import com.sun.javafx.geom.Vec3d; /** * */ public abstract class BaseTransform implements CanTransformVec3d{ public static final BaseTransform IDENTITY_TRANSFORM = new Identity(); public static enum Degree { IDENTITY, TRANSLATE_2D, AFFINE_2D, TRANSLATE_3D, AFFINE_3D, } /* * This constant is only useful for a cached type field. * It indicates that the type has been decached and must be recalculated. */ protected static final int TYPE_UNKNOWN = -1; /** * This constant indicates that the transform defined by this object * is an identity transform. * An identity transform is one in which the output coordinates are * always the same as the input coordinates. * If this transform is anything other than the identity transform, * the type will either be the constant GENERAL_TRANSFORM or a * combination of the appropriate flag bits for the various coordinate * conversions that this transform performs. * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType */ public static final int TYPE_IDENTITY = 0; /** * This flag bit indicates that the transform defined by this object * performs a translation in addition to the conversions indicated * by other flag bits. * A translation moves the coordinates by a constant amount in x * and y without changing the length or angle of vectors. * @see #TYPE_IDENTITY * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType */ public static final int TYPE_TRANSLATION = 1; /** * This flag bit indicates that the transform defined by this object * performs a uniform scale in addition to the conversions indicated * by other flag bits. * A uniform scale multiplies the length of vectors by the same amount * in both the x and y directions without changing the angle between * vectors. * This flag bit is mutually exclusive with the TYPE_GENERAL_SCALE flag. * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType */ public static final int TYPE_UNIFORM_SCALE = 2; /** * This flag bit indicates that the transform defined by this object * performs a general scale in addition to the conversions indicated * by other flag bits. * A general scale multiplies the length of vectors by different * amounts in the x and y directions without changing the angle * between perpendicular vectors. * This flag bit is mutually exclusive with the TYPE_UNIFORM_SCALE flag. * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType */ public static final int TYPE_GENERAL_SCALE = 4; /** * This constant is a bit mask for any of the scale flag bits. * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE */ public static final int TYPE_MASK_SCALE = (TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE); /** * This flag bit indicates that the transform defined by this object * performs a mirror image flip about some axis which changes the * normally right handed coordinate system into a left handed * system in addition to the conversions indicated by other flag bits. * A right handed coordinate system is one where the positive X * axis rotates counterclockwise to overlay the positive Y axis * similar to the direction that the fingers on your right hand * curl when you stare end on at your thumb. * A left handed coordinate system is one where the positive X * axis rotates clockwise to overlay the positive Y axis similar * to the direction that the fingers on your left hand curl. * There is no mathematical way to determine the angle of the * original flipping or mirroring transformation since all angles * of flip are identical given an appropriate adjusting rotation. * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType */ public static final int TYPE_FLIP = 64; /* NOTE: TYPE_FLIP was added after GENERAL_TRANSFORM was in public * circulation and the flag bits could no longer be conveniently * renumbered without introducing binary incompatibility in outside * code. */ /** * This flag bit indicates that the transform defined by this object * performs a quadrant rotation by some multiple of 90 degrees in * addition to the conversions indicated by other flag bits. * A rotation changes the angles of vectors by the same amount * regardless of the original direction of the vector and without * changing the length of the vector. * This flag bit is mutually exclusive with the TYPE_GENERAL_ROTATION flag. * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType */ public static final int TYPE_QUADRANT_ROTATION = 8; /** * This flag bit indicates that the transform defined by this object * performs a rotation by an arbitrary angle in addition to the * conversions indicated by other flag bits. * A rotation changes the angles of vectors by the same amount * regardless of the original direction of the vector and without * changing the length of the vector. * This flag bit is mutually exclusive with the * TYPE_QUADRANT_ROTATION flag. * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_TRANSFORM * @see #getType */ public static final int TYPE_GENERAL_ROTATION = 16; /** * This constant is a bit mask for any of the rotation flag bits. * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION */ public static final int TYPE_MASK_ROTATION = (TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION); /** * This constant indicates that the transform defined by this object * performs an arbitrary conversion of the input coordinates. * If this transform can be classified by any of the above constants, * the type will either be the constant TYPE_IDENTITY or a * combination of the appropriate flag bits for the various coordinate * conversions that this transform performs. * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_FLIP * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #getType */ public static final int TYPE_GENERAL_TRANSFORM = 32; public static final int TYPE_AFFINE2D_MASK = (TYPE_TRANSLATION | TYPE_UNIFORM_SCALE | TYPE_GENERAL_SCALE | TYPE_QUADRANT_ROTATION | TYPE_GENERAL_ROTATION | TYPE_GENERAL_TRANSFORM | TYPE_FLIP); public static final int TYPE_AFFINE_3D = 128; /* * Convenience method used internally to throw exceptions when * an operation of degree higher than AFFINE_2D is attempted. */ static void degreeError(Degree maxSupported) { throw new InternalError("does not support higher than "+ maxSupported+" operations"); } public static BaseTransform getInstance(BaseTransform tx) { if (tx.isIdentity()) { return IDENTITY_TRANSFORM; } else if (tx.isTranslateOrIdentity()) { return new Translate2D(tx); } else if (tx.is2D()) { return new Affine2D(tx); } return new Affine3D(tx); } public static BaseTransform getInstance(double mxx, double mxy, double mxz, double mxt, double myx, double myy, double myz, double myt, double mzx, double mzy, double mzz, double mzt) { if (mxz == 0.0 && myz == 0.0 && mzx == 0.0 && mzy == 0.0 && mzz == 1.0 && mzt == 0.0) { return getInstance(mxx, myx, mxy, myy, mxt, myt); } else { return new Affine3D(mxx, mxy, mxz, mxt, myx, myy, myz, myt, mzx, mzy, mzz, mzt); } } public static BaseTransform getInstance(double mxx, double myx, double mxy, double myy, double mxt, double myt) { if (mxx == 1.0 && myx == 0.0 && mxy == 0.0 && myy == 1.0) { return getTranslateInstance(mxt, myt); } else { return new Affine2D(mxx, myx, mxy, myy, mxt, myt); } } public static BaseTransform getTranslateInstance(double mxt, double myt) { if (mxt == 0.0 && myt == 0.0) { return IDENTITY_TRANSFORM; } else { return new Translate2D(mxt, myt); } } public static BaseTransform getScaleInstance(double mxx, double myy) { return getInstance(mxx, 0, 0, myy, 0, 0); } public static BaseTransform getRotateInstance(double theta, double x, double y) { Affine2D a = new Affine2D(); a.setToRotation(theta, x, y); return a; } public abstract Degree getDegree(); /** * Retrieves the flag bits describing the conversion properties of * this transform. * The return value is either one of the constants TYPE_IDENTITY * or TYPE_GENERAL_TRANSFORM, or a combination of the * appriopriate flag bits. * A valid combination of flag bits is an exclusive OR operation * that can combine * the TYPE_TRANSLATION flag bit * in addition to either of the * TYPE_UNIFORM_SCALE or TYPE_GENERAL_SCALE flag bits * as well as either of the * TYPE_QUADRANT_ROTATION or TYPE_GENERAL_ROTATION flag bits. * @return the OR combination of any of the indicated flags that * apply to this transform * @see #TYPE_IDENTITY * @see #TYPE_TRANSLATION * @see #TYPE_UNIFORM_SCALE * @see #TYPE_GENERAL_SCALE * @see #TYPE_QUADRANT_ROTATION * @see #TYPE_GENERAL_ROTATION * @see #TYPE_GENERAL_TRANSFORM */ public abstract int getType(); public abstract boolean isIdentity(); public abstract boolean isTranslateOrIdentity(); public abstract boolean is2D(); public abstract double getDeterminant(); public double getMxx() { return 1.0; } public double getMxy() { return 0.0; } public double getMxz() { return 0.0; } public double getMxt() { return 0.0; } public double getMyx() { return 0.0; } public double getMyy() { return 1.0; } public double getMyz() { return 0.0; } public double getMyt() { return 0.0; } public double getMzx() { return 0.0; } public double getMzy() { return 0.0; } public double getMzz() { return 1.0; } public double getMzt() { return 0.0; } public abstract Point2D transform(Point2D src, Point2D dst); public abstract Point2D inverseTransform(Point2D src, Point2D dst) throws NoninvertibleTransformException; public abstract Vec3d transform(Vec3d src, Vec3d dst); public abstract Vec3d deltaTransform(Vec3d src, Vec3d dst); public abstract Vec3d inverseTransform(Vec3d src, Vec3d dst) throws NoninvertibleTransformException; public abstract Vec3d inverseDeltaTransform(Vec3d src, Vec3d dst) throws NoninvertibleTransformException; public abstract void transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts); public abstract void transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts); public abstract void transform(float[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts); public abstract void transform(double[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts); public abstract void deltaTransform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts); public abstract void deltaTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts); public abstract void inverseTransform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) throws NoninvertibleTransformException; public abstract void inverseDeltaTransform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) throws NoninvertibleTransformException; public abstract void inverseTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws NoninvertibleTransformException; public abstract BaseBounds transform(BaseBounds bounds, BaseBounds result); public abstract void transform(Rectangle rect, Rectangle result); public abstract BaseBounds inverseTransform(BaseBounds bounds, BaseBounds result) throws NoninvertibleTransformException; public abstract void inverseTransform(Rectangle rect, Rectangle result) throws NoninvertibleTransformException; public abstract Shape createTransformedShape(Shape s); public abstract void setToIdentity(); public abstract void setTransform(BaseTransform xform); /** * This function inverts the {@code BaseTransform} in place. All * current implementations can support their own inverted form, and * that should likely remain true in the future as well. */ public abstract void invert() throws NoninvertibleTransformException; /** * This function is only guaranteed to succeed if the transform is * of degree AFFINE2D or less and the matrix * parameters specified came from this same instance. In practice, * they will also tend to succeed if they specify a transform of * Degree less than or equal to the Degree of this instance as well, * but the intent of this method is to restore the transform that * had been read out of this transform into local variables. */ public abstract void restoreTransform(double mxx, double myx, double mxy, double myy, double mxt, double myt); /** * This function is only guaranteed to succeed if the matrix * parameters specified came from this same instance. In practice, * they will also tend to succeed if they specify a transform of * Degree less than or equal to the Degree of this instance as well, * but the intent of this method is to restore the transform that * had been read out of this transform into local variables. */ public abstract void restoreTransform(double mxx, double mxy, double mxz, double mxt, double myx, double myy, double myz, double myt, double mzx, double mzy, double mzz, double mzt); public abstract BaseTransform deriveWithTranslation(double mxt, double myt); public abstract BaseTransform deriveWithTranslation(double mxt, double myt, double mzt); public abstract BaseTransform deriveWithScale(double mxx, double myy, double mzz); public abstract BaseTransform deriveWithRotation(double theta, double axisX, double axisY, double axisZ); public abstract BaseTransform deriveWithPreTranslation(double mxt, double myt); public abstract BaseTransform deriveWithConcatenation(double mxx, double myx, double mxy, double myy, double mxt, double myt); public abstract BaseTransform deriveWithConcatenation( double mxx, double mxy, double mxz, double mxt, double myx, double myy, double myz, double myt, double mzx, double mzy, double mzz, double mzt); public abstract BaseTransform deriveWithPreConcatenation(BaseTransform transform); public abstract BaseTransform deriveWithConcatenation(BaseTransform tx); public abstract BaseTransform deriveWithNewTransform(BaseTransform tx); /** * This function always returns a new object, unless the transform * is an identity transform in which case it might return the * {@code Identity} singleton. * @return a new transform representing the inverse of this transform. */ public abstract BaseTransform createInverse() throws NoninvertibleTransformException; public abstract BaseTransform copy(); /** * Returns the hashcode for this transform. * @return a hash code for this transform. */ @Override public int hashCode() { if (isIdentity()) return 0; long bits = 0; bits = bits * 31 + Double.doubleToLongBits(getMzz()); bits = bits * 31 + Double.doubleToLongBits(getMzy()); bits = bits * 31 + Double.doubleToLongBits(getMzx()); bits = bits * 31 + Double.doubleToLongBits(getMyz()); bits = bits * 31 + Double.doubleToLongBits(getMxz()); bits = bits * 31 + Double.doubleToLongBits(getMyy()); bits = bits * 31 + Double.doubleToLongBits(getMyx()); bits = bits * 31 + Double.doubleToLongBits(getMxy()); bits = bits * 31 + Double.doubleToLongBits(getMxx()); bits = bits * 31 + Double.doubleToLongBits(getMzt()); bits = bits * 31 + Double.doubleToLongBits(getMyt()); bits = bits * 31 + Double.doubleToLongBits(getMxt()); return (((int) bits) ^ ((int) (bits >> 32))); } /** * Returns true if this BaseTransform * represents the same coordinate transform as the specified * argument. * @param obj the Object to test for equality with this * BaseTransform * @return true if obj equals this * BaseTransform object; false otherwise. */ @Override public boolean equals(Object obj) { if (!(obj instanceof BaseTransform)) { return false; } BaseTransform a = (BaseTransform) obj; return (getMxx() == a.getMxx() && getMxy() == a.getMxy() && getMxz() == a.getMxz() && getMxt() == a.getMxt() && getMyx() == a.getMyx() && getMyy() == a.getMyy() && getMyz() == a.getMyz() && getMyt() == a.getMyt() && getMzx() == a.getMzx() && getMzy() == a.getMzy() && getMzz() == a.getMzz() && getMzt() == a.getMzt()); } static Point2D makePoint(Point2D src, Point2D dst) { if (dst == null) { dst = new Point2D(); } return dst; } static final double EPSILON_ABSOLUTE = 1.0e-5; public static boolean almostZero(double a) { return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE)); } /** * Returns the matrix elements and degree of this transform as a string. * @return the matrix elements and degree of this transform */ @Override public String toString() { return "Matrix: degree " + getDegree() + "\n" + getMxx() + ", " + getMxy() + ", " + getMxz() + ", " + getMxt() + "\n" + getMyx() + ", " + getMyy() + ", " + getMyz() + ", " + getMyt() + "\n" + getMzx() + ", " + getMzy() + ", " + getMzz() + ", " + getMzt() + "\n"; } }