< prev index next >

modules/graphics/src/main/java/javafx/scene/transform/Transform.java

Print this page




  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.transform;
  27 
  28 import java.util.Iterator;
  29 
  30 import com.sun.javafx.geometry.BoundsUtils;
  31 import javafx.event.EventDispatchChain;
  32 
  33 import javafx.scene.Node;
  34 
  35 import com.sun.javafx.util.WeakReferenceQueue;
  36 import com.sun.javafx.binding.ExpressionHelper;
  37 import com.sun.javafx.event.EventHandlerManager;
  38 import com.sun.javafx.geom.transform.Affine2D;
  39 import com.sun.javafx.geom.transform.Affine3D;
  40 import com.sun.javafx.geom.transform.AffineBase;
  41 import com.sun.javafx.geom.transform.BaseTransform;

  42 import com.sun.javafx.scene.transform.TransformUtils;
  43 import java.lang.ref.SoftReference;
  44 import javafx.beans.InvalidationListener;
  45 import javafx.beans.property.ObjectProperty;
  46 import javafx.beans.property.ReadOnlyBooleanProperty;
  47 import javafx.beans.property.SimpleObjectProperty;
  48 import javafx.beans.value.ChangeListener;
  49 import javafx.event.Event;
  50 import javafx.event.EventHandler;
  51 import javafx.event.EventTarget;
  52 import javafx.event.EventType;
  53 import javafx.geometry.BoundingBox;
  54 import javafx.geometry.Bounds;
  55 import javafx.geometry.Point2D;
  56 import javafx.geometry.Point3D;
  57 
  58 // PENDING_DOC_REVIEW of this whole class
  59 /**
  60  * This class is a base class for different affine transformations.
  61  * It provides factory methods for the simple transformations - rotating,
  62  * scaling, shearing, and translation. It allows to get the transformation
  63  * matrix elements for any transform.
  64  *
  65  * <p>Example:</p>
  66  *
  67  * <pre><code>
  68  *  Rectangle rect = new Rectangle(50,50, Color.RED);
  69  *  rect.getTransforms().add(new Rotate(45,0,0)); //rotate by 45 degrees
  70  * </code></pre>
  71  * @since JavaFX 2.0
  72  */
  73 public abstract class Transform implements Cloneable, EventTarget {
  74 
























































  75     /* *************************************************************************
  76      *                                                                         *
  77      *                            Factories                                    *
  78      *                                                                         *
  79      **************************************************************************/
  80 
  81     /**
  82      * Returns a new {@code Affine} object from 12 number
  83      * values representing the 6 specifiable entries of the 3x4
  84      * Affine transformation matrix.
  85      *
  86      * @param mxx the X coordinate scaling element of the 3x4 matrix
  87      * @param myx the Y coordinate shearing element of the 3x4 matrix
  88      * @param mxy the X coordinate shearing element of the 3x4 matrix
  89      * @param myy the Y coordinate scaling element of the 3x4 matrix
  90      * @param tx the X coordinate translation element of the 3x4 matrix
  91      * @param ty the Y coordinate translation element of the 3x4 matrix
  92      * @return a new {@code Affine} object derived from specified parameters
  93      */
  94     public static Affine affine(


1974     void ensureCanTransform2DPoint() throws IllegalStateException {
1975         if (!isType2D()) {
1976             throw new IllegalStateException("Cannot transform 2D point "
1977                     + "with a 3D transform");
1978         }
1979     }
1980 
1981     /**
1982      * Needed for the proper delivery of the TransformChangedEvent.
1983      * If the members are invalid, the transformChanged() notification
1984      * is not called and the event is not delivered. To avoid that
1985      * we need to manually validate all properties. Subclasses validate
1986      * their specific properties.
1987      */
1988     void validate() {
1989         getMxx(); getMxy(); getMxz(); getTx();
1990         getMyx(); getMyy(); getMyz(); getTy();
1991         getMzx(); getMzy(); getMzz(); getTz();
1992     }
1993 
1994     /**
1995      * @treatAsPrivate implementation detail
1996      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1997      */
1998     @Deprecated
1999     public abstract void impl_apply(Affine3D t);
2000 
2001     /**
2002      * @treatAsPrivate implementation detail
2003      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2004      */
2005     @Deprecated
2006     public abstract BaseTransform impl_derive(BaseTransform t);
2007 
2008     /**
2009      * @treatAsPrivate implementation detail
2010      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2011      */
2012     @Deprecated
2013     public void impl_add(final Node node) {
2014         nodes.add(node);
2015     }
2016 
2017     /**
2018      * @treatAsPrivate implementation detail
2019      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2020      */
2021     @Deprecated
2022     public void impl_remove(final Node node) {
2023         nodes.remove(node);
2024     }
2025 
2026     /**
2027      * This method must be called by all transforms whenever any of their
2028      * parameters changes. It is typically called when any of the transform's
2029      * properties is invalidated (it is OK to skip the call if an invalid
2030      * property is set).
2031      * @since JavaFX 8.0
2032      */
2033     protected void transformChanged() {
2034         inverseCache = null;
2035         final Iterator iterator = nodes.iterator();
2036         while (iterator.hasNext()) {
2037             ((Node) iterator.next()).impl_transformsChanged();
2038         }
2039 
2040         if (type2D != null) {
2041             type2D.invalidate();
2042         }


2094             Affine inv = new Affine(
2095                     getMxx(), getMxy(), getMxz(), getTx(),
2096                     getMyx(), getMyy(), getMyz(), getTy(),
2097                     getMzx(), getMzy(), getMzz(), getTz());
2098             inv.invert();
2099             inverseCache = new SoftReference<Transform>(inv);
2100             return inv;
2101         }
2102 
2103         return inverseCache.get();
2104     }
2105 
2106     /**
2107      * Used only by tests to emulate garbage collecting the soft references
2108      */
2109     void clearInverseCache() {
2110         if (inverseCache != null) {
2111             inverseCache.clear();
2112         }
2113     }






































































































































































































































































































































































































































































































































































































































































































































































































2114 }


  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.transform;
  27 
  28 import java.util.Iterator;
  29 
  30 import com.sun.javafx.geometry.BoundsUtils;
  31 import javafx.event.EventDispatchChain;
  32 
  33 import javafx.scene.Node;
  34 
  35 import com.sun.javafx.util.WeakReferenceQueue;
  36 import com.sun.javafx.binding.ExpressionHelper;
  37 import com.sun.javafx.event.EventHandlerManager;

  38 import com.sun.javafx.geom.transform.Affine3D;

  39 import com.sun.javafx.geom.transform.BaseTransform;
  40 import com.sun.javafx.scene.transform.TransformHelper;
  41 import com.sun.javafx.scene.transform.TransformUtils;
  42 import java.lang.ref.SoftReference;
  43 import javafx.beans.InvalidationListener;
  44 import javafx.beans.property.ObjectProperty;
  45 import javafx.beans.property.ReadOnlyBooleanProperty;
  46 import javafx.beans.property.SimpleObjectProperty;
  47 import javafx.beans.value.ChangeListener;
  48 import javafx.event.Event;
  49 import javafx.event.EventHandler;
  50 import javafx.event.EventTarget;
  51 import javafx.event.EventType;

  52 import javafx.geometry.Bounds;
  53 import javafx.geometry.Point2D;
  54 import javafx.geometry.Point3D;
  55 
  56 // PENDING_DOC_REVIEW of this whole class
  57 /**
  58  * This class is a base class for different affine transformations.
  59  * It provides factory methods for the simple transformations - rotating,
  60  * scaling, shearing, and translation. It allows to get the transformation
  61  * matrix elements for any transform.
  62  *
  63  * <p>Example:</p>
  64  *
  65  * <pre><code>
  66  *  Rectangle rect = new Rectangle(50,50, Color.RED);
  67  *  rect.getTransforms().add(new Rotate(45,0,0)); //rotate by 45 degrees
  68  * </code></pre>
  69  * @since JavaFX 2.0
  70  */
  71 public abstract class Transform implements Cloneable, EventTarget {
  72 
  73     static {
  74         // This is used by classes in different packages to get access to
  75         // private and package private methods.
  76         TransformHelper.setTransformAccessor(new TransformHelper.TransformAccessor() {
  77 
  78             @Override
  79             public void add(Transform transform, Node node) {
  80                 transform.add(node);
  81             }
  82 
  83             @Override
  84             public void remove(Transform transform, Node node) {
  85                 transform.remove(node);
  86             }
  87 
  88             @Override
  89             public void apply(Transform transform, Affine3D affine3D) {
  90                 transform.apply(affine3D);
  91             }
  92 
  93             @Override
  94             public BaseTransform derive(Transform transform, BaseTransform baseTransform) {
  95                 return transform.derive(baseTransform);
  96             }
  97 
  98             @Override
  99             public Transform createImmutableTransform() {
 100                 return Transform.createImmutableTransform();
 101             }
 102 
 103             @Override
 104             public Transform createImmutableTransform(
 105                     double mxx, double mxy, double mxz, double tx,
 106                     double myx, double myy, double myz, double ty,
 107                     double mzx, double mzy, double mzz, double tz) {
 108                 return Transform.createImmutableTransform(mxx, mxy, mxz, tx,
 109                         myx, myy, myz, ty, mzx, mzy, mzz, tz);
 110             }
 111 
 112             @Override
 113             public Transform createImmutableTransform(Transform transform,
 114                     double mxx, double mxy, double mxz, double tx,
 115                     double myx, double myy, double myz, double ty,
 116                     double mzx, double mzy, double mzz, double tz) {
 117                 return Transform.createImmutableTransform(transform,
 118                         mxx, mxy, mxz, tx, myx, myy, myz, ty, mzx, mzy, mzz, tz);
 119             }
 120 
 121             @Override
 122             public Transform createImmutableTransform(Transform transform,
 123                     Transform left, Transform right) {
 124                 return Transform.createImmutableTransform(transform, left, right);
 125             }
 126         });
 127     }
 128 
 129     /* *************************************************************************
 130      *                                                                         *
 131      *                            Factories                                    *
 132      *                                                                         *
 133      **************************************************************************/
 134 
 135     /**
 136      * Returns a new {@code Affine} object from 12 number
 137      * values representing the 6 specifiable entries of the 3x4
 138      * Affine transformation matrix.
 139      *
 140      * @param mxx the X coordinate scaling element of the 3x4 matrix
 141      * @param myx the Y coordinate shearing element of the 3x4 matrix
 142      * @param mxy the X coordinate shearing element of the 3x4 matrix
 143      * @param myy the Y coordinate scaling element of the 3x4 matrix
 144      * @param tx the X coordinate translation element of the 3x4 matrix
 145      * @param ty the Y coordinate translation element of the 3x4 matrix
 146      * @return a new {@code Affine} object derived from specified parameters
 147      */
 148     public static Affine affine(


2028     void ensureCanTransform2DPoint() throws IllegalStateException {
2029         if (!isType2D()) {
2030             throw new IllegalStateException("Cannot transform 2D point "
2031                     + "with a 3D transform");
2032         }
2033     }
2034 
2035     /**
2036      * Needed for the proper delivery of the TransformChangedEvent.
2037      * If the members are invalid, the transformChanged() notification
2038      * is not called and the event is not delivered. To avoid that
2039      * we need to manually validate all properties. Subclasses validate
2040      * their specific properties.
2041      */
2042     void validate() {
2043         getMxx(); getMxy(); getMxz(); getTx();
2044         getMyx(); getMyy(); getMyz(); getTy();
2045         getMzx(); getMzy(); getMzz(); getTz();
2046     }
2047 
2048     abstract void apply(Affine3D t);





2049 
2050     abstract BaseTransform derive(BaseTransform t);





2051 
2052     private void add(final Node node) {





2053         nodes.add(node);
2054     }
2055 
2056     private void remove(final Node node) {





2057         nodes.remove(node);
2058     }
2059 
2060     /**
2061      * This method must be called by all transforms whenever any of their
2062      * parameters changes. It is typically called when any of the transform's
2063      * properties is invalidated (it is OK to skip the call if an invalid
2064      * property is set).
2065      * @since JavaFX 8.0
2066      */
2067     protected void transformChanged() {
2068         inverseCache = null;
2069         final Iterator iterator = nodes.iterator();
2070         while (iterator.hasNext()) {
2071             ((Node) iterator.next()).impl_transformsChanged();
2072         }
2073 
2074         if (type2D != null) {
2075             type2D.invalidate();
2076         }


2128             Affine inv = new Affine(
2129                     getMxx(), getMxy(), getMxz(), getTx(),
2130                     getMyx(), getMyy(), getMyz(), getTy(),
2131                     getMzx(), getMzy(), getMzz(), getTz());
2132             inv.invert();
2133             inverseCache = new SoftReference<Transform>(inv);
2134             return inv;
2135         }
2136 
2137         return inverseCache.get();
2138     }
2139 
2140     /**
2141      * Used only by tests to emulate garbage collecting the soft references
2142      */
2143     void clearInverseCache() {
2144         if (inverseCache != null) {
2145             inverseCache.clear();
2146         }
2147     }
2148 
2149     /**************************************************************************
2150      *  ImmutableTransform Class and supporting methods
2151      **************************************************************************/
2152 
2153     static Transform createImmutableTransform() {
2154         return new ImmutableTransform();
2155     }
2156 
2157     static Transform createImmutableTransform(
2158             double mxx, double mxy, double mxz, double tx,
2159             double myx, double myy, double myz, double ty,
2160             double mzx, double mzy, double mzz, double tz) {
2161         return new ImmutableTransform(
2162                 mxx, mxy, mxz, tx,
2163                 myx, myy, myz, ty,
2164                 mzx, mzy, mzz, tz);
2165     }
2166 
2167     static Transform createImmutableTransform(Transform transform,
2168             double mxx, double mxy, double mxz, double tx,
2169             double myx, double myy, double myz, double ty,
2170             double mzx, double mzy, double mzz, double tz) {
2171         if (transform == null) {
2172             return createImmutableTransform(
2173                     mxx, mxy, mxz, tx,
2174                     myx, myy, myz, ty,
2175                     mzx, mzy, mzz, tz);
2176         }
2177         ((Transform.ImmutableTransform) transform).setToTransform(
2178                 mxx, mxy, mxz, tx,
2179                 myx, myy, myz, ty,
2180                 mzx, mzy, mzz, tz);
2181         return transform;
2182     }
2183 
2184     static Transform createImmutableTransform(Transform transform,
2185             Transform left, Transform right) {
2186         if (transform == null) {
2187             transform = createImmutableTransform();
2188         }
2189         ((Transform.ImmutableTransform) transform).setToConcatenation(
2190                 (ImmutableTransform) left, (ImmutableTransform) right);
2191         return transform;
2192     }
2193 
2194     /**
2195      * Immutable transformation with performance optimizations based on Affine.
2196      *
2197      * From user's perspective, this transform is immutable. However, we can
2198      * modify it internally. This allows for reusing instances that were
2199      * not handed to users. The caller is responsible for not modifying
2200      * user-visible instances.
2201      *
2202      * Note: can't override Transform's package private methods so they cannot
2203      * be optimized. Currently not a big deal.
2204      */
2205     static class ImmutableTransform extends Transform {
2206 
2207         private static final int APPLY_IDENTITY = 0;
2208         private static final int APPLY_TRANSLATE = 1;
2209         private static final int APPLY_SCALE = 2;
2210         private static final int APPLY_SHEAR = 4;
2211         private static final int APPLY_NON_3D = 0;
2212         private static final int APPLY_3D_COMPLEX = 4;
2213         private transient int state2d;
2214         private transient int state3d;
2215 
2216         private double xx;
2217         private double xy;
2218         private double xz;
2219         private double yx;
2220         private double yy;
2221         private double yz;
2222         private double zx;
2223         private double zy;
2224         private double zz;
2225         private double xt;
2226         private double yt;
2227         private double zt;
2228 
2229         ImmutableTransform() {
2230             xx = yy = zz = 1.0;
2231         }
2232 
2233         ImmutableTransform(Transform transform) {
2234             this(transform.getMxx(), transform.getMxy(), transform.getMxz(),
2235                                                                  transform.getTx(),
2236                  transform.getMyx(), transform.getMyy(), transform.getMyz(),
2237                                                                  transform.getTy(),
2238                  transform.getMzx(), transform.getMzy(), transform.getMzz(),
2239                                                                  transform.getTz());
2240         }
2241 
2242         ImmutableTransform(double mxx, double mxy, double mxz, double tx,
2243                       double myx, double myy, double myz, double ty,
2244                       double mzx, double mzy, double mzz, double tz) {
2245             xx = mxx;
2246             xy = mxy;
2247             xz = mxz;
2248             xt = tx;
2249 
2250             yx = myx;
2251             yy = myy;
2252             yz = myz;
2253             yt = ty;
2254 
2255             zx = mzx;
2256             zy = mzy;
2257             zz = mzz;
2258             zt = tz;
2259 
2260             updateState();
2261         }
2262 
2263         // Beware: this is modifying immutable transform!
2264         // It is private and it is there just for the purpose of reusing
2265         // instances not given to users
2266         private void setToTransform(double mxx, double mxy, double mxz, double tx,
2267                                     double myx, double myy, double myz, double ty,
2268                                     double mzx, double mzy, double mzz, double tz)
2269         {
2270             xx = mxx;
2271             xy = mxy;
2272             xz = mxz;
2273             xt = tx;
2274             yx = myx;
2275             yy = myy;
2276             yz = myz;
2277             yt = ty;
2278             zx = mzx;
2279             zy = mzy;
2280             zz = mzz;
2281             zt = tz;
2282             updateState();
2283         }
2284 
2285         // Beware: this is modifying immutable transform!
2286         // It is private and it is there just for the purpose of reusing
2287         // instances not given to users
2288         private void setToConcatenation(ImmutableTransform left, ImmutableTransform right) {
2289             if (left.state3d == APPLY_NON_3D && right.state3d == APPLY_NON_3D) {
2290                 xx = left.xx * right.xx + left.xy * right.yx;
2291                 xy = left.xx * right.xy + left.xy * right.yy;
2292                 xt = left.xx * right.xt + left.xy * right.yt + left.xt;
2293                 yx = left.yx * right.xx + left.yy * right.yx;
2294                 yy = left.yx * right.xy + left.yy * right.yy;
2295                 yt = left.yx * right.xt + left.yy * right.yt + left.yt;
2296                 if (state3d != APPLY_NON_3D) {
2297                     xz = yz = zx = zy = zt = 0.0;
2298                     zz = 1.0;
2299                     state3d = APPLY_NON_3D;
2300                 }
2301                 updateState2D();
2302             } else {
2303                 xx = left.xx * right.xx + left.xy * right.yx + left.xz * right.zx;
2304                 xy = left.xx * right.xy + left.xy * right.yy + left.xz * right.zy;
2305                 xz = left.xx * right.xz + left.xy * right.yz + left.xz * right.zz;
2306                 xt = left.xx * right.xt + left.xy * right.yt + left.xz * right.zt + left.xt;
2307                 yx = left.yx * right.xx + left.yy * right.yx + left.yz * right.zx;
2308                 yy = left.yx * right.xy + left.yy * right.yy + left.yz * right.zy;
2309                 yz = left.yx * right.xz + left.yy * right.yz + left.yz * right.zz;
2310                 yt = left.yx * right.xt + left.yy * right.yt + left.yz * right.zt + left.yt;
2311                 zx = left.zx * right.xx + left.zy * right.yx + left.zz * right.zx;
2312                 zy = left.zx * right.xy + left.zy * right.yy + left.zz * right.zy;
2313                 zz = left.zx * right.xz + left.zy * right.yz + left.zz * right.zz;
2314                 zt = left.zx * right.xt + left.zy * right.yt + left.zz * right.zt + left.zt;
2315                 updateState();
2316             }
2317             // could be further optimized using the states, but that would
2318             // require a lot of code (see Affine and all its append* methods)
2319         }
2320 
2321         @Override
2322         public double getMxx() {
2323             return xx;
2324         }
2325 
2326         @Override
2327         public double getMxy() {
2328             return xy;
2329         }
2330 
2331         @Override
2332         public double getMxz() {
2333             return xz;
2334         }
2335 
2336         @Override
2337         public double getTx() {
2338             return xt;
2339         }
2340 
2341         @Override
2342         public double getMyx() {
2343             return yx;
2344         }
2345 
2346         @Override
2347         public double getMyy() {
2348             return yy;
2349         }
2350 
2351         @Override
2352         public double getMyz() {
2353             return yz;
2354         }
2355 
2356         @Override
2357         public double getTy() {
2358             return yt;
2359         }
2360 
2361         @Override
2362         public double getMzx() {
2363             return zx;
2364         }
2365 
2366         @Override
2367         public double getMzy() {
2368             return zy;
2369         }
2370 
2371         @Override
2372         public double getMzz() {
2373             return zz;
2374         }
2375 
2376         @Override
2377         public double getTz() {
2378             return zt;
2379         }
2380 
2381     /* *************************************************************************
2382      *                                                                         *
2383      *                           State getters                                 *
2384      *                                                                         *
2385      **************************************************************************/
2386 
2387         @Override
2388         public double determinant() {
2389             switch(state3d) {
2390                 default:
2391                     stateError();
2392                     // cannot reach
2393                 case APPLY_NON_3D:
2394                     switch (state2d) {
2395                         default:
2396                             stateError();
2397                             // cannot reach
2398                         case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE:
2399                         case APPLY_SHEAR | APPLY_SCALE:
2400                             return xx * yy - xy * yx;
2401                         case APPLY_SHEAR | APPLY_TRANSLATE:
2402                         case APPLY_SHEAR:
2403                             return -(xy* yx);
2404                         case APPLY_SCALE | APPLY_TRANSLATE:
2405                         case APPLY_SCALE:
2406                             return xx * yy;
2407                         case APPLY_TRANSLATE:
2408                         case APPLY_IDENTITY:
2409                             return 1.0;
2410                     }
2411                 case APPLY_TRANSLATE:
2412                     return 1.0;
2413                 case APPLY_SCALE:
2414                 case APPLY_SCALE | APPLY_TRANSLATE:
2415                     return xx * yy * zz;
2416                 case APPLY_3D_COMPLEX:
2417                     return (xx* (yy * zz - zy * yz) +
2418                             xy* (yz * zx - zz * yx) +
2419                             xz* (yx * zy - zx * yy));
2420             }
2421         }
2422 
2423         @Override
2424         public Transform createConcatenation(Transform transform) {
2425             javafx.scene.transform.Affine a = new Affine(this);
2426             a.append(transform);
2427             return a;
2428         }
2429 
2430         @Override
2431         public javafx.scene.transform.Affine createInverse() throws NonInvertibleTransformException {
2432             javafx.scene.transform.Affine t = new Affine(this);
2433             t.invert();
2434             return t;
2435         }
2436 
2437         @Override
2438         public Transform clone() {
2439             return new ImmutableTransform(this);
2440         }
2441 
2442         /* *************************************************************************
2443          *                                                                         *
2444          *                     Transform, Inverse Transform                        *
2445          *                                                                         *
2446          **************************************************************************/
2447 
2448         @Override
2449         public Point2D transform(double x, double y) {
2450             ensureCanTransform2DPoint();
2451 
2452             switch (state2d) {
2453                 default:
2454                     stateError();
2455                     // cannot reach
2456                 case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE:
2457                     return new Point2D(
2458                         xx * x + xy * y + xt,
2459                         yx * x + yy * y + yt);
2460                 case APPLY_SHEAR | APPLY_SCALE:
2461                     return new Point2D(
2462                         xx * x + xy * y,
2463                         yx * x + yy * y);
2464                 case APPLY_SHEAR | APPLY_TRANSLATE:
2465                     return new Point2D(
2466                             xy * y + xt,
2467                             yx * x + yt);
2468                 case APPLY_SHEAR:
2469                     return new Point2D(xy * y, yx * x);
2470                 case APPLY_SCALE | APPLY_TRANSLATE:
2471                     return new Point2D(
2472                             xx * x + xt,
2473                             yy * y + yt);
2474                 case APPLY_SCALE:
2475                     return new Point2D(xx * x, yy * y);
2476                 case APPLY_TRANSLATE:
2477                     return new Point2D(x + xt, y + yt);
2478                 case APPLY_IDENTITY:
2479                     return new Point2D(x, y);
2480             }
2481         }
2482 
2483         @Override
2484         public Point3D transform(double x, double y, double z) {
2485             switch (state3d) {
2486                 default:
2487                     stateError();
2488                     // cannot reach
2489                 case APPLY_NON_3D:
2490                     switch (state2d) {
2491                         default:
2492                             stateError();
2493                             // cannot reach
2494                         case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE:
2495                             return new Point3D(
2496                                 xx * x + xy * y + xt,
2497                                 yx * x + yy * y + yt, z);
2498                         case APPLY_SHEAR | APPLY_SCALE:
2499                             return new Point3D(
2500                                 xx * x + xy * y,
2501                                 yx * x + yy * y, z);
2502                         case APPLY_SHEAR | APPLY_TRANSLATE:
2503                             return new Point3D(
2504                                     xy * y + xt, yx * x + yt,
2505                                     z);
2506                         case APPLY_SHEAR:
2507                             return new Point3D(xy * y, yx * x, z);
2508                         case APPLY_SCALE | APPLY_TRANSLATE:
2509                             return new Point3D(
2510                                     xx * x + xt, yy * y + yt,
2511                                     z);
2512                         case APPLY_SCALE:
2513                             return new Point3D(xx * x, yy * y, z);
2514                         case APPLY_TRANSLATE:
2515                             return new Point3D(x + xt, y + yt, z);
2516                         case APPLY_IDENTITY:
2517                             return new Point3D(x, y, z);
2518                     }
2519                 case APPLY_TRANSLATE:
2520                     return new Point3D(x + xt, y + yt, z + zt);
2521                 case APPLY_SCALE:
2522                     return new Point3D(xx * x, yy * y, zz * z);
2523                 case APPLY_SCALE | APPLY_TRANSLATE:
2524                     return new Point3D(
2525                             xx * x + xt,
2526                             yy * y + yt,
2527                             zz * z + zt);
2528                 case APPLY_3D_COMPLEX:
2529                     return new Point3D(
2530                         xx * x + xy * y + xz * z + xt,
2531                         yx * x + yy * y + yz * z + yt,
2532                         zx * x + zy * y + zz * z + zt);
2533             }
2534         }
2535 
2536         @Override
2537         public Point2D deltaTransform(double x, double y) {
2538             ensureCanTransform2DPoint();
2539 
2540             switch (state2d) {
2541                 default:
2542                     stateError();
2543                     // cannot reach
2544                 case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE:
2545                 case APPLY_SHEAR | APPLY_SCALE:
2546                     return new Point2D(
2547                         xx * x + xy * y,
2548                         yx * x + yy * y);
2549                 case APPLY_SHEAR | APPLY_TRANSLATE:
2550                 case APPLY_SHEAR:
2551                     return new Point2D(xy * y, yx * x);
2552                 case APPLY_SCALE | APPLY_TRANSLATE:
2553                 case APPLY_SCALE:
2554                     return new Point2D(xx * x, yy * y);
2555                 case APPLY_TRANSLATE:
2556                 case APPLY_IDENTITY:
2557                     return new Point2D(x, y);
2558             }
2559         }
2560 
2561         @Override
2562         public Point3D deltaTransform(double x, double y, double z) {
2563             switch (state3d) {
2564                 default:
2565                     stateError();
2566                     // cannot reach
2567                 case APPLY_NON_3D:
2568                     switch (state2d) {
2569                         default:
2570                             stateError();
2571                             // cannot reach
2572                         case APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE:
2573                         case APPLY_SHEAR | APPLY_SCALE:
2574                             return new Point3D(
2575                                 xx * x + xy * y,
2576                                 yx * x + yy * y, z);
2577                         case APPLY_SHEAR | APPLY_TRANSLATE:
2578                         case APPLY_SHEAR:
2579                             return new Point3D(xy * y, yx * x, z);
2580                         case APPLY_SCALE | APPLY_TRANSLATE:
2581                         case APPLY_SCALE:
2582                             return new Point3D(xx * x, yy * y, z);
2583                         case APPLY_TRANSLATE:
2584                         case APPLY_IDENTITY:
2585                             return new Point3D(x, y, z);
2586                     }
2587                 case APPLY_TRANSLATE:
2588                     return new Point3D(x, y, z);
2589                 case APPLY_SCALE:
2590                 case APPLY_SCALE | APPLY_TRANSLATE:
2591                     return new Point3D(xx * x, yy * y, zz * z);
2592                 case APPLY_3D_COMPLEX:
2593                     return new Point3D(
2594                         xx * x + xy * y + xz * z,
2595                         yx * x + yy * y + yz * z,
2596                         zx * x + zy * y + zz * z);
2597             }
2598         }
2599 
2600         @Override
2601         public Point2D inverseTransform(double x, double y)
2602                 throws NonInvertibleTransformException {
2603             ensureCanTransform2DPoint();
2604 
2605             switch (state2d) {
2606                 default:
2607                     return super.inverseTransform(x, y);
2608                 case APPLY_SHEAR | APPLY_TRANSLATE:
2609                     if (xy == 0.0 || yx == 0.0) {
2610                         throw new NonInvertibleTransformException("Determinant is 0");
2611                     }
2612                     return new Point2D(
2613                             (1.0 / yx) * y - yt / yx,
2614                             (1.0 / xy) * x - xt / xy);
2615                 case APPLY_SHEAR:
2616                     if (xy == 0.0 || yx == 0.0) {
2617                         throw new NonInvertibleTransformException("Determinant is 0");
2618                     }
2619                     return new Point2D((1.0 / yx) * y, (1.0 / xy) * x);
2620                 case APPLY_SCALE | APPLY_TRANSLATE:
2621                     if (xx == 0.0 || yy == 0.0) {
2622                         throw new NonInvertibleTransformException("Determinant is 0");
2623                     }
2624                     return new Point2D(
2625                             (1.0 / xx) * x - xt / xx,
2626                             (1.0 / yy) * y - yt / yy);
2627                 case APPLY_SCALE:
2628                     if (xx == 0.0 || yy == 0.0) {
2629                         throw new NonInvertibleTransformException("Determinant is 0");
2630                     }
2631                     return new Point2D((1.0 / xx) * x, (1.0 / yy) * y);
2632                 case APPLY_TRANSLATE:
2633                     return new Point2D(x - xt, y - yt);
2634                 case APPLY_IDENTITY:
2635                     return new Point2D(x, y);
2636             }
2637         }
2638 
2639         @Override
2640         public Point3D inverseTransform(double x, double y, double z)
2641                 throws NonInvertibleTransformException {
2642             switch(state3d) {
2643                 default:
2644                     stateError();
2645                     // cannot reach
2646                 case APPLY_NON_3D:
2647                     switch (state2d) {
2648                         default:
2649                             return super.inverseTransform(x, y, z);
2650                         case APPLY_SHEAR | APPLY_TRANSLATE:
2651                             if (xy == 0.0 || yx == 0.0) {
2652                                 throw new NonInvertibleTransformException(
2653                                         "Determinant is 0");
2654                             }
2655                             return new Point3D(
2656                                     (1.0 / yx) * y - yt / yx,
2657                                     (1.0 / xy) * x - xt / xy, z);
2658                         case APPLY_SHEAR:
2659                             if (xy == 0.0 || yx == 0.0) {
2660                                 throw new NonInvertibleTransformException(
2661                                         "Determinant is 0");
2662                             }
2663                             return new Point3D(
2664                                     (1.0 / yx) * y,
2665                                     (1.0 / xy) * x, z);
2666                         case APPLY_SCALE | APPLY_TRANSLATE:
2667                             if (xx == 0.0 || yy == 0.0) {
2668                                 throw new NonInvertibleTransformException(
2669                                         "Determinant is 0");
2670                             }
2671                             return new Point3D(
2672                                     (1.0 / xx) * x - xt / xx,
2673                                     (1.0 / yy) * y - yt / yy, z);
2674                         case APPLY_SCALE:
2675                             if (xx == 0.0 || yy == 0.0) {
2676                                 throw new NonInvertibleTransformException(
2677                                         "Determinant is 0");
2678                             }
2679                             return new Point3D((1.0 / xx) * x, (1.0 / yy) * y, z);
2680                         case APPLY_TRANSLATE:
2681                             return new Point3D(x - xt, y - yt, z);
2682                         case APPLY_IDENTITY:
2683                             return new Point3D(x, y, z);
2684                     }
2685                 case APPLY_TRANSLATE:
2686                     return new Point3D(x - xt, y - yt, z - zt);
2687                 case APPLY_SCALE:
2688                     if (xx == 0.0 || yy == 0.0 || zz == 0.0) {
2689                         throw new NonInvertibleTransformException("Determinant is 0");
2690                     }
2691                     return new Point3D(
2692                             (1.0 / xx) * x,
2693                             (1.0 / yy) * y,
2694                             (1.0 / zz) * z);
2695                 case APPLY_SCALE | APPLY_TRANSLATE:
2696                     if (xx == 0.0 || yy == 0.0 || zz == 0.0) {
2697                         throw new NonInvertibleTransformException("Determinant is 0");
2698                     }
2699                     return new Point3D(
2700                             (1.0 / xx) * x - xt / xx,
2701                             (1.0 / yy) * y - yt / yy,
2702                             (1.0 / zz) * z - zt / zz);
2703                 case APPLY_3D_COMPLEX:
2704                     return super.inverseTransform(x, y, z);
2705             }
2706         }
2707 
2708         @Override
2709         public Point2D inverseDeltaTransform(double x, double y)
2710                 throws NonInvertibleTransformException {
2711             ensureCanTransform2DPoint();
2712 
2713             switch (state2d) {
2714                 default:
2715                     return super.inverseDeltaTransform(x, y);
2716                 case APPLY_SHEAR | APPLY_TRANSLATE:
2717                 case APPLY_SHEAR:
2718                     if (xy == 0.0 || yx == 0.0) {
2719                         throw new NonInvertibleTransformException("Determinant is 0");
2720                     }
2721                     return new Point2D((1.0 / yx) * y, (1.0 / xy) * x);
2722                 case APPLY_SCALE | APPLY_TRANSLATE:
2723                 case APPLY_SCALE:
2724                     if (xx == 0.0 || yy == 0.0) {
2725                         throw new NonInvertibleTransformException("Determinant is 0");
2726                     }
2727                     return new Point2D((1.0 / xx) * x, (1.0 / yy) * y);
2728                 case APPLY_TRANSLATE:
2729                 case APPLY_IDENTITY:
2730                     return new Point2D(x, y);
2731             }
2732         }
2733 
2734         @Override
2735         public Point3D inverseDeltaTransform(double x, double y, double z)
2736                 throws NonInvertibleTransformException {
2737             switch(state3d) {
2738                 default:
2739                     stateError();
2740                     // cannot reach
2741                 case APPLY_NON_3D:
2742                     switch (state2d) {
2743                         default:
2744                             return super.inverseDeltaTransform(x, y, z);
2745                         case APPLY_SHEAR | APPLY_TRANSLATE:
2746                         case APPLY_SHEAR:
2747                             if (xy == 0.0 || yx == 0.0) {
2748                                 throw new NonInvertibleTransformException(
2749                                         "Determinant is 0");
2750                             }
2751                             return new Point3D(
2752                                     (1.0 / yx) * y,
2753                                     (1.0 / xy) * x, z);
2754                         case APPLY_SCALE | APPLY_TRANSLATE:
2755                         case APPLY_SCALE:
2756                             if (xx == 0.0 || yy == 0.0) {
2757                                 throw new NonInvertibleTransformException(
2758                                         "Determinant is 0");
2759                             }
2760                             return new Point3D(
2761                                     (1.0 / xx) * x,
2762                                     (1.0 / yy) * y, z);
2763                         case APPLY_TRANSLATE:
2764                         case APPLY_IDENTITY:
2765                             return new Point3D(x, y, z);
2766                     }
2767 
2768                 case APPLY_TRANSLATE:
2769                     return new Point3D(x, y, z);
2770                 case APPLY_SCALE | APPLY_TRANSLATE:
2771                 case APPLY_SCALE:
2772                     if (xx == 0.0 || yy == 0.0 || zz == 0.0) {
2773                         throw new NonInvertibleTransformException("Determinant is 0");
2774                     }
2775                     return new Point3D(
2776                             (1.0 / xx) * x,
2777                             (1.0 / yy) * y,
2778                             (1.0 / zz) * z);
2779                 case APPLY_3D_COMPLEX:
2780                     return super.inverseDeltaTransform(x, y, z);
2781             }
2782         }
2783 
2784         /* *************************************************************************
2785          *                                                                         *
2786          *                               Other API                                 *
2787          *                                                                         *
2788          **************************************************************************/
2789 
2790         @Override
2791         public String toString() {
2792            final StringBuilder sb = new StringBuilder("Transform [\n");
2793 
2794             sb.append("\t").append(xx);
2795             sb.append(", ").append(xy);
2796             sb.append(", ").append(xz);
2797             sb.append(", ").append(xt);
2798             sb.append('\n');
2799             sb.append("\t").append(yx);
2800             sb.append(", ").append(yy);
2801             sb.append(", ").append(yz);
2802             sb.append(", ").append(yt);
2803             sb.append('\n');
2804             sb.append("\t").append(zx);
2805             sb.append(", ").append(zy);
2806             sb.append(", ").append(zz);
2807             sb.append(", ").append(zt);
2808 
2809             return sb.append("\n]").toString();
2810         }
2811 
2812         /* *************************************************************************
2813          *                                                                         *
2814          *                    Internal implementation stuff                        *
2815          *                                                                         *
2816          **************************************************************************/
2817 
2818         private void updateState() {
2819             updateState2D();
2820 
2821             state3d = APPLY_NON_3D;
2822 
2823             if (xz != 0.0 ||
2824                 yz != 0.0 ||
2825                 zx != 0.0 ||
2826                 zy != 0.0)
2827             {
2828                 state3d = APPLY_3D_COMPLEX;
2829             } else {
2830                 if ((state2d & APPLY_SHEAR) == 0) {
2831                     if (zt != 0.0) {
2832                         state3d |= APPLY_TRANSLATE;
2833                     }
2834                     if (zz != 1.0) {
2835                         state3d |= APPLY_SCALE;
2836                     }
2837                     if (state3d != APPLY_NON_3D) {
2838                         state3d |= (state2d & (APPLY_SCALE | APPLY_TRANSLATE));
2839                     }
2840                 } else {
2841                     if (zz != 1.0 || zt != 0.0) {
2842                         state3d = APPLY_3D_COMPLEX;
2843                     }
2844                 }
2845             }
2846         }
2847 
2848         private void updateState2D() {
2849             if (xy == 0.0 && yx == 0.0) {
2850                 if (xx == 1.0 && yy == 1.0) {
2851                     if (xt == 0.0 && yt == 0.0) {
2852                         state2d = APPLY_IDENTITY;
2853                     } else {
2854                         state2d = APPLY_TRANSLATE;
2855                     }
2856                 } else {
2857                     if (xt == 0.0 && yt == 0.0) {
2858                         state2d = APPLY_SCALE;
2859                     } else {
2860                         state2d = (APPLY_SCALE | APPLY_TRANSLATE);
2861                     }
2862                 }
2863             } else {
2864                 if (xx == 0.0 && yy == 0.0) {
2865                     if (xt == 0.0 && yt == 0.0) {
2866                         state2d = APPLY_SHEAR;
2867                     } else {
2868                         state2d = (APPLY_SHEAR | APPLY_TRANSLATE);
2869                     }
2870                 } else {
2871                     if (xt == 0.0 && yt == 0.0) {
2872                         state2d = (APPLY_SHEAR | APPLY_SCALE);
2873                     } else {
2874                         state2d = (APPLY_SHEAR | APPLY_SCALE | APPLY_TRANSLATE);
2875                     }
2876                 }
2877             }
2878         }
2879 
2880         void ensureCanTransform2DPoint() throws IllegalStateException {
2881             if (state3d != APPLY_NON_3D) {
2882                 throw new IllegalStateException("Cannot transform 2D point "
2883                         + "with a 3D transform");
2884             }
2885         }
2886 
2887         private static void stateError() {
2888             throw new InternalError("missing case in a switch");
2889         }
2890 
2891 
2892         @Override
2893         void apply(final Affine3D trans) {
2894             trans.concatenate(xx, xy, xz, xt,
2895                               yx, yy, yz, yt,
2896                               zx, zy, zz, zt);
2897         }
2898 
2899         @Override
2900         BaseTransform derive(final BaseTransform trans) {
2901             return trans.deriveWithConcatenation(xx, xy, xz, xt,
2902                                                  yx, yy, yz, yt,
2903                                                  zx, zy, zz, zt);
2904         }
2905 
2906         /**
2907          * Used only by tests to check the 2d matrix state
2908          */
2909         int getState2d() {
2910             return state2d;
2911         }
2912 
2913         /**
2914          * Used only by tests to check the 3d matrix state
2915          */
2916         int getState3d() {
2917             return state3d;
2918         }
2919 
2920     }
2921 
2922 }
< prev index next >