1 /*
   2  * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  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 test.com.sun.javafx.test;
  27 
  28 import javafx.scene.transform.Transform;
  29 import static org.junit.Assert.assertEquals;
  30 import static org.junit.Assert.assertTrue;
  31 import com.sun.javafx.geom.transform.Affine3D;
  32 import com.sun.javafx.geom.transform.BaseTransform;
  33 import com.sun.javafx.geom.transform.GeneralTransform3D;
  34 import com.sun.javafx.scene.transform.TransformUtils;
  35 import javafx.geometry.Point3D;
  36 import javafx.scene.transform.Affine;
  37 import javafx.scene.transform.NonInvertibleTransformException;
  38 import javafx.scene.transform.Rotate;
  39 import javafx.scene.transform.Scale;
  40 import javafx.scene.transform.Shear;
  41 import javafx.scene.transform.Translate;
  42 
  43 /**
  44  * Helper class for transform tests.
  45  */
  46 public final class TransformHelper {
  47 
  48     /**
  49      * Asserts the {@code matrix} equals to the specified expected values
  50      */
  51     public static void assertMatrix(Transform matrix,
  52             double mxx, double mxy, double mxz, double tx,
  53             double myx, double myy, double myz, double ty,
  54             double mzx, double mzy, double mzz, double tz) {
  55         assertEquals(mxx, matrix.getMxx(), 0.00001);
  56         assertEquals(mxy, matrix.getMxy(), 0.00001);
  57         assertEquals(mxz, matrix.getMxz(), 0.00001);
  58         assertEquals(tx, matrix.getTx(), 0.00001);
  59         assertEquals(myx, matrix.getMyx(), 0.00001);
  60         assertEquals(myy, matrix.getMyy(), 0.00001);
  61         assertEquals(myz, matrix.getMyz(), 0.00001);
  62         assertEquals(ty, matrix.getTy(), 0.00001);
  63         assertEquals(mzx, matrix.getMzx(), 0.00001);
  64         assertEquals(mzy, matrix.getMzy(), 0.00001);
  65         assertEquals(mzz, matrix.getMzz(), 0.00001);
  66         assertEquals(tz, matrix.getTz(), 0.00001);
  67     }
  68 
  69     /**
  70      * Asserts the {@code matrix} equals to the specified expected values
  71      */
  72     public static void assertMatrix(BaseTransform matrix,
  73             double mxx, double mxy, double mxz, double tx,
  74             double myx, double myy, double myz, double ty,
  75             double mzx, double mzy, double mzz, double tz) {
  76         assertEquals(mxx, matrix.getMxx(), 0.00001);
  77         assertEquals(mxy, matrix.getMxy(), 0.00001);
  78         assertEquals(mxz, matrix.getMxz(), 0.00001);
  79         assertEquals(tx, matrix.getMxt(), 0.00001);
  80         assertEquals(myx, matrix.getMyx(), 0.00001);
  81         assertEquals(myy, matrix.getMyy(), 0.00001);
  82         assertEquals(myz, matrix.getMyz(), 0.00001);
  83         assertEquals(ty, matrix.getMyt(), 0.00001);
  84         assertEquals(mzx, matrix.getMzx(), 0.00001);
  85         assertEquals(mzy, matrix.getMzy(), 0.00001);
  86         assertEquals(mzz, matrix.getMzz(), 0.00001);
  87         assertEquals(tz, matrix.getMzt(), 0.00001);
  88     }
  89 
  90     /**
  91      * Asserts the {@code matrix} elements equal to the expected values
  92      * specified by {@code reference}
  93      */
  94     public static void assertMatrix(Transform matrix,
  95             Transform reference) {
  96         assertEquals(reference.getMxx(), matrix.getMxx(), 0.00001);
  97         assertEquals(reference.getMxy(), matrix.getMxy(), 0.00001);
  98         assertEquals(reference.getMxz(), matrix.getMxz(), 0.00001);
  99         assertEquals(reference.getTx(), matrix.getTx(), 0.00001);
 100         assertEquals(reference.getMyx(), matrix.getMyx(), 0.00001);
 101         assertEquals(reference.getMyy(), matrix.getMyy(), 0.00001);
 102         assertEquals(reference.getMyz(), matrix.getMyz(), 0.00001);
 103         assertEquals(reference.getTy(), matrix.getTy(), 0.00001);
 104         assertEquals(reference.getMzx(), matrix.getMzx(), 0.00001);
 105         assertEquals(reference.getMzy(), matrix.getMzy(), 0.00001);
 106         assertEquals(reference.getMzz(), matrix.getMzz(), 0.00001);
 107         assertEquals(reference.getTz(), matrix.getTz(), 0.00001);
 108     }
 109 
 110     /**
 111      * Asserts the {@code matrix} elements equal to the expected values
 112      * specified by {@code reference}
 113      */
 114     public static void assertMatrix(Affine3D matrix,
 115             BaseTransform reference) {
 116         assertEquals(reference.getMxx(), matrix.getMxx(), 0.00001);
 117         assertEquals(reference.getMxy(), matrix.getMxy(), 0.00001);
 118         assertEquals(reference.getMxz(), matrix.getMxz(), 0.00001);
 119         assertEquals(reference.getMxt(), matrix.getMxt(), 0.00001);
 120         assertEquals(reference.getMyx(), matrix.getMyx(), 0.00001);
 121         assertEquals(reference.getMyy(), matrix.getMyy(), 0.00001);
 122         assertEquals(reference.getMyz(), matrix.getMyz(), 0.00001);
 123         assertEquals(reference.getMyt(), matrix.getMyt(), 0.00001);
 124         assertEquals(reference.getMzx(), matrix.getMzx(), 0.00001);
 125         assertEquals(reference.getMzy(), matrix.getMzy(), 0.00001);
 126         assertEquals(reference.getMzz(), matrix.getMzz(), 0.00001);
 127         assertEquals(reference.getMzt(), matrix.getMzt(), 0.00001);
 128     }
 129 
 130     /**
 131      * Asserts the {@code matrix} elements equal to the expected values
 132      * specified by {@code reference}
 133      */
 134     public static void assertMatrix(String message, Transform matrix,
 135             Transform reference) {
 136         assertEquals(message, reference.getMxx(), matrix.getMxx(), 0.00001);
 137         assertEquals(message, reference.getMxy(), matrix.getMxy(), 0.00001);
 138         assertEquals(message, reference.getMxz(), matrix.getMxz(), 0.00001);
 139         assertEquals(message, reference.getTx(), matrix.getTx(), 0.00001);
 140         assertEquals(message, reference.getMyx(), matrix.getMyx(), 0.00001);
 141         assertEquals(message, reference.getMyy(), matrix.getMyy(), 0.00001);
 142         assertEquals(message, reference.getMyz(), matrix.getMyz(), 0.00001);
 143         assertEquals(message, reference.getTy(), matrix.getTy(), 0.00001);
 144         assertEquals(message, reference.getMzx(), matrix.getMzx(), 0.00001);
 145         assertEquals(message, reference.getMzy(), matrix.getMzy(), 0.00001);
 146         assertEquals(message, reference.getMzz(), matrix.getMzz(), 0.00001);
 147         assertEquals(message, reference.getTz(), matrix.getTz(), 0.00001);
 148     }
 149 
 150     /**
 151      * Asserts the {@code matrix} elements equal to the expected values
 152      * specified by {@code reference}
 153      */
 154     public static void assertMatrix(GeneralTransform3D matrix,
 155             GeneralTransform3D reference) {
 156         for (int i = 0; i < 16; i++) {
 157             assertEquals(reference.get(i), matrix.get(i), 0.00001);
 158         }
 159     }
 160 
 161     /**
 162      * Asserts the {@code matrix} is NOT equal to the specified values
 163      */
 164     public static void assertMatrixDiffers(Transform matrix,
 165             double mxx, double mxy, double mxz, double tx,
 166             double myx, double myy, double myz, double ty,
 167             double mzx, double mzy, double mzz, double tz) {
 168         assertTrue(
 169                 mxx != matrix.getMxx() ||
 170                 mxy != matrix.getMxy() ||
 171                 mxz != matrix.getMxz() ||
 172                 tx != matrix.getTx() ||
 173                 myx != matrix.getMyx() ||
 174                 myy != matrix.getMyy() ||
 175                 myz != matrix.getMyz() ||
 176                 ty != matrix.getTy() ||
 177                 mzx != matrix.getMzx() ||
 178                 mzy != matrix.getMzy() ||
 179                 mzz != matrix.getMzz() ||
 180                 tz != matrix.getTz());
 181     }
 182 
 183     /**
 184      * Inverts the given transform.
 185      * @throws NonInvertibleTransformException
 186      */
 187     public static Transform invert(Transform t)
 188             throws NonInvertibleTransformException {
 189 
 190         final double det = determinant(t);
 191         if (det == 0 || Math.abs(det) <= Double.MIN_VALUE) {
 192             throw new NonInvertibleTransformException("Det is 0");
 193         }
 194 
 195         final double cxx =   minor(t, 0, 0);
 196         final double cyx = - minor(t, 0, 1);
 197         final double czx =   minor(t, 0, 2);
 198         final double cxy = - minor(t, 1, 0);
 199         final double cyy =   minor(t, 1, 1);
 200         final double czy = - minor(t, 1, 2);
 201         final double cxz =   minor(t, 2, 0);
 202         final double cyz = - minor(t, 2, 1);
 203         final double czz =   minor(t, 2, 2);
 204         final double cxt = - minor(t, 3, 0);
 205         final double cyt =   minor(t, 3, 1);
 206         final double czt = - minor(t, 3, 2);
 207 
 208         return TransformUtils.immutableTransform(
 209             cxx / det, cxy / det, cxz / det, cxt / det,
 210             cyx / det, cyy / det, cyz / det, cyt / det,
 211             czx / det, czy / det, czz / det, czt / det);
 212     }
 213 
 214     /**
 215      * Concatenates the two transforms.
 216      */
 217     public static Transform concatenate(Transform t1, Transform t2) {
 218 
 219         final double txx = t2.getMxx();
 220         final double txy = t2.getMxy();
 221         final double txz = t2.getMxz();
 222         final double ttx = t2.getTx();
 223         final double tyx = t2.getMyx();
 224         final double tyy = t2.getMyy();
 225         final double tyz = t2.getMyz();
 226         final double tty = t2.getTy();
 227         final double tzx = t2.getMzx();
 228         final double tzy = t2.getMzy();
 229         final double tzz = t2.getMzz();
 230         final double ttz = t2.getTz();
 231         final double rxx = (t1.getMxx() * txx + t1.getMxy() * tyx + t1.getMxz() * tzx /* + getMxt * 0.0 */);
 232         final double rxy = (t1.getMxx() * txy + t1.getMxy() * tyy + t1.getMxz() * tzy /* + getMxt * 0.0 */);
 233         final double rxz = (t1.getMxx() * txz + t1.getMxy() * tyz + t1.getMxz() * tzz /* + getMxt * 0.0 */);
 234         final double rxt = (t1.getMxx() * ttx + t1.getMxy() * tty + t1.getMxz() * ttz + t1.getTx() /* * 1.0 */);
 235         final double ryx = (t1.getMyx() * txx + t1.getMyy() * tyx + t1.getMyz() * tzx /* + getMyt * 0.0 */);
 236         final double ryy = (t1.getMyx() * txy + t1.getMyy() * tyy + t1.getMyz() * tzy /* + getMyt * 0.0 */);
 237         final double ryz = (t1.getMyx() * txz + t1.getMyy() * tyz + t1.getMyz() * tzz /* + getMyt * 0.0 */);
 238         final double ryt = (t1.getMyx() * ttx + t1.getMyy() * tty + t1.getMyz() * ttz + t1.getTy() /* * 1.0 */);
 239         final double rzx = (t1.getMzx() * txx + t1.getMzy() * tyx + t1.getMzz() * tzx /* + getMzt * 0.0 */);
 240         final double rzy = (t1.getMzx() * txy + t1.getMzy() * tyy + t1.getMzz() * tzy /* + getMzt * 0.0 */);
 241         final double rzz = (t1.getMzx() * txz + t1.getMzy() * tyz + t1.getMzz() * tzz /* + getMzt * 0.0 */);
 242         final double rzt = (t1.getMzx() * ttx + t1.getMzy() * tty + t1.getMzz() * ttz + t1.getTz() /* * 1.0 */);
 243 
 244         return TransformUtils.immutableTransform(
 245                 rxx, rxy, rxz, rxt,
 246                 ryx, ryy, ryz, ryt,
 247                 rzx, rzy, rzz, rzt);
 248     }
 249 
 250     /**
 251      * Concatenates the two transforms.
 252      */
 253     public static Transform concatenate(BaseTransform t1, Transform t2) {
 254 
 255         final double txx = t2.getMxx();
 256         final double txy = t2.getMxy();
 257         final double txz = t2.getMxz();
 258         final double ttx = t2.getTx();
 259         final double tyx = t2.getMyx();
 260         final double tyy = t2.getMyy();
 261         final double tyz = t2.getMyz();
 262         final double tty = t2.getTy();
 263         final double tzx = t2.getMzx();
 264         final double tzy = t2.getMzy();
 265         final double tzz = t2.getMzz();
 266         final double ttz = t2.getTz();
 267         final double rxx = (t1.getMxx() * txx + t1.getMxy() * tyx + t1.getMxz() * tzx /* + getMxt * 0.0 */);
 268         final double rxy = (t1.getMxx() * txy + t1.getMxy() * tyy + t1.getMxz() * tzy /* + getMxt * 0.0 */);
 269         final double rxz = (t1.getMxx() * txz + t1.getMxy() * tyz + t1.getMxz() * tzz /* + getMxt * 0.0 */);
 270         final double rxt = (t1.getMxx() * ttx + t1.getMxy() * tty + t1.getMxz() * ttz + t1.getMxt() /* * 1.0 */);
 271         final double ryx = (t1.getMyx() * txx + t1.getMyy() * tyx + t1.getMyz() * tzx /* + getMyt * 0.0 */);
 272         final double ryy = (t1.getMyx() * txy + t1.getMyy() * tyy + t1.getMyz() * tzy /* + getMyt * 0.0 */);
 273         final double ryz = (t1.getMyx() * txz + t1.getMyy() * tyz + t1.getMyz() * tzz /* + getMyt * 0.0 */);
 274         final double ryt = (t1.getMyx() * ttx + t1.getMyy() * tty + t1.getMyz() * ttz + t1.getMyt() /* * 1.0 */);
 275         final double rzx = (t1.getMzx() * txx + t1.getMzy() * tyx + t1.getMzz() * tzx /* + getMzt * 0.0 */);
 276         final double rzy = (t1.getMzx() * txy + t1.getMzy() * tyy + t1.getMzz() * tzy /* + getMzt * 0.0 */);
 277         final double rzz = (t1.getMzx() * txz + t1.getMzy() * tyz + t1.getMzz() * tzz /* + getMzt * 0.0 */);
 278         final double rzt = (t1.getMzx() * ttx + t1.getMzy() * tty + t1.getMzz() * ttz + t1.getMzt() /* * 1.0 */);
 279 
 280         return TransformUtils.immutableTransform(
 281                 rxx, rxy, rxz, rxt,
 282                 ryx, ryy, ryz, ryt,
 283                 rzx, rzy, rzz, rzt);
 284     }
 285 
 286     /**
 287      * Computes determinant of the specified transform.
 288      */
 289     public static double determinant(Transform t) {
 290         return
 291                 t.getMxx() * (t.getMyy() * t.getMzz() - t.getMzy() * t.getMyz()) +
 292                 t.getMxy() * (t.getMyz() * t.getMzx() - t.getMzz() * t.getMyx()) +
 293                 t.getMxz() * (t.getMyx() * t.getMzy() - t.getMzx() * t.getMyy());
 294     }
 295 
 296     /**
 297      * Needed for inversion.
 298      */
 299     private static double minor(Transform t, int row, int col) {
 300         double m00 = t.getMxx(), m01 = t.getMxy(), m02 = t.getMxz();
 301         double m10 = t.getMyx(), m11 = t.getMyy(), m12 = t.getMyz();
 302         double m20 = t.getMzx(), m21 = t.getMzy(), m22 = t.getMzz();
 303         switch (col) {
 304             case 0:
 305                 m00 = m01;
 306                 m10 = m11;
 307                 m20 = m21;
 308             case 1:
 309                 m01 = m02;
 310                 m11 = m12;
 311                 m21 = m22;
 312             case 2:
 313                 m02 = t.getTx();
 314                 m12 = t.getTy();
 315                 m22 = t.getTz();
 316         }
 317         switch (row) {
 318             case 0:
 319                 m00 = m10;
 320                 m01 = m11;
 321                 // m02 = m12;
 322             case 1:
 323                 m10 = m20;
 324                 m11 = m21;
 325                 // m12 = m22;
 326             case 2:
 327                 // m20 = 0.0;
 328                 // m21 = 0.0;
 329                 // m22 = 1.0;
 330                 break;
 331             case 3:
 332                 // This is the only row that requires a full 3x3 determinant
 333                 return (m00 * (m11 * m22 - m21 * m12) +
 334                         m01 * (m12 * m20 - m22 * m10) +
 335                         m02 * (m10 * m21 - m20 * m11));
 336         }
 337         // return (m00 * (m11 * 1.0 - 0.0 * m12) +
 338         //         m01 * (m12 * 0.0 - 1.0 * m10) +
 339         //         m02 * (m10 * 0.0 - 0.0 * m11));
 340         return (m00 * m11 - m01 * m10);
 341     }
 342 
 343     /**
 344      * Modifies the given transform, if possible.
 345      * @param t Transform to modify
 346      * @param value Value to set to one of transform's properties
 347      * @return true if the transform was modified
 348      */
 349     public static boolean modify(Transform t, double value) {
 350         if (t instanceof Translate) {
 351             ((Translate) t).setY(value);
 352         } else if (t instanceof Scale) {
 353             ((Scale) t).setY(value);
 354         } else if (t instanceof Shear) {
 355             ((Shear) t).setY(value);
 356         } else if (t instanceof Rotate) {
 357             Rotate r = (Rotate) t;
 358             if (r.getAxis().equals(new Point3D(0, 0, 0))) {
 359                 r.setAxis(Rotate.Z_AXIS);
 360             }
 361             if ((r.getAxis().getX() != 0 || r.getAxis().getY() != 0) && r.getAngle() == 0) {
 362                 r.setAxis(Rotate.Z_AXIS);
 363             }
 364             r.setAngle(value);
 365         } else if (t instanceof Affine) {
 366             ((Affine) t).setMyx(value);
 367         } else {
 368             return false;
 369         }
 370 
 371         return true;
 372     }
 373 
 374     /**
 375      * Makes a small modification to the transform, if possible.
 376      * @param t Transform to modify
 377      * @return true if the transform was modified
 378      */
 379     public static boolean tinyModify(Transform t) {
 380         if (t instanceof Translate) {
 381             Translate tr = (Translate) t;
 382             tr.setY(tr.getY() + 1);
 383             if (tr.getZ() != 0) {
 384                 tr.setZ(tr.getZ() + 3);
 385             }
 386         } else if (t instanceof Scale) {
 387             Scale sc = (Scale) t;
 388             sc.setY(sc.getY() + 0.01);
 389             if (sc.getZ() != 1) {
 390                 sc.setZ(sc.getZ() + 0.01);
 391             }
 392         } else if (t instanceof Shear) {
 393             ((Shear) t).setY(((Shear) t).getY() + 0.01);
 394         } else if (t instanceof Rotate) {
 395             Rotate r = (Rotate) t;
 396             if (r.getAxis().getX() == 0 &&
 397                     r.getAxis().getY() == 0 &&
 398                     r.getAxis().getZ() == 0) {
 399                 return false;
 400             }
 401             if ((r.getAxis().getX() != 0 || r.getAxis().getY() != 0) && r.getAngle() == 0) {
 402                 return false;
 403             }
 404             r.setAngle(r.getAngle() + 0.2);
 405         } else if (t instanceof Affine) {
 406             Affine a = (Affine) t;
 407             a.setTy(a.getTy() + 1);
 408             if (!a.isType2D()) {
 409                 a.setMzz(a.getMzz() + 2);
 410             }
 411         } else {
 412             return false;
 413         }
 414 
 415         return true;
 416     }
 417 
 418     /**
 419      * Makes a small modification to one of the 3D (Z-axis-effective) properties
 420      * of the transform, if possible.
 421      * @param t Transform to modify
 422      * @return true if the transform was modified
 423      */
 424     public static boolean tinyModify3D(Transform t) {
 425         if (t instanceof Translate) {
 426             ((Translate) t).setZ(((Translate) t).getZ() + 1);
 427         } else if (t instanceof Scale) {
 428             ((Scale) t).setZ(((Scale) t).getZ() + 0.01);
 429         } else if (t instanceof Shear) {
 430             return false;
 431         } else if (t instanceof Rotate) {
 432             Rotate r = (Rotate) t;
 433             if (r.getAxis().getX() == 0 &&
 434                     r.getAxis().getY() == 0 &&
 435                     r.getAxis().getZ() == 0) {
 436                 return false;
 437             }
 438             r.setAngle(r.getAngle() + 0.2);
 439         } else if (t instanceof Affine) {
 440             ((Affine) t).setTy(((Affine) t).getTy() + 1);
 441         } else {
 442             return false;
 443         }
 444 
 445         return true;
 446     }
 447 
 448     /**
 449      * Makes the transform 3D, if possible.
 450      * @param t Transform to modify
 451      * @return true if the transform was modified to a 3D state
 452      */
 453     public static boolean make3D(Transform t) {
 454         if (t instanceof Translate) {
 455             ((Translate) t).setZ(42);
 456         } else if (t instanceof Scale) {
 457             ((Scale) t).setZ(42);
 458         } else if (t instanceof Shear) {
 459             return false;
 460         } else if (t instanceof Rotate) {
 461             Rotate r = (Rotate) t;
 462             if (r.getAxis().getX() == 0 && r.getAxis().getY() == 0) {
 463                 r.setAxis(Rotate.Y_AXIS);
 464             }
 465             if (r.getAngle() == 0) {
 466                 r.setAngle(23);
 467             }
 468         } else if (t instanceof Affine) {
 469             ((Affine) t).setMyz(42);
 470         } else {
 471             return false;
 472         }
 473 
 474         return true;
 475     }
 476 
 477     /**
 478      * Makes the transform 2D, if possible.
 479      * @param t Transform to modify
 480      * @return true if the transform was modified to a 2D state
 481      */
 482     public static boolean make2D(Transform t) {
 483         if (t instanceof Translate) {
 484             ((Translate) t).setZ(0);
 485         } else if (t instanceof Scale) {
 486             ((Scale) t).setZ(1);
 487         } else if (t instanceof Shear) {
 488             return true;
 489         } else if (t instanceof Rotate) {
 490             ((Rotate) t).setAxis(Rotate.Z_AXIS);
 491         } else if (t instanceof Affine) {
 492             ((Affine) t).setToIdentity();
 493         } else {
 494             return false;
 495         }
 496 
 497         return true;
 498     }
 499 
 500     /**
 501      * Makes the transform identity, if possible.
 502      * @param t Transform to modify
 503      * @return true if the transform was modified to an identity state
 504      */
 505     public static boolean makeIdentity(Transform t) {
 506         if (t instanceof Translate) {
 507             ((Translate) t).setX(0);
 508             ((Translate) t).setY(0);
 509             ((Translate) t).setZ(0);
 510         } else if (t instanceof Scale) {
 511             ((Scale) t).setX(1);
 512             ((Scale) t).setY(1);
 513             ((Scale) t).setZ(1);
 514         } else if (t instanceof Shear) {
 515             ((Shear) t).setX(0);
 516             ((Shear) t).setY(0);
 517         } else if (t instanceof Rotate) {
 518             ((Rotate) t).setAngle(0);
 519         } else if (t instanceof Affine) {
 520             ((Affine) t).setToIdentity();
 521         } else {
 522             return false;
 523         }
 524 
 525         return true;
 526     }
 527 
 528     private static enum State2D {
 529         IDENTITY(0),
 530         TRANSLATE(1),
 531         SCALE(2),
 532         SC_TR(3),
 533         SHEAR(4),
 534         SH_TR(5),
 535         SH_SC(6),
 536         SH_SC_TR(7);
 537 
 538         private int value;
 539 
 540         private State2D(int value) {
 541             this.value = value;
 542         }
 543 
 544         public int getValue() {
 545             return value;
 546         }
 547     }
 548 
 549     private static enum State3D {
 550         NON_3D(0),
 551         TRANSLATE(1),
 552         SCALE(2),
 553         SC_TR(3),
 554         COMPLEX(4);
 555 
 556         private int value;
 557 
 558         private State3D(int value) {
 559             this.value = value;
 560         }
 561 
 562         public int getValue() {
 563             return value;
 564         }
 565     }
 566 
 567     private static State2D getExpectedState2D(Transform t) {
 568         if (t.getMxy() == 0.0 && t.getMyx() == 0.0) {
 569             if (t.getMxx() == 1.0 && t.getMyy() == 1.0) {
 570                 if (t.getTx() == 0.0 && t.getTy() == 0.0) {
 571                     return State2D.IDENTITY;
 572                 } else {
 573                     return State2D.TRANSLATE;
 574                 }
 575             } else {
 576                 if (t.getTx() == 0.0 && t.getTy() == 0.0) {
 577                     return State2D.SCALE;
 578                 } else {
 579                     return State2D.SC_TR;
 580                 }
 581             }
 582         } else {
 583             if (t.getMxx() == 0.0 && t.getMyy() == 0.0) {
 584                 if (t.getTx() == 0.0 && t.getTy() == 0.0) {
 585                     return State2D.SHEAR;
 586                 } else {
 587                     return State2D.SH_TR;
 588                 }
 589             } else {
 590                 if (t.getTx() == 0.0 && t.getTy() == 0.0) {
 591                     return State2D.SH_SC;
 592                 } else {
 593                     return State2D.SH_SC_TR;
 594                 }
 595             }
 596         }
 597     }
 598 
 599     private static State3D getExpectedState3D(Transform t) {
 600         if (t.getMxz() == 0.0 && t.getMyz() == 0.0 &&
 601                 t.getMzx() == 0.0 && t.getMzy() == 0.0 &&
 602                 t.getMzz() == 1.0 && t.getTz() == 0.0) {
 603             return State3D.NON_3D;
 604         }
 605 
 606         if (t.getMxy() != 0.0 || t.getMxz() != 0.0 ||
 607                 t.getMyx() != 0.0 || t.getMyz() != 0.0 ||
 608                 t.getMzx() != 0.0 || t.getMzy() != 0.0) {
 609             return State3D.COMPLEX;
 610         }
 611 
 612         if ((t.getMxx() != 1.0 || t.getMyy() != 1.0 || t.getMzz() != 1.0) &&
 613                 (t.getTx() != 0.0 || t.getTy() != 0.0 || t.getTz() != 0.0)) {
 614             return State3D.SC_TR;
 615         }
 616 
 617         if (t.getMxx() != 1.0 || t.getMyy() != 1.0 || t.getMzz() != 1.0) {
 618             return State3D.SCALE;
 619         }
 620         if (t.getTx() != 0.0 || t.getTy() != 0.0 || t.getTz() != 0.0) {
 621             return State3D.TRANSLATE;
 622         }
 623         return null;
 624     }
 625 
 626     public static void assertStateOk(Transform t, int state3d, int state2d) {
 627         TransformHelper.State3D expectedState3D = TransformHelper.getExpectedState3D(t);
 628         assertEquals(expectedState3D.getValue(), state3d);
 629         if (expectedState3D == TransformHelper.State3D.NON_3D) {
 630             assertEquals(TransformHelper.getExpectedState2D(t).getValue(), state2d);
 631         }
 632     }
 633 
 634     public static void assertStateOk(String message, Transform t, int state3d, int state2d) {
 635         TransformHelper.State3D expectedState3D = TransformHelper.getExpectedState3D(t);
 636         assertEquals(message, expectedState3D.getValue(), state3d);
 637         if (expectedState3D == TransformHelper.State3D.NON_3D) {
 638             assertEquals(message, TransformHelper.getExpectedState2D(t).getValue(), state2d);
 639         }
 640     }
 641 
 642     /**
 643      * Convenient factory for 2D immutable transforms.
 644      */
 645     public static Transform immutableTransform(
 646             double mxx, double mxy, double tx,
 647             double myx, double myy, double ty) {
 648         return TransformUtils.immutableTransform(
 649                 mxx, mxy, 0.0, tx,
 650                 myx, myy, 0.0, ty,
 651                 0.0, 0.0, 1.0, 0.0);
 652     }
 653 
 654     /**
 655      * Creates a raw transformation to test the Transform class.
 656      */
 657     public static Transform rawTransform(
 658                 double mxx, double mxy, double mxz, double tx,
 659                 double myx, double myy, double myz, double ty,
 660                 double mzx, double mzy, double mzz, double tz) {
 661         return new RawTransform(
 662                 mxx, mxy, mxz, tx,
 663                 myx, myy, myz, ty,
 664                 mzx, mzy, mzz, tz);
 665     }
 666 
 667     private static class RawTransform extends Transform {
 668         private final double mxx, mxy, mxz, tx;
 669         private final double myx, myy, myz, ty;
 670         private final double mzx, mzy, mzz, tz;
 671 
 672         public RawTransform(
 673                 double mxx, double mxy, double mxz, double tx,
 674                 double myx, double myy, double myz, double ty,
 675                 double mzx, double mzy, double mzz, double tz) {
 676             this.mxx = mxx;
 677             this.mxy = mxy;
 678             this.mxz = mxz;
 679             this.tx = tx;
 680             this.myx = myx;
 681             this.myy = myy;
 682             this.myz = myz;
 683             this.ty = ty;
 684             this.mzx = mzx;
 685             this.mzy = mzy;
 686             this.mzz = mzz;
 687             this.tz = tz;
 688         }
 689 
 690         @Override
 691         public double getMxx() {
 692             return mxx;
 693         }
 694 
 695         @Override
 696         public double getMxy() {
 697             return mxy;
 698         }
 699 
 700         @Override
 701         public double getMxz() {
 702             return mxz;
 703         }
 704 
 705         @Override
 706         public double getTx() {
 707             return tx;
 708         }
 709 
 710         @Override
 711         public double getMyx() {
 712             return myx;
 713         }
 714 
 715         @Override
 716         public double getMyy() {
 717             return myy;
 718         }
 719 
 720         @Override
 721         public double getMyz() {
 722             return myz;
 723         }
 724 
 725         @Override
 726         public double getTy() {
 727             return ty;
 728         }
 729 
 730         @Override
 731         public double getMzx() {
 732             return mzx;
 733         }
 734 
 735         @Override
 736         public double getMzy() {
 737             return mzy;
 738         }
 739 
 740         @Override
 741         public double getMzz() {
 742             return mzz;
 743         }
 744 
 745         @Override
 746         public double getTz() {
 747             return tz;
 748         }
 749 
 750         @Override
 751         public void impl_apply(Affine3D t) {
 752             t.concatenate(
 753                     getMxx(), getMxy(), getMxz(), getTx(),
 754                     getMyx(), getMyy(), getMyz(), getTy(),
 755                     getMzx(), getMzy(), getMzz(), getTz());
 756         }
 757 
 758         @Override
 759         public BaseTransform impl_derive(BaseTransform t) {
 760             return t.deriveWithConcatenation(
 761                     getMxx(), getMxy(), getMxz(), getTx(),
 762                     getMyx(), getMyy(), getMyz(), getTy(),
 763                     getMzx(), getMzy(), getMzz(), getTz());
 764         }
 765     }
 766 }