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 } |