1 /*
   2  * Copyright (c) 2012, 2014, 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 javafx.scene.transform;
  27 
  28 import java.util.Arrays;
  29 import java.util.Collection;
  30 import javafx.beans.InvalidationListener;
  31 import javafx.beans.Observable;
  32 import javafx.beans.value.ChangeListener;
  33 import javafx.beans.value.ObservableValue;
  34 import javafx.event.EventHandler;
  35 import javafx.geometry.BoundingBox;
  36 import javafx.geometry.Bounds;
  37 import javafx.geometry.Point2D;
  38 import javafx.geometry.Point3D;
  39 import com.sun.javafx.scene.transform.TransformUtils;
  40 import com.sun.javafx.test.TransformHelper;
  41 import org.junit.Test;
  42 import org.junit.runner.RunWith;
  43 import org.junit.runners.Parameterized;
  44 import org.junit.runners.Parameterized.Parameters;
  45 
  46 import static org.junit.Assert.*;
  47 
  48 @RunWith(Parameterized.class)
  49 public class TransformOperationsTest {
  50     private static final Affine affine_identity = new Affine();
  51     private static final Affine affine_translate_only = new Affine(0, 0, 2,
  52                                                                    0, 0, 3);
  53     private static final Affine affine_translate = new Affine(1, 0, 2,
  54                                                               0, 1, 3);
  55     private static final Affine affine_scale = new Affine(4, 0, 0,
  56                                                           0, 5, 0);
  57     private static final Affine affine_sc_tr = new Affine(6, 0, 8,
  58                                                           0, 7, 9);
  59     private static final Affine affine_shear = new Affine( 0, 10, 0,
  60                                                           11,  0, 0);
  61     private static final Affine affine_sh_tr = new Affine( 0, 12, 14,
  62                                                           13,  0, 15);
  63     private static final Affine affine_sh_sc_simple = new Affine( 1, 18, 0,
  64                                                                  19,  1, 0);
  65     private static final Affine affine_sh_sc = new Affine(16, 18, 0,
  66                                                           19, 17, 0);
  67     private static final Affine affine_sh_sc_tr = new Affine(20, 21, 22,
  68                                                              23, 24, 25);
  69     private static final Affine affine_3d_tr = new Affine(1, 0, 0, 0,
  70                                                     0, 1, 0, 0,
  71                                                     0, 0, 1, 30);
  72     private static final Affine affine_3d_sc = new Affine(1, 0, 0, 0,
  73                                                     0, 1, 0, 0,
  74                                                     0, 0, 3, 0);
  75     private static final Affine affine_3d_sc_tr = new Affine(1, 0, 0, 0,
  76                                                        0, 1, 0, 0,
  77                                                        0, 0, 3, 30);
  78     private static final Affine affine_3d_sc2_tr3 = new Affine(1, 0, 0, 0,
  79                                                          0, 3, 0, 0,
  80                                                          0, 0, 1, 30);
  81     private static final Affine affine_3d_sc3_tr2 = new Affine(1, 0, 0, 25,
  82                                                          0, 1, 0, 0,
  83                                                          0, 0, 3, 0);
  84     private static final Affine affine_3d_withShear = new Affine(1, 5, 0, 0,
  85                                                            0, 1, 0, 0,
  86                                                            0, 0, 3, 30);
  87     private static final Affine affine_3d_only3d = new Affine( 1,  0, 20, 0,
  88                                                          0,  1, 30, 0,
  89                                                         11, 12, 13, 0);
  90     private static final Affine affine_3d_translate_only = new Affine(0, 0, 0, 10,
  91                                                                        0, 0, 0, 20,
  92                                                                        0, 0, 0, 30);
  93     private static final Affine affine_3d_complex = new Affine( 7,  3,  4,  5,
  94                                                           6,  7,  5,  9,
  95                                                          10, 11, 12, 13);
  96     private static final Affine affine_3d_complex_noninvertible =
  97                                                      new Affine( 2,  3,  4,  5,
  98                                                                  6,  7,  8,  9,
  99                                                                 10, 11, 12, 13);
 100     private static final Affine affine_empty = new Affine(0, 0, 0, 0,
 101                                                           0, 0, 0, 0,
 102                                                           0, 0, 0, 0);
 103     private static final Affine affine_emptyZ = new Affine(1, 0, 0, 0,
 104                                                            0, 1, 0, 0,
 105                                                            0, 0, 0, 0);
 106     private static final Affine affine_emptyXY = new Affine(0, 0, 0, 0,
 107                                                             0, 0, 0, 0,
 108                                                             0, 0, 1, 0);
 109     private static final Affine affine_nonInv_translate_x = new Affine(0, 0, 2,
 110                                                                        0, 0, 0);
 111     private static final Affine affine_nonInv_translate_y = new Affine(0, 0, 0,
 112                                                                        0, 0, 4);
 113     private static final Affine affine_nonInv_translate_z = new Affine(0, 0, 0, 0,
 114                                                                        0, 0, 0, 0,
 115                                                                        0, 0, 0, 4);
 116     private static final Affine affine_nonInv_scale_x = new Affine(2, 0, 0,
 117                                                           0, 0, 0);
 118     private static final Affine affine_nonInv_scale_y = new Affine(0, 0, 0,
 119                                                           0, 2, 0);
 120     private static final Affine affine_nonInv_scale_xy = new Affine(2, 0, 0, 0,
 121                                                                     0, 2, 0, 0,
 122                                                                     0, 0, 0, 0);
 123     private static final Affine affine_nonInv_scale_z = new Affine(0, 0, 0, 0,
 124                                                                     0, 0, 0, 0,
 125                                                                     0, 0, 4, 0);
 126     private static final Affine affine_nonInv_shear_x = new Affine(0, 3, 0,
 127                                                           0, 0, 0);
 128     private static final Affine affine_nonInv_shear_y = new Affine(0, 0, 0,
 129                                                           3, 0, 0);
 130     private static final Affine affine_nonInv_sh_tr_x = new Affine(0, 3, 4,
 131                                                           0, 0, 0);
 132     private static final Affine affine_nonInv_sh_tr_y = new Affine(0, 0, 0,
 133                                                           3, 0, 4);
 134     private static final Affine affine_nonInv_sh_sc_tr = new Affine(0, 0, 0,
 135                                                              2, 3, 4);
 136     private static final Affine affine_nonInv_sh_sc = new Affine(0, 0, 0,
 137                                                           2, 3, 0);
 138     private static final Affine affine_nonInv_sh_tr = new Affine(0, 0, 0,
 139                                                           2, 0, 5);
 140     private static final Affine affine_nonInv_sc_tr = new Affine(0, 0, 0,
 141                                                           0, 6, 5);
 142     private static final Affine affine_nonInv_sc_tr_x = new Affine(2, 0, 4,
 143                                                           0, 0, 0);
 144     private static final Affine affine_nonInv_sc_tr_y = new Affine(0, 0, 0,
 145                                                           0, 2, 7);
 146     private static final Translate translate2d = new Translate(120, 225);
 147     private static final Translate translate3d = new Translate(120, 225, 346);
 148     private static final Translate translate3d_only = new Translate(0, 0, 346);
 149     private static final Translate noTranslate = new Translate(0, 0);
 150     private static final Scale scale2d = new Scale(0.5, 2.5, 35, 46);
 151     private static final Scale scale2d_x = new Scale(1.0, 2.5, 35, 46);
 152     private static final Scale scale2d_y = new Scale(0.5, 1.0, 35, 46);
 153     private static final Scale scale3d = new Scale(0.5, 2.5, 3.6, 35, 46, 55);
 154     private static final Scale scale3dOnly = new Scale(1.0, 1.0, 3.6);
 155     private static final Scale scale2dNoPivot = new Scale(0.5, 2.5);
 156     private static final Scale scale2dUslessPivots = new Scale(0.5, 1.0, 0.0, 45);
 157     private static final Scale scale2dPivot3d = new Scale(0.5, 2.5, 1.0, 35, 46, 52);
 158     private static final Scale scale3dNoPivot = new Scale(0.5, 2.5, 3.6);
 159     private static final Scale noScale = new Scale(1, 1);
 160     private static final Scale nonInvertible3dScale = new Scale(0.0, 2.5, 3.6, 35, 46, 55);
 161     private static final Scale nonInvertible2dScale = new Scale(1.3, 0.0, 35, 45);
 162     private static final Shear shear = new Shear(3.2, 4.3, 75, 84);
 163     private static final Shear shearX = new Shear(3.2, 0, 75, 84);
 164     private static final Shear shearY = new Shear(0, 4.3, 75, 84);
 165     private static final Shear shearNoPivot = new Shear(3.5, 4.3);
 166     private static final Shear noShear = new Shear(0, 0, 75, 84);
 167     private static final Rotate simpleRotate3d = new Rotate(97.5, Rotate.Y_AXIS);
 168     private static final Rotate rotate2d = new Rotate(97.5, 123, 456);
 169     private static final Rotate rotate3d = new Rotate(97.5, 33, 44, 55, new Point3D(66, 77, 88));
 170     private static final Rotate rotate3d2d = new Rotate(97.5, 33, 44, 55, new Point3D(0, 0, 10));
 171     private static final Rotate rotateZeroAxis = new Rotate(97.5, 33, 44, 55, new Point3D(0, 0, 0));
 172     private static final Rotate rotate3dUpsideDown2d = new Rotate(97.5, 33, 44, 55, new Point3D(0, 0, -10));
 173     private static final Rotate rotate2dNoPivot = new Rotate(97.5);
 174     private static final Rotate rotate3dNoPivot = new Rotate(97.5, new Point3D(66, 77, 88));
 175     private static final Rotate rotate2dPivot3d = new Rotate(97.5, 125, 126, 127, Rotate.Z_AXIS);
 176     private static final Rotate noRotate = new Rotate(0, Rotate.Y_AXIS);
 177     private static final Transform immutable_identity =
 178             TransformHelper.immutableTransform(1, 0, 0, 0, 1, 0);
 179     private static final Transform immutable_translate_only =
 180             TransformHelper.immutableTransform(0, 0, 2, 0, 0, 3);
 181     private static final Transform immutable_translate =
 182             TransformHelper.immutableTransform(1, 0, 2, 0, 1, 3);
 183     private static final Transform immutable_scale =
 184             TransformHelper.immutableTransform(4, 0, 0, 0, 5, 0);
 185     private static final Transform immutable_sc_tr =
 186             TransformHelper.immutableTransform(6, 0, 8, 0, 7, 9);
 187     private static final Transform immutable_shear =
 188             TransformHelper.immutableTransform( 0, 10, 0, 11,  0, 0);
 189     private static final Transform immutable_sh_tr =
 190             TransformHelper.immutableTransform( 0, 12, 14, 13,  0, 15);
 191     private static final Transform immutable_sh_sc_simple =
 192             TransformHelper.immutableTransform( 1, 18, 0, 19,  1, 0);
 193     private static final Transform immutable_sh_sc =
 194             TransformHelper.immutableTransform(16, 18, 0, 19, 17, 0);
 195     private static final Transform immutable_sh_sc_tr =
 196             TransformHelper.immutableTransform(20, 21, 22, 23, 24, 25);
 197     private static final Transform immutable_3d_tr =
 198             TransformUtils.immutableTransform(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 30);
 199     private static final Transform immutable_3d_sc =
 200             TransformUtils.immutableTransform(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0);
 201     private static final Transform immutable_3d_sc_tr =
 202             TransformUtils.immutableTransform(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 30);
 203     private static final Transform immutable_3d_sc2_tr3 =
 204             TransformUtils.immutableTransform(1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 30);
 205     private static final Transform immutable_3d_sc3_tr2 =
 206             TransformUtils.immutableTransform(1, 0, 0, 25, 0, 1, 0, 0, 0, 0, 3, 0);
 207     private static final Transform immutable_3d_withShear =
 208             TransformUtils.immutableTransform(1, 5, 0, 0, 0, 1, 0, 0, 0, 0, 3, 30);
 209     private static final Transform immutable_3d_only3d =
 210             TransformUtils.immutableTransform(1, 0, 20, 0, 0, 1, 30, 0, 11, 12, 13, 0);
 211     private static final Transform immutable_3d_translate_only =
 212             TransformUtils.immutableTransform(0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30);
 213     private static final Transform immutable_3d_complex =
 214             TransformUtils.immutableTransform(7, 3, 4, 5, 5, 7, 8, 9, 10, 11, 12, 13);
 215     private static final Transform immutable_3d_complex_noninvertible =
 216             TransformUtils.immutableTransform(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
 217     private static final Transform immutable_empty =
 218             TransformUtils.immutableTransform(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
 219     private static final Transform immutable_emptyZ =
 220             TransformUtils.immutableTransform(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
 221     private static final Transform immutable_emptyXY =
 222             TransformUtils.immutableTransform(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0);
 223     private static final Transform immutable_nonInv_translate_x =
 224             TransformHelper.immutableTransform(0, 0, 2, 0, 0, 0);
 225     private static final Transform immutable_nonInv_translate_y =
 226             TransformHelper.immutableTransform(0, 0, 0, 0, 0, 4);
 227     private static final Transform immutable_nonInv_translate_z =
 228             TransformUtils.immutableTransform(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4);
 229     private static final Transform immutable_nonInv_scale_x =
 230             TransformHelper.immutableTransform(2, 0, 0, 0, 0, 0);
 231     private static final Transform immutable_nonInv_scale_y =
 232             TransformHelper.immutableTransform(0, 0, 0, 0, 2, 0);
 233     private static final Transform immutable_nonInv_scale_xy =
 234             TransformUtils.immutableTransform(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0);
 235     private static final Transform immutable_nonInv_scale_z =
 236             TransformUtils.immutableTransform(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0);
 237     private static final Transform immutable_nonInv_shear_x =
 238             TransformHelper.immutableTransform(0, 3, 0, 0, 0, 0);
 239     private static final Transform immutable_nonInv_shear_y =
 240             TransformHelper.immutableTransform(0, 0, 0, 3, 0, 0);
 241     private static final Transform immutable_nonInv_sh_tr_x =
 242             TransformHelper.immutableTransform(0, 3, 4, 0, 0, 0);
 243     private static final Transform immutable_nonInv_sh_tr_y =
 244             TransformHelper.immutableTransform(0, 0, 0, 3, 0, 4);
 245     private static final Transform immutable_nonInv_sh_sc_tr =
 246             TransformHelper.immutableTransform(0, 0, 0, 2, 3, 4);
 247     private static final Transform immutable_nonInv_sh_sc =
 248             TransformHelper.immutableTransform(0, 0, 0, 2, 3, 0);
 249     private static final Transform immutable_nonInv_sh_tr =
 250             TransformHelper.immutableTransform(0, 0, 0, 2, 0, 5);
 251     private static final Transform immutable_nonInv_sc_tr =
 252             TransformHelper.immutableTransform(0, 0, 0, 0, 6, 5);
 253     private static final Transform immutable_nonInv_sc_tr_x =
 254             TransformHelper.immutableTransform(2, 0, 4, 0, 0, 0);
 255     private static final Transform immutable_nonInv_sc_tr_y =
 256             TransformHelper.immutableTransform(0, 0, 0, 0, 2, 7);
 257     private static final Transform raw_arbitrary_nonInvertible =
 258             TransformHelper.rawTransform( 5,  6,  7,  8,
 259                                          10, 11, 12, 13,
 260                                          15, 16, 17, 18);
 261     private static final Transform raw_arbitrary =
 262             TransformHelper.rawTransform( 5,  6, 13,  8,
 263                                          10,  4, 12, 13,
 264                                          15, 16, 26, 18);
 265     private static final Transform raw_empty =
 266             TransformHelper.rawTransform(0, 0, 0, 0,
 267                                          0, 0, 0, 0,
 268                                          0, 0, 0, 0);
 269     private static final Transform raw_emptyZ =
 270             TransformHelper.rawTransform(1, 0, 0, 0,
 271                                          0, 1, 0, 0,
 272                                          0, 0, 0, 0);
 273     private static final Transform raw_emptyXY =
 274             TransformHelper.rawTransform(0, 0, 0, 0,
 275                                          0, 0, 0, 0,
 276                                          0, 0, 1, 0);
 277 
 278     private boolean listenerCalled;
 279     private int eventCounter;
 280 
 281     //BEWARE: used also in AffineOperationsTest
 282     @Parameters
 283     public static Collection getParams() {
 284         return Arrays.asList(new Object[][] {
 285             { affine_identity, true, Affine.class },            //  0
 286             { affine_translate, true, Affine.class },           //  1
 287             { affine_translate_only, true, Affine.class },      //  2
 288             { affine_scale, true, Affine.class },               //  3
 289             { affine_sc_tr, true, Affine.class },               //  4
 290             { affine_shear, true, Affine.class },               //  5
 291             { affine_sh_tr, true, Affine.class },               //  6
 292             { affine_sh_sc_simple, true, Affine.class },        //  7
 293             { affine_sh_sc, true, Affine.class },               //  8
 294             { affine_sh_sc_tr, true, Affine.class },            //  9
 295             { affine_3d_tr, false, Affine.class },              // 10
 296             { affine_3d_sc, false, Affine.class },              // 11
 297             { affine_3d_sc_tr, false, Affine.class },           // 12
 298             { affine_3d_sc2_tr3, false, Affine.class },         // 13
 299             { affine_3d_sc3_tr2, false, Affine.class },         // 14
 300             { affine_3d_withShear, false, Affine.class },       // 15
 301             { affine_3d_only3d, false, Affine.class },          // 16
 302             { affine_3d_translate_only, false, null },          // 17
 303             { affine_3d_complex, false, Affine.class },         // 18
 304             { affine_3d_complex_noninvertible, false, null },   // 19
 305             { affine_empty, false, null },                      // 20
 306             { affine_emptyZ, false, null },                     // 21
 307             { affine_emptyXY, true, null },                     // 22
 308             { affine_nonInv_translate_x, true, null },          // 23
 309             { affine_nonInv_translate_y, true, null },          // 24
 310             { affine_nonInv_translate_z, false, null },         // 25
 311             { affine_nonInv_scale_x, true, null },              // 26
 312             { affine_nonInv_scale_y, true, null },              // 27
 313             { affine_nonInv_scale_xy, false, null },            // 28
 314             { affine_nonInv_scale_z, false, null },             // 29
 315             { affine_nonInv_shear_x, true, null },              // 30
 316             { affine_nonInv_shear_y, true, null },              // 31
 317             { affine_nonInv_sh_tr_x, true, null },              // 32
 318             { affine_nonInv_sh_tr_y, true, null },              // 33
 319             { affine_nonInv_sh_sc_tr, true, null },             // 34
 320             { affine_nonInv_sh_sc, true, null },                // 35
 321             { affine_nonInv_sh_tr, true, null },                // 36
 322             { affine_nonInv_sc_tr, true, null },                // 37
 323             { affine_nonInv_sc_tr_x, true, null },              // 38
 324             { affine_nonInv_sc_tr_y, true, null },              // 39
 325             { translate2d, true, Translate.class },             // 40
 326             { translate3d, false, Translate.class },            // 41
 327             { translate3d_only, false, Translate.class },       // 42
 328             { noTranslate, true, Translate.class },             // 43
 329             { scale2d, true, Scale.class },                     // 44
 330             { scale2d_x, true, Scale.class },                   // 45
 331             { scale2d_y, true, Scale.class },                   // 46
 332             { scale3d, false, Scale.class },                    // 47
 333             { scale3dOnly, false, Scale.class },                // 48
 334             { scale2dNoPivot, true, Scale.class },              // 49
 335             { scale2dUslessPivots, true, Scale.class },         // 50
 336             { scale3dNoPivot, false, Scale.class },             // 51
 337             { scale2dPivot3d, true, Scale.class },              // 52
 338             { noScale, true, Scale.class },                     // 53
 339             { nonInvertible2dScale, true, null },               // 54
 340             { nonInvertible3dScale, false, null },              // 55
 341             { shear, true, Affine.class },                      // 56
 342             { shearX, true, Shear.class },                      // 57
 343             { shearY, true, Shear.class },                      // 58
 344             { shearNoPivot, true, Affine.class },               // 59
 345             { noShear, true, Shear.class },                     // 60
 346             { simpleRotate3d, false, Rotate.class},             // 61
 347             { rotate2d, true, Rotate.class },                   // 62
 348             { rotate3d, false, Rotate.class },                  // 63
 349             { rotate3d2d, true, Rotate.class },                 // 64
 350             { rotate3dUpsideDown2d, true, Rotate.class },       // 65
 351             { rotateZeroAxis, true, Rotate.class },             // 66
 352             { rotate2dNoPivot, true, Rotate.class },            // 67
 353             { rotate3dNoPivot, false, Rotate.class },           // 68
 354             { rotate2dPivot3d, true, Rotate.class },            // 69
 355             { noRotate, true, Rotate.class },                   // 70
 356             { immutable_identity, true, Affine.class },         // 71
 357             { immutable_translate, true, Affine.class },        // 72
 358             { immutable_translate_only, true, Affine.class },   // 73
 359             { immutable_scale, true, Affine.class },            // 74
 360             { immutable_sc_tr, true, Affine.class },            // 75
 361             { immutable_shear, true, Affine.class },            // 76
 362             { immutable_sh_tr, true, Affine.class },            // 77
 363             { immutable_sh_sc_simple, true, Affine.class },     // 78
 364             { immutable_sh_sc, true, Affine.class },            // 79
 365             { immutable_sh_sc_tr, true, Affine.class },         // 80
 366             { immutable_3d_tr, false, Affine.class },           // 81
 367             { immutable_3d_sc, false, Affine.class },           // 82
 368             { immutable_3d_sc_tr, false, Affine.class },        // 83
 369             { immutable_3d_sc2_tr3, false, Affine.class },      // 84
 370             { immutable_3d_sc3_tr2, false, Affine.class },      // 85
 371             { immutable_3d_withShear, false, Affine.class },    // 86
 372             { immutable_3d_only3d, false, Affine.class },       // 87
 373             { immutable_3d_translate_only, false, null },       // 88
 374             { immutable_3d_complex, false, Affine.class },      // 89
 375             { immutable_3d_complex_noninvertible, false, null },// 90
 376             { immutable_empty, false, null },                   // 91
 377             { immutable_emptyZ, false, null },                  // 92
 378             { immutable_emptyXY, true, null },                  // 93
 379             { immutable_nonInv_translate_x, true, null },       // 94
 380             { immutable_nonInv_translate_y, true, null },       // 95
 381             { immutable_nonInv_translate_z, false, null },      // 96
 382             { immutable_nonInv_scale_x, true, null },           // 97
 383             { immutable_nonInv_scale_y, true, null },           // 98
 384             { immutable_nonInv_scale_xy, false, null },         // 99
 385             { immutable_nonInv_scale_z, false, null },          //100
 386             { immutable_nonInv_shear_x, true, null },           //101
 387             { immutable_nonInv_shear_y, true, null },           //102
 388             { immutable_nonInv_sh_tr_x, true, null },           //103
 389             { immutable_nonInv_sh_tr_y, true, null },           //104
 390             { immutable_nonInv_sh_sc_tr, true, null },          //105
 391             { immutable_nonInv_sh_sc, true, null },             //106
 392             { immutable_nonInv_sh_tr, true, null },             //107
 393             { immutable_nonInv_sc_tr, true, null },             //108
 394             { immutable_nonInv_sc_tr_x, true, null },           //109
 395             { immutable_nonInv_sc_tr_y, true, null },           //110
 396             { raw_arbitrary, false, Affine.class },             //111
 397             { raw_arbitrary_nonInvertible, false, null },       //112
 398             { raw_empty, false, null },                         //113
 399             { raw_emptyZ, false, null },                        //114
 400             { raw_emptyXY, true, null },                        //115
 401         });
 402     }
 403 
 404     private Transform t;
 405     private Transform it;
 406     private boolean is2d, isIdentity;
 407     private boolean isInvertible;
 408     private Class inverseType;
 409 
 410     public TransformOperationsTest(Transform t, boolean twoDee, Class inverseType) {
 411         this.t = t;
 412         this.is2d = twoDee;
 413         this.isIdentity =
 414                (t.getMxx() == 1 && t.getMxy() == 0 && t.getMxz() == 0 && t.getTx() == 0
 415              && t.getMyx() == 0 && t.getMyy() == 1 && t.getMyz() == 0 && t.getTy() == 0
 416              && t.getMzx() == 0 && t.getMzy() == 0 && t.getMzz() == 1 && t.getTz() == 0);
 417 
 418         this.it = null;
 419         this.inverseType = inverseType;
 420         this.isInvertible = (TransformHelper.determinant(t) != 0);
 421         if (isInvertible) {
 422             try {
 423                 it = TransformHelper.invert(t);
 424             } catch (NonInvertibleTransformException e) {
 425                 // error in test
 426                 throw new RuntimeException("Test is wrong, it must be invertible");
 427             }
 428         } else {
 429             // to avoid non-null checks everywhere
 430             it = new Affine();
 431         }
 432     }
 433 
 434     @Test
 435     public void testClone() {
 436         final double mxx = t.getMxx();
 437         final double mxy = t.getMxy();
 438         final double mxz = t.getMxz();
 439         final double tx = t.getTx();
 440         final double myx = t.getMyx();
 441         final double myy = t.getMyy();
 442         final double myz = t.getMyz();
 443         final double ty = t.getTy();
 444         final double mzx = t.getMzx();
 445         final double mzy = t.getMzy();
 446         final double mzz = t.getMzz();
 447         final double tz = t.getTz();
 448 
 449         Transform clone = t.clone();
 450 
 451         TransformHelper.assertMatrix(clone,
 452                 mxx, mxy, mxz, tx, myx, myy, myz, ty, mzx, mzy, mzz, tz);
 453 
 454         if (!TransformHelper.modify(clone, 42)) {
 455             // cannot modify, nothing else to test
 456             return;
 457         }
 458 
 459         TransformHelper.assertMatrixDiffers(clone,
 460                 mxx, mxy, mxz, tx, myx, myy, myz, ty, mzx, mzy, mzz, tz);
 461 
 462         TransformHelper.assertMatrix(t,
 463                 mxx, mxy, mxz, tx, myx, myy, myz, ty, mzx, mzy, mzz, tz);
 464     }
 465 
 466     private Class getExpectedConcatenationClass(Transform t1, Transform t2) {
 467         Class c1 = t1.getClass();
 468         Class c2 = t2.getClass();
 469 
 470         if (c1 == Translate.class && c2 == Translate.class) {
 471             return Translate.class;
 472         }
 473 
 474         if (c1 == Translate.class && c2 == Scale.class) {
 475             Translate t = (Translate) t1;
 476             Scale s = (Scale) t2;
 477 
 478             if ((t.getX() == 0.0 || s.getX() != 1.0) &&
 479                     (t.getY() == 0.0 || s.getY() != 1.0) &&
 480                     (t.getZ() == 0.0 || s.getZ() != 1.0)) {
 481                 return Scale.class;
 482             }
 483         }
 484 
 485         if (c1 == Scale.class && c2 == Translate.class) {
 486             Scale s = (Scale) t1;
 487             Translate tr = (Translate) t2;
 488 
 489             if ((tr.getX() == 0.0 || (s.getX() != 1.0 && s.getX() != 0.0)) &&
 490                     (tr.getY() == 0.0 || (s.getY() != 1.0 && s.getY() != 0.0)) &&
 491                     (tr.getZ() == 0.0 || (s.getZ() != 1.0 && s.getY() != 0.0))) {
 492                 return Scale.class;
 493             }
 494         }
 495 
 496         if (c1 == Scale.class && c2 == Scale.class) {
 497             Scale s1 = (Scale) t1;
 498             Scale s2 = (Scale) t2;
 499 
 500             if (s1.getPivotX() == s2.getPivotX() &&
 501                     s1.getPivotY() == s2.getPivotY() &&
 502                     s1.getPivotZ() == s2.getPivotZ()) {
 503                 return Scale.class;
 504             }
 505         }
 506 
 507         if (c1 == Rotate.class && c2 == Rotate.class) {
 508             Rotate r1 = (Rotate) t1;
 509             Rotate r2 = (Rotate) t2;
 510             if (r1.getAxis().normalize().equals(r2.getAxis().normalize()) &&
 511                     r1.getPivotX() == r2.getPivotX() &&
 512                     r1.getPivotY() == r2.getPivotY() &&
 513                     r1.getPivotZ() == r2.getPivotZ()) {
 514                 return Rotate.class;
 515             }
 516         }
 517 
 518         return Affine.class;
 519     }
 520 
 521     @Test
 522     public void testCreateConcatenation() {
 523         int counter = 0;
 524         for (Object o : TransformOperationsTest.getParams()) {
 525             Object[] arr = (Object[]) o;
 526             Transform other = (Transform) arr[0];
 527 
 528             Transform res = TransformHelper.concatenate(t, other);
 529             Transform conc = t.createConcatenation(other);
 530 
 531             TransformHelper.assertMatrix("Concatenating with #" + counter,
 532                     conc, res);
 533             assertSame("Concatenating with #" + counter,
 534                     getExpectedConcatenationClass(t, other),
 535                     conc.getClass());
 536             counter++;
 537         }
 538     }
 539 
 540     @Test(expected=NullPointerException.class)
 541     public void testCreateConcatenationNullTransform() {
 542         t.createConcatenation(null);
 543     }
 544 
 545     @Test
 546     public void testCreateInverse() {
 547         Transform res = null;
 548         try {
 549             res = t.createInverse();
 550         } catch(NonInvertibleTransformException e) {
 551             if (isInvertible) {
 552                 e.printStackTrace();
 553                 fail("NonInvertibleTransformException thrown for invertible transform");
 554             } else {
 555                 // ok
 556                 return;
 557             }
 558         }
 559 
 560         if (!isInvertible) {
 561             fail("Should have thrown NonInvertibleTransformException");
 562         }
 563 
 564         assertNotNull(res);
 565         assertSame(inverseType, res.getClass());
 566         TransformHelper.assertMatrix(res, it);
 567     }
 568 
 569     @Test
 570     public void createInverseShouldUpdateCache() {
 571         Transform ct = t.clone();
 572         Transform res = null;
 573         boolean canInvert = isInvertible;
 574         try {
 575             res = ct.createInverse();
 576         } catch(NonInvertibleTransformException e) {
 577             if (canInvert) {
 578                 e.printStackTrace();
 579                 fail("NonInvertibleTransformException thrown for invertible transform");
 580             } else {
 581                 // ok
 582                 return;
 583             }
 584         }
 585 
 586         if (!canInvert) {
 587             fail("Should have thrown NonInvertibleTransformException");
 588         }
 589 
 590         assertNotNull(res);
 591         assertSame(inverseType, res.getClass());
 592         TransformHelper.assertMatrix(res, it);
 593 
 594         // modify the matrix to check the cache keeps up
 595 
 596         TransformHelper.modify(ct, 43);
 597         Transform inv = null;
 598         try {
 599             inv = TransformHelper.invert(ct);
 600             canInvert = true;
 601         } catch (NonInvertibleTransformException e) {
 602             canInvert = false;
 603         }
 604 
 605         try {
 606             res = ct.createInverse();
 607         } catch(NonInvertibleTransformException e) {
 608             if (canInvert) {
 609                 e.printStackTrace();
 610                 fail("NonInvertibleTransformException thrown for invertible transform");
 611             } else {
 612                 // ok
 613                 return;
 614             }
 615         }
 616 
 617         if (!isInvertible) {
 618             fail("Should have thrown NonInvertibleTransformException");
 619         }
 620 
 621         assertNotNull(res);
 622         TransformHelper.assertMatrix(res, inv);
 623 
 624         // emulate garbage collection of the cache to check it's renewed
 625         ct.clearInverseCache();
 626 
 627         try {
 628             res = ct.createInverse();
 629         } catch(NonInvertibleTransformException e) {
 630             if (canInvert) {
 631                 e.printStackTrace();
 632                 fail("NonInvertibleTransformException thrown for invertible transform");
 633             } else {
 634                 // ok
 635                 return;
 636             }
 637         }
 638 
 639         if (!isInvertible) {
 640             fail("Should have thrown NonInvertibleTransformException");
 641         }
 642 
 643         assertNotNull(res);
 644         TransformHelper.assertMatrix(res, inv);
 645     }
 646 
 647     @Test
 648     public void testTransformPoint3d() {
 649         Point3D p = new Point3D(12, -18, 30);
 650         Point3D expected = new Point3D(
 651             t.getMxx() * 12 - t.getMxy() * 18 + t.getMxz() * 30 + t.getTx(),
 652             t.getMyx() * 12 - t.getMyy() * 18 + t.getMyz() * 30 + t.getTy(),
 653             t.getMzx() * 12 - t.getMzy() * 18 + t.getMzz() * 30 + t.getTz());
 654 
 655 
 656         Point3D result = t.transform(p);
 657         assertEquals(expected.getX(), result.getX(), 0.00001);
 658         assertEquals(expected.getY(), result.getY(), 0.00001);
 659         assertEquals(expected.getZ(), result.getZ(), 0.00001);
 660 
 661         result = t.transform(12, -18, 30);
 662         assertEquals(expected.getX(), result.getX(), 0.00001);
 663         assertEquals(expected.getY(), result.getY(), 0.00001);
 664         assertEquals(expected.getZ(), result.getZ(), 0.00001);
 665     }
 666 
 667     @Test(expected=NullPointerException.class)
 668     public void testTransformNullPoint3D() {
 669         t.transform((Point3D) null);
 670     }
 671 
 672     @Test
 673     public void testTransformPoint2d() {
 674 
 675         Point2D p = new Point2D(12, -18);
 676         Point2D expected = new Point2D(
 677             t.getMxx() * 12 - t.getMxy() * 18 + t.getTx(),
 678             t.getMyx() * 12 - t.getMyy() * 18 + t.getTy());
 679 
 680         try {
 681             Point2D result = t.transform(p);
 682             if (!is2d) {
 683                 fail("Should have thrown ISE");
 684             }
 685             assertEquals(expected.getX(), result.getX(), 0.00001);
 686             assertEquals(expected.getY(), result.getY(), 0.00001);
 687         } catch (IllegalStateException e) {
 688             if (is2d) {
 689                 fail("Wrong exception thrown");
 690             }
 691         }
 692 
 693         try {
 694             Point2D result = t.transform(12, -18);
 695             if (!is2d) {
 696                 fail("Should have thrown ISE");
 697             }
 698             assertEquals(expected.getX(), result.getX(), 0.00001);
 699             assertEquals(expected.getY(), result.getY(), 0.00001);
 700         } catch (IllegalStateException e) {
 701             if (is2d) {
 702                 fail("Wrong exception thrown");
 703             }
 704         }
 705     }
 706 
 707     @Test(expected=NullPointerException.class)
 708     public void testTransformNullPoint2D() {
 709         t.transform((Point2D) null);
 710     }
 711 
 712     @Test
 713     public void testDeltaTransformPoint3d() {
 714         Point3D p = new Point3D(12, -18, 30);
 715         Point3D expected = new Point3D(
 716             t.getMxx() * 12 - t.getMxy() * 18 + t.getMxz() * 30,
 717             t.getMyx() * 12 - t.getMyy() * 18 + t.getMyz() * 30,
 718             t.getMzx() * 12 - t.getMzy() * 18 + t.getMzz() * 30);
 719 
 720 
 721         Point3D result = t.deltaTransform(p);
 722         assertEquals(expected.getX(), result.getX(), 0.00001);
 723         assertEquals(expected.getY(), result.getY(), 0.00001);
 724         assertEquals(expected.getZ(), result.getZ(), 0.00001);
 725 
 726         result = t.deltaTransform(12, -18, 30);
 727         assertEquals(expected.getX(), result.getX(), 0.00001);
 728         assertEquals(expected.getY(), result.getY(), 0.00001);
 729         assertEquals(expected.getZ(), result.getZ(), 0.00001);
 730     }
 731 
 732     @Test(expected=NullPointerException.class)
 733     public void testDeltaTransformNullPoint3D() {
 734         t.deltaTransform((Point3D) null);
 735     }
 736 
 737     @Test
 738     public void testDeltaTransformPoint2d() {
 739 
 740         Point2D p = new Point2D(12, -18);
 741         Point2D expected = new Point2D(
 742             t.getMxx() * 12 - t.getMxy() * 18,
 743             t.getMyx() * 12 - t.getMyy() * 18);
 744 
 745         try {
 746             Point2D result = t.deltaTransform(p);
 747             if (!is2d) {
 748                 fail("Should have thrown ISE");
 749             }
 750             assertEquals(expected.getX(), result.getX(), 0.00001);
 751             assertEquals(expected.getY(), result.getY(), 0.00001);
 752         } catch (IllegalStateException e) {
 753             if (is2d) {
 754                 fail("Wrong exception thrown");
 755             }
 756         }
 757 
 758         try {
 759             Point2D result = t.deltaTransform(12, -18);
 760             if (!is2d) {
 761                 fail("Should have thrown ISE");
 762             }
 763             assertEquals(expected.getX(), result.getX(), 0.00001);
 764             assertEquals(expected.getY(), result.getY(), 0.00001);
 765         } catch (IllegalStateException e) {
 766             if (is2d) {
 767                 fail("Wrong exception thrown");
 768             }
 769         }
 770     }
 771 
 772     @Test(expected=NullPointerException.class)
 773     public void testDeltaTransformNullPoint2D() {
 774         t.deltaTransform((Point2D) null);
 775     }
 776 
 777     @Test
 778     public void testTransformBounds() {
 779         Bounds result = t.transform(new BoundingBox(10, 11, 12, 13, 14, 15));
 780 
 781         Point3D[] points = new Point3D[] {
 782                 new Point3D(10, 11, 12),
 783                 new Point3D(10, 11, 27),
 784                 new Point3D(10, 25, 12),
 785                 new Point3D(10, 25, 27),
 786                 new Point3D(23, 11, 12),
 787                 new Point3D(23, 11, 27),
 788                 new Point3D(23, 25, 12),
 789                 new Point3D(23, 25, 27),
 790         };
 791 
 792         Point3D expected1 = new Point3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
 793         Point3D expected2 = new Point3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE);
 794 
 795         for (Point3D p : points) {
 796             Point3D tp = new Point3D(
 797                     t.getMxx() * p.getX() + t.getMxy() * p.getY() + t.getMxz() * p.getZ() + t.getTx(),
 798                     t.getMyx() * p.getX() + t.getMyy() * p.getY() + t.getMyz() * p.getZ() + t.getTy(),
 799                     t.getMzx() * p.getX() + t.getMzy() * p.getY() + t.getMzz() * p.getZ() + t.getTz());
 800             expected1 = new Point3D(Math.min(expected1.getX(), tp.getX()), Math.min(expected1.getY(), tp.getY()),
 801                     Math.min(expected1.getZ(), tp.getZ()));
 802             expected2 = new Point3D(Math.max(expected2.getX(), tp.getX()), Math.max(expected2.getY(), tp.getY()),
 803                     Math.max(expected2.getZ(), tp.getZ()));
 804 
 805         }
 806 
 807         assertEquals(expected1.getX(), result.getMinX(), 0.00001);
 808         assertEquals(expected1.getY(), result.getMinY(), 0.00001);
 809         assertEquals(expected1.getZ(), result.getMinZ(), 0.00001);
 810         assertEquals(expected2.getX(), result.getMaxX(), 0.00001);
 811         assertEquals(expected2.getY(), result.getMaxY(), 0.00001);
 812         assertEquals(expected2.getZ(), result.getMaxZ(), 0.00001);
 813     }
 814 
 815     @Test(expected=NullPointerException.class)
 816     public void testTransformNullBounds() {
 817         t.transform((Bounds) null);
 818     }
 819 
 820     @Test
 821     public void testTransform2DPoints() {
 822         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6 };
 823         double[] dstPts = new double[] { 1, 2, 3, 4, 5, 6 };
 824 
 825         Point2D expected1 = new Point2D(
 826             t.getMxx() * 3 + t.getMxy() * 4 + t.getTx(),
 827             t.getMyx() * 3 + t.getMyy() * 4 + t.getTy());
 828 
 829         Point2D expected2 = new Point2D(
 830             t.getMxx() * 5 + t.getMxy() * 6 + t.getTx(),
 831             t.getMyx() * 5 + t.getMyy() * 6 + t.getTy());
 832 
 833         try {
 834             t.transform2DPoints(srcPts, 3, dstPts, 1, 2);
 835             if (!is2d) {
 836                 fail("Should have thrown ISE");
 837             }
 838 
 839             assertEquals(1, dstPts[0], 0.00001);
 840             assertEquals(expected1.getX(), dstPts[1], 0.00001);
 841             assertEquals(expected1.getY(), dstPts[2], 0.00001);
 842             assertEquals(expected2.getX(), dstPts[3], 0.00001);
 843             assertEquals(expected2.getY(), dstPts[4], 0.00001);
 844             assertEquals(6, dstPts[5], 0.00001);
 845         } catch (IllegalStateException e) {
 846             if (is2d) {
 847                 fail("Wrong exception thrown");
 848             }
 849         }
 850     }
 851 
 852     @Test(expected=NullPointerException.class)
 853     public void testTransform2DPointsBothPtsNull() {
 854         t.transform2DPoints(null, 2, null, 0, 0);
 855     }
 856 
 857     @Test(expected=NullPointerException.class)
 858     public void testTransform2DPointsSrcPtsNull() {
 859         t.transform2DPoints(null, 2, new double[] { 1, 2 }, 0, 0);
 860     }
 861 
 862     @Test(expected=NullPointerException.class)
 863     public void testTransform2DPointsDstPtsNull() {
 864         t.transform2DPoints(new double[] { 1, 2, 3, 4 }, 2, null, 0, 0);
 865     }
 866 
 867     @Test
 868     public void testTransform2DPointsWithOverlap() {
 869         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
 870 
 871         Point2D expected1 = new Point2D(
 872             t.getMxx() * 2 + t.getMxy() * 3 + t.getTx(),
 873             t.getMyx() * 2 + t.getMyy() * 3 + t.getTy());
 874 
 875         Point2D expected2 = new Point2D(
 876             t.getMxx() * 4 + t.getMxy() * 5 + t.getTx(),
 877             t.getMyx() * 4 + t.getMyy() * 5 + t.getTy());
 878 
 879         try {
 880             t.transform2DPoints(srcPts, 2, srcPts, 4, 2);
 881             if (!is2d) {
 882                 fail("Should have thrown ISE");
 883             }
 884 
 885             assertEquals(0, srcPts[0], 0.00001);
 886             assertEquals(1, srcPts[1], 0.00001);
 887             assertEquals(2, srcPts[2], 0.00001);
 888             assertEquals(3, srcPts[3], 0.00001);
 889             assertEquals(expected1.getX(), srcPts[4], 0.00001);
 890             assertEquals(expected1.getY(), srcPts[5], 0.00001);
 891             assertEquals(expected2.getX(), srcPts[6], 0.00001);
 892             assertEquals(expected2.getY(), srcPts[7], 0.00001);
 893             assertEquals(8, srcPts[8], 0.00001);
 894         } catch (IllegalStateException e) {
 895             if (is2d) {
 896                 fail("Wrong exception thrown");
 897             }
 898         }
 899     }
 900 
 901     @Test(expected=IndexOutOfBoundsException.class)
 902     public void testTransform2DPointsSrcOut() {
 903         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7 };
 904         double[] dstPts = new double[] { 1, 2, 3, 4, 5, 6 };
 905 
 906         try {
 907             t.transform2DPoints(srcPts, 3, dstPts, 0, 3);
 908         } catch (IllegalStateException e) {
 909             if (!is2d) {
 910                 throw new IndexOutOfBoundsException("expected result");
 911             }
 912         }
 913     }
 914 
 915     @Test(expected=IndexOutOfBoundsException.class)
 916     public void testTransform2DPointsDstOut() {
 917         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7 };
 918         double[] dstPts = new double[] { 1 };
 919 
 920         try {
 921             t.transform2DPoints(srcPts, 1, dstPts, 0, 2);
 922         } catch (IllegalStateException e) {
 923             if (!is2d) {
 924                 throw new IndexOutOfBoundsException("expected result");
 925             }
 926         }
 927     }
 928 
 929     @Test
 930     public void testTransform3DPoints() {
 931         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
 932         double[] dstPts = new double[] { 1, 2, 3, 4, 5, 6, 7, 8 };
 933 
 934         Point3D expected1 = new Point3D(
 935             t.getMxx() * 3 + t.getMxy() * 4 + t.getMxz() * 5 + t.getTx(),
 936             t.getMyx() * 3 + t.getMyy() * 4 + t.getMyz() * 5 + t.getTy(),
 937             t.getMzx() * 3 + t.getMzy() * 4 + t.getMzz() * 5 + t.getTz());
 938 
 939         Point3D expected2 = new Point3D(
 940             t.getMxx() * 6 + t.getMxy() * 7 + t.getMxz() * 8 + t.getTx(),
 941             t.getMyx() * 6 + t.getMyy() * 7 + t.getMyz() * 8 + t.getTy(),
 942             t.getMzx() * 6 + t.getMzy() * 7 + t.getMzz() * 8 + t.getTz());
 943 
 944         t.transform3DPoints(srcPts, 3, dstPts, 1, 2);
 945 
 946         assertEquals(1, dstPts[0], 0.00001);
 947         assertEquals(expected1.getX(), dstPts[1], 0.00001);
 948         assertEquals(expected1.getY(), dstPts[2], 0.00001);
 949         assertEquals(expected1.getZ(), dstPts[3], 0.00001);
 950         assertEquals(expected2.getX(), dstPts[4], 0.00001);
 951         assertEquals(expected2.getY(), dstPts[5], 0.00001);
 952         assertEquals(expected2.getZ(), dstPts[6], 0.00001);
 953         assertEquals(8, dstPts[7], 0.00001);
 954     }
 955 
 956     @Test(expected=NullPointerException.class)
 957     public void testTransform3DPointsBothPtsNull() {
 958         t.transform3DPoints(null, 2, null, 0, 0);
 959     }
 960 
 961     @Test(expected=NullPointerException.class)
 962     public void testTransform3DPointsSrcPtsNull() {
 963         t.transform3DPoints(null, 2, new double[] { 1, 2, 3 }, 0, 0);
 964     }
 965 
 966     @Test(expected=NullPointerException.class)
 967     public void testTransform3DPointsDstPtsNull() {
 968         t.transform3DPoints(new double[] { 1, 2, 3, 4 }, 2, null, 0, 0);
 969     }
 970 
 971     @Test
 972     public void testTransform3DPointsWithOverlap() {
 973         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 974 
 975         Point3D expected1 = new Point3D(
 976             t.getMxx() * 2 + t.getMxy() * 3 + t.getMxz() * 4 + t.getTx(),
 977             t.getMyx() * 2 + t.getMyy() * 3 + t.getMyz() * 4 + t.getTy(),
 978             t.getMzx() * 2 + t.getMzy() * 3 + t.getMzz() * 4 + t.getTz());
 979 
 980         Point3D expected2 = new Point3D(
 981             t.getMxx() * 5 + t.getMxy() * 6 + t.getMxz() * 7 + t.getTx(),
 982             t.getMyx() * 5 + t.getMyy() * 6 + t.getMyz() * 7 + t.getTy(),
 983             t.getMzx() * 5 + t.getMzy() * 6 + t.getMzz() * 7 + t.getTz());
 984 
 985         t.transform3DPoints(srcPts, 2, srcPts, 3, 2);
 986 
 987         assertEquals(0, srcPts[0], 0.00001);
 988         assertEquals(1, srcPts[1], 0.00001);
 989         assertEquals(2, srcPts[2], 0.00001);
 990         assertEquals(expected1.getX(), srcPts[3], 0.00001);
 991         assertEquals(expected1.getY(), srcPts[4], 0.00001);
 992         assertEquals(expected1.getZ(), srcPts[5], 0.00001);
 993         assertEquals(expected2.getX(), srcPts[6], 0.00001);
 994         assertEquals(expected2.getY(), srcPts[7], 0.00001);
 995         assertEquals(expected2.getZ(), srcPts[8], 0.00001);
 996         assertEquals(9, srcPts[9], 0.00001);
 997     }
 998 
 999     @Test(expected=IndexOutOfBoundsException.class)
1000     public void testTransform3DPointsSrcOut() {
1001         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7 };
1002         double[] dstPts = new double[] { 1, 2, 3, 4, 5, 6 };
1003 
1004         t.transform3DPoints(srcPts, 6, dstPts, 0, 1);
1005     }
1006 
1007     @Test(expected=IndexOutOfBoundsException.class)
1008     public void testTransform3DPointsDstOut() {
1009         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7 };
1010         double[] dstPts = new double[] { 1 };
1011 
1012         t.transform3DPoints(srcPts, 1, dstPts, 0, 1);
1013     }
1014 
1015     @Test
1016     public void testInverseTransformPoint3d() throws Exception {
1017         Point3D p = new Point3D(12, -18, 30);
1018 
1019         Point3D expected = new Point3D(
1020             it.getMxx() * 12 - it.getMxy() * 18 + it.getMxz() * 30 + it.getTx(),
1021             it.getMyx() * 12 - it.getMyy() * 18 + it.getMyz() * 30 + it.getTy(),
1022             it.getMzx() * 12 - it.getMzy() * 18 + it.getMzz() * 30 + it.getTz());
1023 
1024         try {
1025             Point3D result = t.inverseTransform(p);
1026             if (!isInvertible) {
1027                 fail("Should have thrown NonInvertibleTransformException");
1028             }
1029             assertEquals(expected.getX(), result.getX(), 0.00001);
1030             assertEquals(expected.getY(), result.getY(), 0.00001);
1031             assertEquals(expected.getZ(), result.getZ(), 0.00001);
1032         } catch (NonInvertibleTransformException e) {
1033             if (isInvertible) {
1034                 fail("Wrong exception thrown");
1035             }
1036         }
1037 
1038         try {
1039             Point3D result = t.inverseTransform(12, -18, 30);
1040             if (!isInvertible) {
1041                 fail("Should have thrown NonInvertibleTransformException");
1042             }
1043             assertEquals(expected.getX(), result.getX(), 0.00001);
1044             assertEquals(expected.getY(), result.getY(), 0.00001);
1045             assertEquals(expected.getZ(), result.getZ(), 0.00001);
1046         } catch (NonInvertibleTransformException e) {
1047             if (isInvertible) {
1048                 fail("Wrong exception thrown");
1049             }
1050         }
1051     }
1052 
1053     @Test(expected=NullPointerException.class)
1054     public void testInverseTransformNullPoint3D()
1055             throws NonInvertibleTransformException {
1056         t.inverseTransform((Point3D) null);
1057     }
1058 
1059     @Test
1060     public void testInverseTransformPoint2d() throws Exception {
1061 
1062         Point2D p = new Point2D(12, -18);
1063         Point2D expected = new Point2D(
1064             it.getMxx() * 12 - it.getMxy() * 18 + it.getTx(),
1065             it.getMyx() * 12 - it.getMyy() * 18 + it.getTy());
1066 
1067         try {
1068             Point2D result = t.inverseTransform(p);
1069             if (!is2d) {
1070                 fail("Should have thrown ISE");
1071             }
1072             if (!isInvertible) {
1073                 fail("Should have thrown NonInvertibleTransformException");
1074             }
1075             assertEquals(expected.getX(), result.getX(), 0.00001);
1076             assertEquals(expected.getY(), result.getY(), 0.00001);
1077         } catch (IllegalStateException e) {
1078             if (is2d) {
1079                 fail("Wrong exception thrown");
1080             }
1081         } catch (NonInvertibleTransformException e) {
1082             if (isInvertible) {
1083                 fail("Wrong exception thrown");
1084             }
1085         }
1086 
1087         try {
1088             Point2D result = t.inverseTransform(12, -18);
1089             if (!is2d) {
1090                 fail("Should have thrown ISE");
1091             }
1092             if (!isInvertible) {
1093                 fail("Should have thrown NonInvertibleTransformException");
1094             }
1095             assertEquals(expected.getX(), result.getX(), 0.00001);
1096             assertEquals(expected.getY(), result.getY(), 0.00001);
1097         } catch (IllegalStateException e) {
1098             if (is2d) {
1099                 fail("Wrong exception thrown");
1100             }
1101         } catch (NonInvertibleTransformException e) {
1102             if (isInvertible) {
1103                 fail("Wrong exception thrown");
1104             }
1105         }
1106     }
1107 
1108     @Test(expected=NullPointerException.class)
1109     public void testInverseTransformNullPoint2D()
1110             throws NonInvertibleTransformException {
1111         t.inverseTransform((Point2D) null);
1112     }
1113 
1114     @Test
1115     public void testInverseDeltaTransformPoint3d() throws Exception {
1116         Point3D p = new Point3D(12, -18, 30);
1117         Point3D expected = new Point3D(
1118             it.getMxx() * 12 - it.getMxy() * 18 + it.getMxz() * 30,
1119             it.getMyx() * 12 - it.getMyy() * 18 + it.getMyz() * 30,
1120             it.getMzx() * 12 - it.getMzy() * 18 + it.getMzz() * 30);
1121 
1122         try {
1123             Point3D result = t.inverseDeltaTransform(p);
1124             if (!isInvertible) {
1125                 fail("Should have thrown NonInvertibleTransformException");
1126             }
1127             assertEquals(expected.getX(), result.getX(), 0.00001);
1128             assertEquals(expected.getY(), result.getY(), 0.00001);
1129             assertEquals(expected.getZ(), result.getZ(), 0.00001);
1130         } catch (NonInvertibleTransformException e) {
1131             if (isInvertible) {
1132                 fail("Wrong exception thrown");
1133             }
1134         }
1135 
1136         try {
1137             Point3D result = t.inverseDeltaTransform(12, -18, 30);
1138             if (!isInvertible) {
1139                 fail("Should have thrown NonInvertibleTransformException");
1140             }
1141             assertEquals(expected.getX(), result.getX(), 0.00001);
1142             assertEquals(expected.getY(), result.getY(), 0.00001);
1143             assertEquals(expected.getZ(), result.getZ(), 0.00001);
1144         } catch (NonInvertibleTransformException e) {
1145             if (isInvertible) {
1146                 fail("Wrong exception thrown");
1147             }
1148         }
1149     }
1150 
1151     @Test(expected=NullPointerException.class)
1152     public void testInverseDeltaTransformNullPoint3D()
1153             throws NonInvertibleTransformException {
1154         t.inverseDeltaTransform((Point3D) null);
1155     }
1156 
1157     @Test
1158     public void testInverseDeltaTransformPoint2d() throws Exception {
1159 
1160         Point2D p = new Point2D(12, -18);
1161         Point2D expected = new Point2D(
1162             it.getMxx() * 12 - it.getMxy() * 18,
1163             it.getMyx() * 12 - it.getMyy() * 18);
1164 
1165         try {
1166             Point2D result = t.inverseDeltaTransform(p);
1167             if (!is2d) {
1168                 fail("Should have thrown ISE");
1169             }
1170             if (!isInvertible) {
1171                 fail("Should have thrown NonInvertibleTransformException");
1172             }
1173             assertEquals(expected.getX(), result.getX(), 0.00001);
1174             assertEquals(expected.getY(), result.getY(), 0.00001);
1175         } catch (IllegalStateException e) {
1176             if (is2d) {
1177                 fail("Wrong exception thrown");
1178             }
1179         } catch (NonInvertibleTransformException e) {
1180             if (isInvertible) {
1181                 fail("Wrong exception thrown");
1182             }
1183         }
1184 
1185         try {
1186             Point2D result = t.inverseDeltaTransform(12, -18);
1187             if (!is2d) {
1188                 fail("Should have thrown ISE");
1189             }
1190             if (!isInvertible) {
1191                 fail("Should have thrown NonInvertibleTransformException");
1192             }
1193             assertEquals(expected.getX(), result.getX(), 0.00001);
1194             assertEquals(expected.getY(), result.getY(), 0.00001);
1195         } catch (IllegalStateException e) {
1196             if (is2d) {
1197                 fail("Wrong exception thrown");
1198             }
1199         } catch (NonInvertibleTransformException e) {
1200             if (isInvertible) {
1201                 fail("Wrong exception thrown");
1202             }
1203         }
1204     }
1205 
1206     @Test(expected=NullPointerException.class)
1207     public void testInverseDeltaTransformNullPoint2D()
1208             throws NonInvertibleTransformException {
1209         t.inverseDeltaTransform((Point2D) null);
1210     }
1211 
1212     @Test
1213     public void testInverseTransformBounds() throws Exception {
1214         Bounds result = null;
1215         try {
1216             result = t.inverseTransform(new BoundingBox(10, 11, 12, 13, 14, 15));
1217             if (!isInvertible) {
1218                 fail("Should have thrown NonInvertibleTransformException");
1219             }
1220 
1221             Point3D[] points = new Point3D[] {
1222                     new Point3D(10, 11, 12),
1223                     new Point3D(10, 11, 27),
1224                     new Point3D(10, 25, 12),
1225                     new Point3D(10, 25, 27),
1226                     new Point3D(23, 11, 12),
1227                     new Point3D(23, 11, 27),
1228                     new Point3D(23, 25, 12),
1229                     new Point3D(23, 25, 27),
1230             };
1231 
1232             Point3D expected1 = new Point3D(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
1233             Point3D expected2 = new Point3D(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE);
1234 
1235             for (Point3D p : points) {
1236                 Point3D tp = new Point3D(
1237                         it.getMxx() * p.getX() + it.getMxy() * p.getY() + it.getMxz() * p.getZ() + it.getTx(),
1238                         it.getMyx() * p.getX() + it.getMyy() * p.getY() + it.getMyz() * p.getZ() + it.getTy(),
1239                         it.getMzx() * p.getX() + it.getMzy() * p.getY() + it.getMzz() * p.getZ() + it.getTz());
1240                 expected1 = new Point3D(Math.min(expected1.getX(), tp.getX()), Math.min(expected1.getY(), tp.getY()),
1241                         Math.min(expected1.getZ(), tp.getZ()));
1242                 expected2 = new Point3D(Math.max(expected2.getX(), tp.getX()), Math.max(expected2.getY(), tp.getY()),
1243                         Math.max(expected2.getZ(), tp.getZ()));
1244 
1245             }
1246 
1247             assertEquals(expected1.getX(), result.getMinX(), 0.00001);
1248             assertEquals(expected1.getY(), result.getMinY(), 0.00001);
1249             assertEquals(expected1.getZ(), result.getMinZ(), 0.00001);
1250             assertEquals(expected2.getX(), result.getMaxX(), 0.00001);
1251             assertEquals(expected2.getY(), result.getMaxY(), 0.00001);
1252             assertEquals(expected2.getZ(), result.getMaxZ(), 0.00001);
1253 
1254         } catch (NonInvertibleTransformException e) {
1255             if (isInvertible) {
1256                 fail("Wrong exception thrown");
1257             }
1258             return;
1259         }
1260     }
1261 
1262     @Test(expected=NullPointerException.class)
1263     public void testInverseTransformNullBounds()
1264             throws NonInvertibleTransformException {
1265         t.inverseTransform((Bounds) null);
1266     }
1267 
1268     @Test
1269     public void testInverseTransform2DPoints() throws Exception {
1270         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6 };
1271         double[] dstPts = new double[] { 1, 2, 3, 4, 5, 6 };
1272 
1273         Point2D expected1 = new Point2D(
1274             it.getMxx() * 3 + it.getMxy() * 4 + it.getTx(),
1275             it.getMyx() * 3 + it.getMyy() * 4 + it.getTy());
1276 
1277         Point2D expected2 = new Point2D(
1278             it.getMxx() * 5 + it.getMxy() * 6 + it.getTx(),
1279             it.getMyx() * 5 + it.getMyy() * 6 + it.getTy());
1280 
1281         try {
1282             t.inverseTransform2DPoints(srcPts, 3, dstPts, 1, 2);
1283             if (!is2d) {
1284                 fail("Should have thrown ISE");
1285             }
1286             if (!isInvertible) {
1287                 fail("Should have thrown NonInvertibleTransformException");
1288             }
1289 
1290             assertEquals(1, dstPts[0], 0.00001);
1291             assertEquals(expected1.getX(), dstPts[1], 0.00001);
1292             assertEquals(expected1.getY(), dstPts[2], 0.00001);
1293             assertEquals(expected2.getX(), dstPts[3], 0.00001);
1294             assertEquals(expected2.getY(), dstPts[4], 0.00001);
1295             assertEquals(6, dstPts[5], 0.00001);
1296         } catch (IllegalStateException e) {
1297             if (is2d) {
1298                 fail("Wrong exception thrown");
1299             }
1300         } catch (NonInvertibleTransformException e) {
1301             if (isInvertible) {
1302                 fail("Wrong exception thrown");
1303             }
1304         }
1305     }
1306 
1307     @Test(expected=NullPointerException.class)
1308     public void testInverseTransform2DPointsBothPtsNull()
1309             throws NonInvertibleTransformException {
1310         t.inverseTransform2DPoints(null, 2, null, 0, 0);
1311     }
1312 
1313     @Test(expected=NullPointerException.class)
1314     public void testInverseTransform2DPointsSrcPtsNull()
1315             throws NonInvertibleTransformException {
1316         t.inverseTransform2DPoints(null, 2, new double[] { 1, 2, 3 }, 0, 0);
1317     }
1318 
1319     @Test(expected=NullPointerException.class)
1320     public void testInverseTransform2DPointsDstPtsNull()
1321             throws NonInvertibleTransformException {
1322         t.inverseTransform2DPoints(new double[] { 1, 2, 3, 4 }, 2, null, 0, 0);
1323     }
1324 
1325     @Test
1326     public void testInverseTransform2DPointsWithOverlap() throws Exception {
1327         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
1328 
1329         Point2D expected1 = new Point2D(
1330             it.getMxx() * 2 + it.getMxy() * 3 + it.getTx(),
1331             it.getMyx() * 2 + it.getMyy() * 3 + it.getTy());
1332 
1333         Point2D expected2 = new Point2D(
1334             it.getMxx() * 4 + it.getMxy() * 5 + it.getTx(),
1335             it.getMyx() * 4 + it.getMyy() * 5 + it.getTy());
1336 
1337         try {
1338             t.inverseTransform2DPoints(srcPts, 2, srcPts, 4, 2);
1339             if (!is2d) {
1340                 fail("Should have thrown ISE");
1341             }
1342             if (!isInvertible) {
1343                 fail("Should have thrown NonInvertibleTransformException");
1344             }
1345             assertEquals(0, srcPts[0], 0.00001);
1346             assertEquals(1, srcPts[1], 0.00001);
1347             assertEquals(2, srcPts[2], 0.00001);
1348             assertEquals(3, srcPts[3], 0.00001);
1349             assertEquals(expected1.getX(), srcPts[4], 0.00001);
1350             assertEquals(expected1.getY(), srcPts[5], 0.00001);
1351             assertEquals(expected2.getX(), srcPts[6], 0.00001);
1352             assertEquals(expected2.getY(), srcPts[7], 0.00001);
1353             assertEquals(8, srcPts[8], 0.00001);
1354         } catch (IllegalStateException e) {
1355             if (is2d) {
1356                 fail("Wrong exception thrown");
1357             }
1358         } catch (NonInvertibleTransformException e) {
1359             if (isInvertible) {
1360                 fail("Wrong exception thrown");
1361             }
1362         }
1363     }
1364 
1365     @Test(expected=IndexOutOfBoundsException.class)
1366     public void testInverseTransform2DPointsSrcOut() throws Exception {
1367         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7 };
1368         double[] dstPts = new double[] { 1, 2, 3, 4, 5, 6 };
1369 
1370         try {
1371             t.inverseTransform2DPoints(srcPts, 3, dstPts, 0, 3);
1372         } catch (IllegalStateException e) {
1373             if (!is2d) {
1374                 throw new IndexOutOfBoundsException("expected result");
1375             }
1376         } catch (NonInvertibleTransformException e) {
1377             if (!isInvertible) {
1378                 throw new IndexOutOfBoundsException("expected result");
1379             }
1380         }
1381     }
1382 
1383     @Test(expected=IndexOutOfBoundsException.class)
1384     public void testInverseTransform2DPointsDstOut() throws Exception {
1385         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7 };
1386         double[] dstPts = new double[] { 1 };
1387 
1388         try {
1389             t.inverseTransform2DPoints(srcPts, 1, dstPts, 0, 2);
1390         } catch (IllegalStateException e) {
1391             if (!is2d) {
1392                 throw new IndexOutOfBoundsException("expected result");
1393             }
1394         } catch (NonInvertibleTransformException e) {
1395             if (!isInvertible) {
1396                 throw new IndexOutOfBoundsException("expected result");
1397             }
1398         }
1399     }
1400 
1401     @Test
1402     public void testInverseTransform3DPoints() throws Exception {
1403         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
1404         double[] dstPts = new double[] { 1, 2, 3, 4, 5, 6, 7, 8 };
1405 
1406         Point3D expected1 = new Point3D(
1407             it.getMxx() * 3 + it.getMxy() * 4 + it.getMxz() * 5 + it.getTx(),
1408             it.getMyx() * 3 + it.getMyy() * 4 + it.getMyz() * 5 + it.getTy(),
1409             it.getMzx() * 3 + it.getMzy() * 4 + it.getMzz() * 5 + it.getTz());
1410 
1411         Point3D expected2 = new Point3D(
1412             it.getMxx() * 6 + it.getMxy() * 7 + it.getMxz() * 8 + it.getTx(),
1413             it.getMyx() * 6 + it.getMyy() * 7 + it.getMyz() * 8 + it.getTy(),
1414             it.getMzx() * 6 + it.getMzy() * 7 + it.getMzz() * 8 + it.getTz());
1415 
1416         try {
1417             t.inverseTransform3DPoints(srcPts, 3, dstPts, 1, 2);
1418             if (!isInvertible) {
1419                 fail("Should have thrown NonInvertibleTransformException");
1420             }
1421 
1422             assertEquals(1, dstPts[0], 0.00001);
1423             assertEquals(expected1.getX(), dstPts[1], 0.00001);
1424             assertEquals(expected1.getY(), dstPts[2], 0.00001);
1425             assertEquals(expected1.getZ(), dstPts[3], 0.00001);
1426             assertEquals(expected2.getX(), dstPts[4], 0.00001);
1427             assertEquals(expected2.getY(), dstPts[5], 0.00001);
1428             assertEquals(expected2.getZ(), dstPts[6], 0.00001);
1429             assertEquals(8, dstPts[7], 0.00001);
1430         } catch (NonInvertibleTransformException e) {
1431             if (isInvertible) {
1432                 fail("Wrong exception thrown");
1433             }
1434         }
1435     }
1436 
1437     @Test(expected=NullPointerException.class)
1438     public void testInverseTransform3DPointsBothPtsNull()
1439             throws NonInvertibleTransformException {
1440         t.inverseTransform3DPoints(null, 2, null, 0, 0);
1441     }
1442 
1443     @Test(expected=NullPointerException.class)
1444     public void testInverseTransform3DPointsSrcPtsNull()
1445             throws NonInvertibleTransformException {
1446         t.inverseTransform3DPoints(null, 2, new double[] { 1, 2, 3 }, 0, 0);
1447     }
1448 
1449     @Test(expected=NullPointerException.class)
1450     public void testInverseTransform3DPointsDstPtsNull()
1451             throws NonInvertibleTransformException {
1452         t.inverseTransform3DPoints(new double[] { 1, 2, 3, 4 }, 2, null, 0, 0);
1453     }
1454 
1455     @Test
1456     public void testInverseTransform3DPointsWithOverlap() throws Exception {
1457         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
1458 
1459         Point3D expected1 = new Point3D(
1460             it.getMxx() * 2 + it.getMxy() * 3 + it.getMxz() * 4 + it.getTx(),
1461             it.getMyx() * 2 + it.getMyy() * 3 + it.getMyz() * 4 + it.getTy(),
1462             it.getMzx() * 2 + it.getMzy() * 3 + it.getMzz() * 4 + it.getTz());
1463 
1464         Point3D expected2 = new Point3D(
1465             it.getMxx() * 5 + it.getMxy() * 6 + it.getMxz() * 7 + it.getTx(),
1466             it.getMyx() * 5 + it.getMyy() * 6 + it.getMyz() * 7 + it.getTy(),
1467             it.getMzx() * 5 + it.getMzy() * 6 + it.getMzz() * 7 + it.getTz());
1468 
1469         try {
1470             t.inverseTransform3DPoints(srcPts, 2, srcPts, 3, 2);
1471             if (!isInvertible) {
1472                 fail("Should have thrown NonInvertibleTransformException");
1473             }
1474 
1475             assertEquals(0, srcPts[0], 0.00001);
1476             assertEquals(1, srcPts[1], 0.00001);
1477             assertEquals(2, srcPts[2], 0.00001);
1478             assertEquals(expected1.getX(), srcPts[3], 0.00001);
1479             assertEquals(expected1.getY(), srcPts[4], 0.00001);
1480             assertEquals(expected1.getZ(), srcPts[5], 0.00001);
1481             assertEquals(expected2.getX(), srcPts[6], 0.00001);
1482             assertEquals(expected2.getY(), srcPts[7], 0.00001);
1483             assertEquals(expected2.getZ(), srcPts[8], 0.00001);
1484             assertEquals(9, srcPts[9], 0.00001);
1485         } catch (NonInvertibleTransformException e) {
1486             if (isInvertible) {
1487                 fail("Wrong exception thrown");
1488             }
1489         }
1490     }
1491 
1492     @Test(expected=IndexOutOfBoundsException.class)
1493     public void testInverseTransform3DPointsSrcOut() throws Exception {
1494         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7 };
1495         double[] dstPts = new double[] { 1, 2, 3, 4, 5, 6 };
1496 
1497         try {
1498             t.inverseTransform3DPoints(srcPts, 6, dstPts, 0, 1);
1499         } catch (NonInvertibleTransformException e) {
1500             if (!isInvertible) {
1501                 throw new IndexOutOfBoundsException("expected result");
1502             }
1503         }
1504     }
1505 
1506     @Test(expected=IndexOutOfBoundsException.class)
1507     public void testInverseTransform3DPointsDstOut() throws Exception {
1508         double[] srcPts = new double[] { 0, 1, 2, 3, 4, 5, 6, 7 };
1509         double[] dstPts = new double[] { 1 };
1510 
1511         try {
1512             t.inverseTransform3DPoints(srcPts, 1, dstPts, 0, 1);
1513         } catch (NonInvertibleTransformException e) {
1514             if (!isInvertible) {
1515                 throw new IndexOutOfBoundsException("expected result");
1516             }
1517         }
1518     }
1519 
1520     @Test
1521     public void testDeterminant() {
1522         assertEquals(TransformHelper.determinant(t), t.determinant(), 0.00001);
1523     }
1524 
1525     @Test
1526     public void testIsType2D() {
1527         Transform clone = t.clone();
1528 
1529         if (is2d) {
1530             assertTrue(t.isType2D());
1531             if (TransformHelper.make3D(clone)) {
1532                 assertFalse(clone.isType2D());
1533             }
1534         } else {
1535             assertFalse(t.isType2D());
1536             if (TransformHelper.make2D(clone)) {
1537                 assertTrue(clone.isType2D());
1538             }
1539         }
1540     }
1541 
1542     @Test
1543     public void testType2DProperty() {
1544         Transform clone = t.clone();
1545 
1546         assertEquals("type2D", clone.type2DProperty().getName());
1547         assertSame(clone, clone.type2DProperty().getBean());
1548     }
1549 
1550     @Test
1551     public void testType2DPropertyGetter() {
1552         Transform clone = t.clone();
1553 
1554         if (is2d) {
1555             assertTrue(clone.type2DProperty().get());
1556             assertTrue(clone.isType2D());
1557             if (TransformHelper.make3D(clone)) {
1558                 assertFalse(clone.type2DProperty().get());
1559                 assertFalse(clone.isType2D());
1560             }
1561         } else {
1562             assertFalse(clone.type2DProperty().get());
1563             assertFalse(clone.isType2D());
1564             if (TransformHelper.make2D(clone)) {
1565                 assertTrue(clone.type2DProperty().get());
1566                 assertTrue(clone.isType2D());
1567             }
1568         }
1569     }
1570 
1571     @Test
1572     public void testType2DPropertyInvalidation() {
1573         final Transform clone = t.clone();
1574 
1575         InvalidationListener l =
1576                 valueModel -> {
1577                      if (is2d) {
1578                          assertFalse(clone.type2DProperty().get());
1579                      } else {
1580                          assertTrue(clone.type2DProperty().get());
1581                      }
1582                      listenerCalled = true;
1583                 };
1584 
1585         clone.type2DProperty().addListener(l);
1586 
1587         listenerCalled = false;
1588 
1589         if (is2d) {
1590             if (TransformHelper.make3D(clone)) {
1591                 assertTrue(listenerCalled);
1592             }
1593         } else {
1594             if (TransformHelper.make2D(clone)) {
1595                 assertTrue(listenerCalled);
1596             }
1597         }
1598 
1599         listenerCalled = false;
1600         clone.type2DProperty().removeListener(l);
1601 
1602         if (is2d) {
1603             TransformHelper.make2D(clone);
1604             assertFalse(listenerCalled);
1605         } else {
1606             TransformHelper.make3D(clone);
1607             assertFalse(listenerCalled);
1608         }
1609     }
1610 
1611     @Test
1612     public void testType2DPropertyChange() {
1613         final Transform clone = t.clone();
1614 
1615         ChangeListener<Boolean> l =
1616                 (observable, oldValue, newValue) -> {
1617 
1618                     if ((is2d && (eventCounter == 0 || eventCounter == 2))
1619                             || (!is2d && eventCounter == 1)) {
1620                         assertTrue(oldValue);
1621                         assertFalse(newValue);
1622                         assertFalse(clone.type2DProperty().get());
1623                     } else {
1624                         assertFalse(oldValue);
1625                         assertTrue(newValue);
1626                         assertTrue(clone.type2DProperty().get());
1627                     }
1628 
1629                     listenerCalled = true;
1630                     eventCounter++;
1631                 };
1632 
1633         clone.type2DProperty().addListener(l);
1634 
1635         listenerCalled = false;
1636         eventCounter = 0;
1637         TransformHelper.modify(clone, 42);
1638         assertFalse(listenerCalled);
1639 
1640         if (is2d) {
1641             if (TransformHelper.make3D(clone)) {
1642                 assertTrue(listenerCalled);
1643                 listenerCalled = false;
1644                 TransformHelper.make3D(clone);
1645                 assertFalse(listenerCalled);
1646                 TransformHelper.make2D(clone);
1647                 assertTrue(listenerCalled);
1648                 listenerCalled = false;
1649                 clone.type2DProperty().removeListener(l);
1650                 TransformHelper.make3D(clone);
1651                 assertFalse(listenerCalled);
1652             }
1653         } else {
1654             if (TransformHelper.make2D(clone)) {
1655                 assertTrue(listenerCalled);
1656                 listenerCalled = false;
1657                 TransformHelper.make2D(clone);
1658                 assertFalse(listenerCalled);
1659                 TransformHelper.make3D(clone);
1660                 assertTrue(listenerCalled);
1661                 listenerCalled = false;
1662                 clone.type2DProperty().removeListener(l);
1663                 TransformHelper.make2D(clone);
1664                 assertFalse(listenerCalled);
1665             }
1666         }
1667     }
1668 
1669     @Test
1670     public void testIsIdentity() {
1671         Transform clone = t.clone();
1672 
1673         if (isIdentity) {
1674             assertTrue(t.isIdentity());
1675             if (TransformHelper.modify(clone, 42)) {
1676                 assertFalse(clone.isIdentity());
1677             }
1678         } else {
1679             assertFalse(t.isIdentity());
1680             if (TransformHelper.makeIdentity(clone)) {
1681                 assertTrue(clone.isIdentity());
1682             }
1683         }
1684     }
1685 
1686     @Test
1687     public void testIdentityProperty() {
1688         Transform clone = t.clone();
1689 
1690         assertEquals("identity", clone.identityProperty().getName());
1691         assertSame(clone, clone.identityProperty().getBean());
1692     }
1693 
1694     @Test
1695     public void testIdentityPropertyGetter() {
1696         Transform clone = t.clone();
1697 
1698         if (isIdentity) {
1699             assertTrue(clone.identityProperty().get());
1700             assertTrue(clone.isIdentity());
1701             if (TransformHelper.modify(clone, 42)) {
1702                 assertFalse(clone.identityProperty().get());
1703                 assertFalse(clone.isIdentity());
1704             }
1705         } else {
1706             assertFalse(clone.identityProperty().get());
1707             assertFalse(clone.isIdentity());
1708             if (TransformHelper.makeIdentity(clone)) {
1709                 assertTrue(clone.identityProperty().get());
1710                 assertTrue(clone.isIdentity());
1711             }
1712         }
1713     }
1714 
1715     @Test
1716     public void testIdentityPropertyInvalidation() {
1717         final Transform clone = t.clone();
1718 
1719         InvalidationListener l =
1720                 valueModel -> {
1721                      if (isIdentity) {
1722                          if (!clone.identityProperty().get()) {
1723                             listenerCalled = true;
1724                          }
1725                      } else {
1726                          if (clone.identityProperty().get()) {
1727                             listenerCalled = true;
1728                          }
1729                      }
1730                 };
1731 
1732         clone.identityProperty().addListener(l);
1733 
1734         listenerCalled = false;
1735 
1736         if (isIdentity) {
1737             if (TransformHelper.modify(clone, 42)) {
1738                 assertTrue(listenerCalled);
1739             }
1740         } else {
1741             if (TransformHelper.makeIdentity(clone)) {
1742                 assertTrue(listenerCalled);
1743             }
1744         }
1745 
1746         listenerCalled = false;
1747         clone.identityProperty().removeListener(l);
1748 
1749         if (isIdentity) {
1750             TransformHelper.makeIdentity(clone);
1751             TransformHelper.modify(clone, 42);
1752             assertFalse(listenerCalled);
1753         } else {
1754             TransformHelper.modify(clone, 42);
1755             TransformHelper.makeIdentity(clone);
1756             assertFalse(listenerCalled);
1757         }
1758     }
1759 
1760     @Test
1761     public void testIdentityPropertyChange() {
1762         final Transform clone = t.clone();
1763 
1764         ChangeListener<Boolean> l =
1765                 (observable, oldValue, newValue) -> {
1766                     if (isIdentity) {
1767                         if (oldValue == true && newValue == false
1768                                 && clone.identityProperty().get() == false) {
1769                             listenerCalled = true;
1770                         }
1771                     } else {
1772                         if (oldValue == false && newValue == true
1773                                 && clone.identityProperty().get() == true) {
1774                             listenerCalled = true;
1775                         }
1776                     }
1777 
1778                 };
1779 
1780         clone.identityProperty().addListener(l);
1781 
1782         listenerCalled = false;
1783 
1784         if (isIdentity) {
1785             if (TransformHelper.modify(clone, 42)) {
1786                 assertTrue(listenerCalled);
1787                 listenerCalled = false;
1788                 TransformHelper.modify(clone, 43);
1789                 assertFalse(listenerCalled);
1790 
1791                 clone.identityProperty().removeListener(l);
1792                 TransformHelper.makeIdentity(clone);
1793                 TransformHelper.modify(clone, 42);
1794                 assertFalse(listenerCalled);
1795             }
1796         } else {
1797             TransformHelper.modify(clone, 42);
1798             assertFalse(listenerCalled);
1799             if (TransformHelper.makeIdentity(clone)) {
1800                 assertTrue(listenerCalled);
1801                 listenerCalled = false;
1802                 TransformHelper.makeIdentity(clone);
1803                 assertFalse(listenerCalled);
1804 
1805                 clone.identityProperty().removeListener(l);
1806                 TransformHelper.modify(clone, 42);
1807                 TransformHelper.makeIdentity(clone);
1808                 assertFalse(listenerCalled);
1809             }
1810         }
1811     }
1812 
1813     @Test
1814     public void testSimilarTo() {
1815         Transform clone = t.clone();
1816 
1817         assertTrue(t.similarTo(clone, new BoundingBox(-10000, -10000, 10000, 10000), 1e-10));
1818 
1819         if (TransformHelper.tinyModify(clone)) {
1820             assertTrue(t.similarTo(clone, new BoundingBox(0, 0, 1, 1), 5));
1821             if (is2d) {
1822                 assertTrue(t.similarTo(clone, new BoundingBox(0, 0, 0, 0.1, 0.1, 10000), 2));
1823             } else {
1824                 assertFalse(t.similarTo(clone, new BoundingBox(0, 0, 0, 0.1, 0.1, 10000), 2));
1825             }
1826             assertFalse(t.similarTo(clone, new BoundingBox(0, 0, 1000, 1000), 0.5));
1827 
1828             if (t instanceof Translate) {
1829                 assertTrue(t.similarTo(clone, new BoundingBox(0, 0, 1, 1), 4));
1830                 assertFalse(t.similarTo(clone, new BoundingBox(0, 0, 1, 1), 0.5));
1831             }
1832 
1833             if (t instanceof Scale) {
1834                 assertTrue(t.similarTo(clone, new BoundingBox(0, 0, 1, 1), 5));
1835                 assertFalse(t.similarTo(clone, new BoundingBox(0, 0, 1000, 1000), 5));
1836             }
1837 
1838         }
1839     }
1840 
1841     @Test(expected=NullPointerException.class)
1842     public void testSimilarToNullTransform() {
1843         t.similarTo(null, new BoundingBox(0, 0, 0, 1, 1, 1), 0);
1844     }
1845 
1846     @Test(expected=NullPointerException.class)
1847     public void testSimilarToNullRange() {
1848         t.similarTo(t, null, 0);
1849     }
1850 
1851     private void assertGetElement(MatrixType type, int row, int col,
1852             double expected, boolean iae, boolean iob) {
1853         double res = Double.MIN_VALUE;
1854 
1855         try {
1856             res = t.getElement(type, row, col);
1857         } catch (IllegalArgumentException e) {
1858             if (iae) {
1859                 // ok
1860                 return;
1861             }
1862         } catch (IndexOutOfBoundsException e) {
1863             if (iob) {
1864                 // ok
1865                 return;
1866             }
1867         }
1868 
1869         if (iae) {
1870             fail("Should have thrown IAE");
1871         }
1872         if (iob) {
1873             fail("Should have thrown IOB");
1874         }
1875 
1876         assertEquals(expected, res, 1e-100);
1877     }
1878 
1879     @Test
1880     public void testGetElement() {
1881         assertGetElement(MatrixType.MT_2D_2x3, 0, 0, t.getMxx(), !is2d, false);
1882         assertGetElement(MatrixType.MT_2D_2x3, 0, 1, t.getMxy(), !is2d, false);
1883         assertGetElement(MatrixType.MT_2D_2x3, 0, 2, t.getTx(), !is2d, false);
1884         assertGetElement(MatrixType.MT_2D_2x3, 1, 0, t.getMyx(), !is2d, false);
1885         assertGetElement(MatrixType.MT_2D_2x3, 1, 1, t.getMyy(), !is2d, false);
1886         assertGetElement(MatrixType.MT_2D_2x3, 1, 2, t.getTy(), !is2d, false);
1887         assertGetElement(MatrixType.MT_2D_2x3, -1, 0, 0, !is2d, true);
1888         assertGetElement(MatrixType.MT_2D_2x3, 2, 1, 0, !is2d, true);
1889         assertGetElement(MatrixType.MT_2D_2x3, 1, 3, 0, !is2d, true);
1890         assertGetElement(MatrixType.MT_2D_2x3, 1, -1, 0, !is2d, true);
1891         assertGetElement(MatrixType.MT_2D_3x3, 0, 0, t.getMxx(), !is2d, false);
1892         assertGetElement(MatrixType.MT_2D_3x3, 0, 1, t.getMxy(), !is2d, false);
1893         assertGetElement(MatrixType.MT_2D_3x3, 0, 2, t.getTx(), !is2d, false);
1894         assertGetElement(MatrixType.MT_2D_3x3, 1, 0, t.getMyx(), !is2d, false);
1895         assertGetElement(MatrixType.MT_2D_3x3, 1, 1, t.getMyy(), !is2d, false);
1896         assertGetElement(MatrixType.MT_2D_3x3, 1, 2, t.getTy(), !is2d, false);
1897         assertGetElement(MatrixType.MT_2D_3x3, 2, 0, 0, !is2d, false);
1898         assertGetElement(MatrixType.MT_2D_3x3, 2, 1, 0, !is2d, false);
1899         assertGetElement(MatrixType.MT_2D_3x3, 2, 2, 1, !is2d, false);
1900         assertGetElement(MatrixType.MT_2D_3x3, -1, 0, 0, !is2d, true);
1901         assertGetElement(MatrixType.MT_2D_3x3, 3, 1, 0, !is2d, true);
1902         assertGetElement(MatrixType.MT_2D_3x3, 1, 3, 0, !is2d, true);
1903         assertGetElement(MatrixType.MT_2D_3x3, 1, -1, 0, !is2d, true);
1904         assertGetElement(MatrixType.MT_3D_3x4, 0, 0, t.getMxx(), false, false);
1905         assertGetElement(MatrixType.MT_3D_3x4, 0, 1, t.getMxy(), false, false);
1906         assertGetElement(MatrixType.MT_3D_3x4, 0, 2, t.getMxz(), false, false);
1907         assertGetElement(MatrixType.MT_3D_3x4, 0, 3, t.getTx(), false, false);
1908         assertGetElement(MatrixType.MT_3D_3x4, 1, 0, t.getMyx(), false, false);
1909         assertGetElement(MatrixType.MT_3D_3x4, 1, 1, t.getMyy(), false, false);
1910         assertGetElement(MatrixType.MT_3D_3x4, 1, 2, t.getMyz(), false, false);
1911         assertGetElement(MatrixType.MT_3D_3x4, 1, 3, t.getTy(), false, false);
1912         assertGetElement(MatrixType.MT_3D_3x4, 2, 0, t.getMzx(), false, false);
1913         assertGetElement(MatrixType.MT_3D_3x4, 2, 1, t.getMzy(), false, false);
1914         assertGetElement(MatrixType.MT_3D_3x4, 2, 2, t.getMzz(), false, false);
1915         assertGetElement(MatrixType.MT_3D_3x4, 2, 3, t.getTz(), false, false);
1916         assertGetElement(MatrixType.MT_3D_3x4, -1, 0, 0, false, true);
1917         assertGetElement(MatrixType.MT_3D_3x4, 3, 1, 0, false, true);
1918         assertGetElement(MatrixType.MT_3D_3x4, 1, 4, 0, false, true);
1919         assertGetElement(MatrixType.MT_3D_3x4, 1, -1, 0, false, true);
1920         assertGetElement(MatrixType.MT_3D_4x4, 0, 0, t.getMxx(), false, false);
1921         assertGetElement(MatrixType.MT_3D_4x4, 0, 1, t.getMxy(), false, false);
1922         assertGetElement(MatrixType.MT_3D_4x4, 0, 2, t.getMxz(), false, false);
1923         assertGetElement(MatrixType.MT_3D_4x4, 0, 3, t.getTx(), false, false);
1924         assertGetElement(MatrixType.MT_3D_4x4, 1, 0, t.getMyx(), false, false);
1925         assertGetElement(MatrixType.MT_3D_4x4, 1, 1, t.getMyy(), false, false);
1926         assertGetElement(MatrixType.MT_3D_4x4, 1, 2, t.getMyz(), false, false);
1927         assertGetElement(MatrixType.MT_3D_4x4, 1, 3, t.getTy(), false, false);
1928         assertGetElement(MatrixType.MT_3D_4x4, 2, 0, t.getMzx(), false, false);
1929         assertGetElement(MatrixType.MT_3D_4x4, 2, 1, t.getMzy(), false, false);
1930         assertGetElement(MatrixType.MT_3D_4x4, 2, 2, t.getMzz(), false, false);
1931         assertGetElement(MatrixType.MT_3D_4x4, 2, 3, t.getTz(), false, false);
1932         assertGetElement(MatrixType.MT_3D_4x4, 3, 0, 0, false, false);
1933         assertGetElement(MatrixType.MT_3D_4x4, 3, 1, 0, false, false);
1934         assertGetElement(MatrixType.MT_3D_4x4, 3, 2, 0, false, false);
1935         assertGetElement(MatrixType.MT_3D_4x4, 3, 3, 1, false, false);
1936         assertGetElement(MatrixType.MT_3D_4x4, -1, 0, 0, false, true);
1937         assertGetElement(MatrixType.MT_3D_4x4, 4, 1, 0, false, true);
1938         assertGetElement(MatrixType.MT_3D_4x4, 1, 4, 0, false, true);
1939         assertGetElement(MatrixType.MT_3D_4x4, 1, -1, 0, false, true);
1940     }
1941 
1942     @Test(expected=NullPointerException.class)
1943     public void testGetElementNullType() {
1944         t.getElement(null, 0, 0);
1945     }
1946 
1947     private void assertArray(MatrixType type, double[] a, Transform t) {
1948         switch (type) {
1949             case MT_2D_3x3:
1950                 assertEquals(0, a[6], 1e-100);
1951                 assertEquals(0, a[7], 1e-100);
1952                 assertEquals(1, a[8], 1e-100);
1953                 // fallthrough
1954             case MT_2D_2x3:
1955                 assertEquals(t.getMxx(), a[0], 1e-100);
1956                 assertEquals(t.getMxy(), a[1], 1e-100);
1957                 assertEquals(t.getTx(),  a[2], 1e-100);
1958                 assertEquals(t.getMyx(), a[3], 1e-100);
1959                 assertEquals(t.getMyy(), a[4], 1e-100);
1960                 assertEquals(t.getTy(),  a[5], 1e-100);
1961                 break;
1962             case MT_3D_4x4:
1963                 assertEquals(0, a[12], 1e-100);
1964                 assertEquals(0, a[13], 1e-100);
1965                 assertEquals(0, a[14], 1e-100);
1966                 assertEquals(1, a[15], 1e-100);
1967                 // fallthrough
1968             case MT_3D_3x4:
1969                 assertEquals(t.getMxx(), a[0], 1e-100);
1970                 assertEquals(t.getMxy(), a[1], 1e-100);
1971                 assertEquals(t.getMxz(), a[2], 1e-100);
1972                 assertEquals(t.getTx(),  a[3], 1e-100);
1973                 assertEquals(t.getMyx(), a[4], 1e-100);
1974                 assertEquals(t.getMyy(), a[5], 1e-100);
1975                 assertEquals(t.getMyz(), a[6], 1e-100);
1976                 assertEquals(t.getTy(),  a[7], 1e-100);
1977                 assertEquals(t.getMzx(), a[8], 1e-100);
1978                 assertEquals(t.getMzy(), a[9], 1e-100);
1979                 assertEquals(t.getMzz(), a[10], 1e-100);
1980                 assertEquals(t.getTz(),  a[11], 1e-100);
1981                 break;
1982         }
1983     }
1984 
1985     private void assertToArray2D(MatrixType type, double[] tmp,
1986             boolean shouldPass, boolean shouldUse) {
1987         double[] a = null;
1988         try {
1989             if (shouldPass) {
1990                 a = t.toArray(type, tmp);
1991             } else {
1992                 a = t.toArray(type);
1993             }
1994         } catch (IllegalArgumentException e) {
1995             if (is2d) {
1996                 fail("Wrong exception thrown");
1997             }
1998             return;
1999         }
2000 
2001         if (!is2d) {
2002             fail("Should have thrown IAE");
2003         } else {
2004             assertNotNull(a);
2005             if (shouldUse) {
2006                 assertSame(tmp, a);
2007             }
2008             assertArray(type, a, t);
2009         }
2010     }
2011 
2012     private void assertToArray3D(MatrixType type, double[] tmp,
2013             boolean shouldPass, boolean shouldUse) {
2014         double[] a = null;
2015 
2016         if (shouldPass) {
2017             a = t.toArray(type, tmp);
2018         } else {
2019             a = t.toArray(type);
2020         }
2021 
2022         assertNotNull(a);
2023         if (shouldUse) {
2024             assertSame(tmp, a);
2025         }
2026         assertArray(type, a, t);
2027     }
2028 
2029     @Test
2030     public void testToArray() {
2031 
2032         assertToArray2D(MatrixType.MT_2D_2x3, null, false, false);
2033         assertToArray2D(MatrixType.MT_2D_2x3, null, true, false);
2034         assertToArray2D(MatrixType.MT_2D_2x3, new double[4], true, false);
2035         assertToArray2D(MatrixType.MT_2D_2x3, new double[6], true, true);
2036 
2037         assertToArray2D(MatrixType.MT_2D_3x3, null, false, false);
2038         assertToArray2D(MatrixType.MT_2D_3x3, null, true, false);
2039         assertToArray2D(MatrixType.MT_2D_3x3, new double[8], true, false);
2040         assertToArray2D(MatrixType.MT_2D_3x3, new double[9], true, true);
2041 
2042         assertToArray3D(MatrixType.MT_3D_3x4, null, false, false);
2043         assertToArray3D(MatrixType.MT_3D_3x4, null, true, false);
2044         assertToArray3D(MatrixType.MT_3D_3x4, new double[11], true, false);
2045         assertToArray3D(MatrixType.MT_3D_3x4, new double[12], true, true);
2046 
2047         assertToArray3D(MatrixType.MT_3D_4x4, null, false, false);
2048         assertToArray3D(MatrixType.MT_3D_4x4, null, true, false);
2049         assertToArray3D(MatrixType.MT_3D_4x4, new double[15], true, false);
2050         assertToArray3D(MatrixType.MT_3D_4x4, new double[16], true, true);
2051     }
2052 
2053     @Test(expected=NullPointerException.class)
2054     public void testToArrayNullType1() {
2055         t.toArray(null);
2056     }
2057 
2058     @Test(expected=NullPointerException.class)
2059     public void testToArrayNullType2() {
2060         t.toArray(null, new double[] {});
2061     }
2062 
2063     private void assertRow(MatrixType type, int row, double[] a, Transform t) {
2064         switch (type) {
2065             case MT_2D_3x3:
2066                 if (row == 2) {
2067                     assertEquals(0, a[0], 1e-100);
2068                     assertEquals(0, a[1], 1e-100);
2069                     assertEquals(1, a[2], 1e-100);
2070                     return;
2071                 }
2072                 // fallthrough
2073             case MT_2D_2x3:
2074                 if (row == 0) {
2075                     assertEquals(t.getMxx(), a[0], 1e-100);
2076                     assertEquals(t.getMxy(), a[1], 1e-100);
2077                     assertEquals(t.getTx(),  a[2], 1e-100);
2078                     return;
2079                 } else if (row == 1) {
2080                     assertEquals(t.getMyx(), a[0], 1e-100);
2081                     assertEquals(t.getMyy(), a[1], 1e-100);
2082                     assertEquals(t.getTy(),  a[2], 1e-100);
2083                     return;
2084                 }
2085                 break;
2086             case MT_3D_4x4:
2087                 if (row == 3) {
2088                     assertEquals(0, a[0], 1e-100);
2089                     assertEquals(0, a[1], 1e-100);
2090                     assertEquals(0, a[2], 1e-100);
2091                     assertEquals(1, a[3], 1e-100);
2092                     return;
2093                 }
2094                 // fallthrough
2095             case MT_3D_3x4:
2096                 if (row == 0) {
2097                     assertEquals(t.getMxx(), a[0], 1e-100);
2098                     assertEquals(t.getMxy(), a[1], 1e-100);
2099                     assertEquals(t.getMxz(), a[2], 1e-100);
2100                     assertEquals(t.getTx(),  a[3], 1e-100);
2101                     return;
2102                 } else if (row == 1) {
2103                     assertEquals(t.getMyx(), a[0], 1e-100);
2104                     assertEquals(t.getMyy(), a[1], 1e-100);
2105                     assertEquals(t.getMyz(), a[2], 1e-100);
2106                     assertEquals(t.getTy(),  a[3], 1e-100);
2107                     return;
2108                 } else if (row == 2) {
2109                     assertEquals(t.getMzx(), a[0], 1e-100);
2110                     assertEquals(t.getMzy(), a[1], 1e-100);
2111                     assertEquals(t.getMzz(), a[2], 1e-100);
2112                     assertEquals(t.getTz(),  a[3], 1e-100);
2113                     return;
2114                 }
2115                 break;
2116         }
2117 
2118         fail("Should have thrown IOB");
2119     }
2120 
2121     private void assertRow2D(MatrixType type, int row, double[] tmp,
2122             boolean shouldPass, boolean shouldUse, boolean iob) {
2123         double[] a = null;
2124         try {
2125             if (shouldPass) {
2126                 a = t.row(type, row, tmp);
2127             } else {
2128                 a = t.row(type, row);
2129             }
2130         } catch (IllegalArgumentException e) {
2131             if (is2d) {
2132                 fail("Wrong exception thrown");
2133             }
2134             return;
2135         } catch (IndexOutOfBoundsException e) {
2136             if (!iob) {
2137                 fail("Wrong exception thrown");
2138             }
2139             return;
2140         }
2141         if (!is2d) {
2142             fail("Should have thrown IAE");
2143         } else if (iob) {
2144             fail("Should have thrown IOB");
2145         } else {
2146             assertNotNull(a);
2147             if (shouldUse) {
2148                 assertSame(tmp, a);
2149             }
2150             assertRow(type, row, a, t);
2151         }
2152     }
2153 
2154     private void assertRow3D(MatrixType type, int row, double[] tmp,
2155             boolean shouldPass, boolean shouldUse, boolean iob) {
2156         double[] a = null;
2157         try {
2158             if (shouldPass) {
2159                 a = t.row(type, row, tmp);
2160             } else {
2161                 a = t.row(type, row);
2162             }
2163         } catch (IndexOutOfBoundsException e) {
2164             if (!iob) {
2165                 fail("Wrong exception thrown");
2166             }
2167             return;
2168         }
2169         if (iob) {
2170             fail("Should have thrown IOB");
2171         } else {
2172             assertNotNull(a);
2173             if (shouldUse) {
2174                 assertSame(tmp, a);
2175             }
2176             assertRow(type, row, a, t);
2177         }
2178     }
2179 
2180     @Test
2181     public void testRow() {
2182         assertRow2D(MatrixType.MT_2D_2x3, 0, null, false, false, false);
2183         assertRow2D(MatrixType.MT_2D_2x3, 0, null, true, false, false);
2184         assertRow2D(MatrixType.MT_2D_2x3, 0, new double[2], true, false, false);
2185         assertRow2D(MatrixType.MT_2D_2x3, 0, new double[3], true, true, false);
2186         assertRow2D(MatrixType.MT_2D_2x3, 1, null, false, false, false);
2187         assertRow2D(MatrixType.MT_2D_2x3, 1, null, true, false, false);
2188         assertRow2D(MatrixType.MT_2D_2x3, 1, new double[2], true, false, false);
2189         assertRow2D(MatrixType.MT_2D_2x3, 1, new double[3], true, true, false);
2190         assertRow2D(MatrixType.MT_2D_2x3, -1, null, true, false, true);
2191         assertRow2D(MatrixType.MT_2D_2x3, 2, null, false, false, true);
2192 
2193         assertRow2D(MatrixType.MT_2D_3x3, 0, null, false, false, false);
2194         assertRow2D(MatrixType.MT_2D_3x3, 0, null, true, false, false);
2195         assertRow2D(MatrixType.MT_2D_3x3, 0, new double[2], true, false, false);
2196         assertRow2D(MatrixType.MT_2D_3x3, 0, new double[3], true, true, false);
2197         assertRow2D(MatrixType.MT_2D_3x3, 1, null, false, false, false);
2198         assertRow2D(MatrixType.MT_2D_3x3, 1, null, true, false, false);
2199         assertRow2D(MatrixType.MT_2D_3x3, 1, new double[2], true, false, false);
2200         assertRow2D(MatrixType.MT_2D_3x3, 1, new double[3], true, true, false);
2201         assertRow2D(MatrixType.MT_2D_3x3, 2, null, false, false, false);
2202         assertRow2D(MatrixType.MT_2D_3x3, 2, null, true, false, false);
2203         assertRow2D(MatrixType.MT_2D_3x3, 2, new double[2], true, false, false);
2204         assertRow2D(MatrixType.MT_2D_3x3, 2, new double[3], true, true, false);
2205         assertRow2D(MatrixType.MT_2D_3x3, -1, null, true, false, true);
2206         assertRow2D(MatrixType.MT_2D_3x3, 3, null, false, false, true);
2207 
2208         assertRow3D(MatrixType.MT_3D_3x4, 0, null, false, false, false);
2209         assertRow3D(MatrixType.MT_3D_3x4, 0, null, true, false, false);
2210         assertRow3D(MatrixType.MT_3D_3x4, 0, new double[3], true, false, false);
2211         assertRow3D(MatrixType.MT_3D_3x4, 0, new double[4], true, true, false);
2212         assertRow3D(MatrixType.MT_3D_3x4, 1, null, false, false, false);
2213         assertRow3D(MatrixType.MT_3D_3x4, 1, null, true, false, false);
2214         assertRow3D(MatrixType.MT_3D_3x4, 1, new double[3], true, false, false);
2215         assertRow3D(MatrixType.MT_3D_3x4, 1, new double[4], true, true, false);
2216         assertRow3D(MatrixType.MT_3D_3x4, 2, null, false, false, false);
2217         assertRow3D(MatrixType.MT_3D_3x4, 2, null, true, false, false);
2218         assertRow3D(MatrixType.MT_3D_3x4, 2, new double[3], true, false, false);
2219         assertRow3D(MatrixType.MT_3D_3x4, 2, new double[4], true, true, false);
2220         assertRow3D(MatrixType.MT_3D_3x4, -1, null, true, false, true);
2221         assertRow3D(MatrixType.MT_3D_3x4, 3, null, false, false, true);
2222 
2223         assertRow3D(MatrixType.MT_3D_4x4, 0, null, false, false, false);
2224         assertRow3D(MatrixType.MT_3D_4x4, 0, null, true, false, false);
2225         assertRow3D(MatrixType.MT_3D_4x4, 0, new double[3], true, false, false);
2226         assertRow3D(MatrixType.MT_3D_4x4, 0, new double[4], true, true, false);
2227         assertRow3D(MatrixType.MT_3D_4x4, 1, null, false, false, false);
2228         assertRow3D(MatrixType.MT_3D_4x4, 1, null, true, false, false);
2229         assertRow3D(MatrixType.MT_3D_4x4, 1, new double[3], true, false, false);
2230         assertRow3D(MatrixType.MT_3D_4x4, 1, new double[4], true, true, false);
2231         assertRow3D(MatrixType.MT_3D_4x4, 2, null, false, false, false);
2232         assertRow3D(MatrixType.MT_3D_4x4, 2, null, true, false, false);
2233         assertRow3D(MatrixType.MT_3D_4x4, 2, new double[3], true, false, false);
2234         assertRow3D(MatrixType.MT_3D_4x4, 2, new double[4], true, true, false);
2235         assertRow3D(MatrixType.MT_3D_4x4, 3, null, false, false, false);
2236         assertRow3D(MatrixType.MT_3D_4x4, 3, null, true, false, false);
2237         assertRow3D(MatrixType.MT_3D_4x4, 3, new double[3], true, false, false);
2238         assertRow3D(MatrixType.MT_3D_4x4, 3, new double[4], true, true, false);
2239         assertRow3D(MatrixType.MT_3D_4x4, -1, null, true, false, true);
2240         assertRow3D(MatrixType.MT_3D_4x4, 4, null, false, false, true);
2241     }
2242 
2243     @Test(expected=NullPointerException.class)
2244     public void testRowNullType1() {
2245         t.row(null, 0);
2246     }
2247 
2248     @Test(expected=NullPointerException.class)
2249     public void testRowNullType2() {
2250         t.row(null, 0, new double[] {});
2251     }
2252 
2253     private void assertCol(MatrixType type, int col, double[] a, Transform t) {
2254         switch (type) {
2255             case MT_2D_2x3:
2256                 if (col == 0) {
2257                     assertEquals(t.getMxx(), a[0], 1e-100);
2258                     assertEquals(t.getMyx(), a[1], 1e-100);
2259                     return;
2260                 } else if (col == 1) {
2261                     assertEquals(t.getMxy(), a[0], 1e-100);
2262                     assertEquals(t.getMyy(), a[1], 1e-100);
2263                     return;
2264                 } else if (col == 2) {
2265                     assertEquals(t.getTx(), a[0], 1e-100);
2266                     assertEquals(t.getTy(), a[1], 1e-100);
2267                     return;
2268                 }
2269                 break;
2270             case MT_2D_3x3:
2271                 if (col == 0) {
2272                     assertEquals(t.getMxx(), a[0], 1e-100);
2273                     assertEquals(t.getMyx(), a[1], 1e-100);
2274                     assertEquals(0, a[2], 1e-100);
2275                     return;
2276                 } else if (col == 1) {
2277                     assertEquals(t.getMxy(), a[0], 1e-100);
2278                     assertEquals(t.getMyy(), a[1], 1e-100);
2279                     assertEquals(0, a[2], 1e-100);
2280                     return;
2281                 } else if (col == 2) {
2282                     assertEquals(t.getTx(), a[0], 1e-100);
2283                     assertEquals(t.getTy(), a[1], 1e-100);
2284                     assertEquals(1, a[2], 1e-100);
2285                     return;
2286                 }
2287                 break;
2288             case MT_3D_3x4:
2289                 if (col == 0) {
2290                     assertEquals(t.getMxx(), a[0], 1e-100);
2291                     assertEquals(t.getMyx(), a[1], 1e-100);
2292                     assertEquals(t.getMzx(), a[2], 1e-100);
2293                     return;
2294                 } else if (col == 1) {
2295                     assertEquals(t.getMxy(), a[0], 1e-100);
2296                     assertEquals(t.getMyy(), a[1], 1e-100);
2297                     assertEquals(t.getMzy(), a[2], 1e-100);
2298                     return;
2299                 } else if (col == 2) {
2300                     assertEquals(t.getMxz(), a[0], 1e-100);
2301                     assertEquals(t.getMyz(), a[1], 1e-100);
2302                     assertEquals(t.getMzz(), a[2], 1e-100);
2303                     return;
2304                 } else if (col == 3) {
2305                     assertEquals(t.getTx(), a[0], 1e-100);
2306                     assertEquals(t.getTy(), a[1], 1e-100);
2307                     assertEquals(t.getTz(), a[2], 1e-100);
2308                     return;
2309                 }
2310                 break;
2311             case MT_3D_4x4:
2312                 if (col == 0) {
2313                     assertEquals(t.getMxx(), a[0], 1e-100);
2314                     assertEquals(t.getMyx(), a[1], 1e-100);
2315                     assertEquals(t.getMzx(), a[2], 1e-100);
2316                     assertEquals(0, a[3], 1e-100);
2317                     return;
2318                 } else if (col == 1) {
2319                     assertEquals(t.getMxy(), a[0], 1e-100);
2320                     assertEquals(t.getMyy(), a[1], 1e-100);
2321                     assertEquals(t.getMzy(), a[2], 1e-100);
2322                     assertEquals(0, a[3], 1e-100);
2323                     return;
2324                 } else if (col == 2) {
2325                     assertEquals(t.getMxz(), a[0], 1e-100);
2326                     assertEquals(t.getMyz(), a[1], 1e-100);
2327                     assertEquals(t.getMzz(), a[2], 1e-100);
2328                     assertEquals(0, a[3], 1e-100);
2329                     return;
2330                 } else if (col == 3) {
2331                     assertEquals(t.getTx(), a[0], 1e-100);
2332                     assertEquals(t.getTy(), a[1], 1e-100);
2333                     assertEquals(t.getTz(), a[2], 1e-100);
2334                     assertEquals(1, a[3], 1e-100);
2335                     return;
2336                 }
2337                 break;
2338         }
2339 
2340         fail("Should have thrown IOB");
2341     }
2342 
2343     private void assertCol2D(MatrixType type, int col, double[] tmp,
2344             boolean shouldPass, boolean shouldUse, boolean iob) {
2345         double[] a = null;
2346         try {
2347             if (shouldPass) {
2348                 a = t.column(type, col, tmp);
2349             } else {
2350                 a = t.column(type, col);
2351             }
2352         } catch (IllegalArgumentException e) {
2353             if (is2d) {
2354                 fail("Wrong exception thrown");
2355             }
2356             return;
2357         } catch (IndexOutOfBoundsException e) {
2358             if (!iob) {
2359                 fail("Wrong exception thrown");
2360             }
2361             return;
2362         }
2363         if (!is2d) {
2364             fail("Should have thrown IAE");
2365         } else if (iob) {
2366             fail("Should have thrown IOB");
2367         } else {
2368             assertNotNull(a);
2369             if (shouldUse) {
2370                 assertSame(tmp, a);
2371             }
2372             assertCol(type, col, a, t);
2373         }
2374     }
2375 
2376     private void assertCol3D(MatrixType type, int col, double[] tmp,
2377             boolean shouldPass, boolean shouldUse, boolean iob) {
2378         double[] a = null;
2379         try {
2380             if (shouldPass) {
2381                 a = t.column(type, col, tmp);
2382             } else {
2383                 a = t.column(type, col);
2384             }
2385         } catch (IndexOutOfBoundsException e) {
2386             if (!iob) {
2387                 fail("Wrong exception thrown");
2388             }
2389             return;
2390         }
2391         if (iob) {
2392             fail("Should have thrown IOB");
2393         } else {
2394             assertNotNull(a);
2395             if (shouldUse) {
2396                 assertSame(tmp, a);
2397             }
2398             assertCol(type, col, a, t);
2399         }
2400     }
2401 
2402     @Test
2403     public void testColumn() {
2404         assertCol2D(MatrixType.MT_2D_2x3, 0, null, false, false, false);
2405         assertCol2D(MatrixType.MT_2D_2x3, 0, null, true, false, false);
2406         assertCol2D(MatrixType.MT_2D_2x3, 0, new double[1], true, false, false);
2407         assertCol2D(MatrixType.MT_2D_2x3, 0, new double[2], true, true, false);
2408         assertCol2D(MatrixType.MT_2D_2x3, 1, null, false, false, false);
2409         assertCol2D(MatrixType.MT_2D_2x3, 1, null, true, false, false);
2410         assertCol2D(MatrixType.MT_2D_2x3, 1, new double[1], true, false, false);
2411         assertCol2D(MatrixType.MT_2D_2x3, 1, new double[2], true, true, false);
2412         assertCol2D(MatrixType.MT_2D_2x3, 2, null, false, false, false);
2413         assertCol2D(MatrixType.MT_2D_2x3, 2, null, true, false, false);
2414         assertCol2D(MatrixType.MT_2D_2x3, 2, new double[1], true, false, false);
2415         assertCol2D(MatrixType.MT_2D_2x3, 2, new double[2], true, true, false);
2416         assertCol2D(MatrixType.MT_2D_2x3, -1, null, true, false, true);
2417         assertCol2D(MatrixType.MT_2D_2x3, 3, null, false, false, true);
2418 
2419         assertCol2D(MatrixType.MT_2D_3x3, 0, null, false, false, false);
2420         assertCol2D(MatrixType.MT_2D_3x3, 0, null, true, false, false);
2421         assertCol2D(MatrixType.MT_2D_3x3, 0, new double[2], true, false, false);
2422         assertCol2D(MatrixType.MT_2D_3x3, 0, new double[3], true, true, false);
2423         assertCol2D(MatrixType.MT_2D_3x3, 1, null, false, false, false);
2424         assertCol2D(MatrixType.MT_2D_3x3, 1, null, true, false, false);
2425         assertCol2D(MatrixType.MT_2D_3x3, 1, new double[2], true, false, false);
2426         assertCol2D(MatrixType.MT_2D_3x3, 1, new double[3], true, true, false);
2427         assertCol2D(MatrixType.MT_2D_3x3, 2, null, false, false, false);
2428         assertCol2D(MatrixType.MT_2D_3x3, 2, null, true, false, false);
2429         assertCol2D(MatrixType.MT_2D_3x3, 2, new double[2], true, false, false);
2430         assertCol2D(MatrixType.MT_2D_3x3, 2, new double[3], true, true, false);
2431         assertCol2D(MatrixType.MT_2D_3x3, -1, null, true, false, true);
2432         assertCol2D(MatrixType.MT_2D_3x3, 3, null, false, false, true);
2433 
2434         assertCol3D(MatrixType.MT_3D_3x4, 0, null, false, false, false);
2435         assertCol3D(MatrixType.MT_3D_3x4, 0, null, true, false, false);
2436         assertCol3D(MatrixType.MT_3D_3x4, 0, new double[2], true, false, false);
2437         assertCol3D(MatrixType.MT_3D_3x4, 0, new double[3], true, true, false);
2438         assertCol3D(MatrixType.MT_3D_3x4, 1, null, false, false, false);
2439         assertCol3D(MatrixType.MT_3D_3x4, 1, null, true, false, false);
2440         assertCol3D(MatrixType.MT_3D_3x4, 1, new double[2], true, false, false);
2441         assertCol3D(MatrixType.MT_3D_3x4, 1, new double[3], true, true, false);
2442         assertCol3D(MatrixType.MT_3D_3x4, 2, null, false, false, false);
2443         assertCol3D(MatrixType.MT_3D_3x4, 2, null, true, false, false);
2444         assertCol3D(MatrixType.MT_3D_3x4, 2, new double[2], true, false, false);
2445         assertCol3D(MatrixType.MT_3D_3x4, 2, new double[3], true, true, false);
2446         assertCol3D(MatrixType.MT_3D_3x4, 3, null, false, false, false);
2447         assertCol3D(MatrixType.MT_3D_3x4, 3, null, true, false, false);
2448         assertCol3D(MatrixType.MT_3D_3x4, 3, new double[2], true, false, false);
2449         assertCol3D(MatrixType.MT_3D_3x4, 3, new double[3], true, true, false);
2450         assertCol3D(MatrixType.MT_3D_3x4, -1, null, true, false, true);
2451         assertCol3D(MatrixType.MT_3D_3x4, 4, null, false, false, true);
2452 
2453         assertCol3D(MatrixType.MT_3D_4x4, 0, null, false, false, false);
2454         assertCol3D(MatrixType.MT_3D_4x4, 0, null, true, false, false);
2455         assertCol3D(MatrixType.MT_3D_4x4, 0, new double[3], true, false, false);
2456         assertCol3D(MatrixType.MT_3D_4x4, 0, new double[4], true, true, false);
2457         assertCol3D(MatrixType.MT_3D_4x4, 1, null, false, false, false);
2458         assertCol3D(MatrixType.MT_3D_4x4, 1, null, true, false, false);
2459         assertCol3D(MatrixType.MT_3D_4x4, 1, new double[3], true, false, false);
2460         assertCol3D(MatrixType.MT_3D_4x4, 1, new double[4], true, true, false);
2461         assertCol3D(MatrixType.MT_3D_4x4, 2, null, false, false, false);
2462         assertCol3D(MatrixType.MT_3D_4x4, 2, null, true, false, false);
2463         assertCol3D(MatrixType.MT_3D_4x4, 2, new double[3], true, false, false);
2464         assertCol3D(MatrixType.MT_3D_4x4, 2, new double[4], true, true, false);
2465         assertCol3D(MatrixType.MT_3D_4x4, 3, null, false, false, false);
2466         assertCol3D(MatrixType.MT_3D_4x4, 3, null, true, false, false);
2467         assertCol3D(MatrixType.MT_3D_4x4, 3, new double[3], true, false, false);
2468         assertCol3D(MatrixType.MT_3D_4x4, 3, new double[4], true, true, false);
2469         assertCol3D(MatrixType.MT_3D_4x4, -1, null, true, false, true);
2470         assertCol3D(MatrixType.MT_3D_4x4, 4, null, false, false, true);
2471     }
2472 
2473     @Test(expected=NullPointerException.class)
2474     public void testColumnNullType1() {
2475         t.column(null, 0);
2476     }
2477 
2478     @Test(expected=NullPointerException.class)
2479     public void testColumnNullType2() {
2480         t.column(null, 0, new double[] {});
2481     }
2482 
2483     @Test
2484     public void testSetOnTransformChanged() {
2485         Transform clone = t.clone();
2486 
2487         EventHandler<TransformChangedEvent> ontc =
2488                 event -> {
2489                     eventCounter++;
2490                 };
2491 
2492         assertSame(null, clone.getOnTransformChanged());
2493         clone.setOnTransformChanged(ontc);
2494         assertSame(ontc, clone.getOnTransformChanged());
2495 
2496         eventCounter = 0;
2497         if (TransformHelper.modify(clone, 42)) {
2498             if (t == rotateZeroAxis || t == noRotate) {
2499                 // needs two changes to be further usable
2500                 eventCounter--;
2501             }
2502             assertEquals(1, eventCounter);
2503             TransformHelper.modify(clone, 42);
2504             assertEquals(1, eventCounter);
2505             TransformHelper.modify(clone, 43);
2506             assertEquals(2, eventCounter);
2507 
2508             clone.setOnTransformChanged(null);
2509             assertNull(clone.getOnTransformChanged());
2510             TransformHelper.modify(clone, 44);
2511             assertEquals(2, eventCounter);
2512 
2513         } else {
2514             assertEquals(0, eventCounter);
2515         }
2516     }
2517 
2518     @Test
2519     public void testAddRemoveEventHandler() {
2520         Transform clone = t.clone();
2521 
2522         EventHandler<TransformChangedEvent> counting =
2523                 event -> {
2524                     eventCounter++;
2525                 };
2526 
2527         EventHandler<TransformChangedEvent> checking =
2528                 event -> {
2529                     listenerCalled = true;
2530                 };
2531 
2532         clone.addEventHandler(TransformChangedEvent.TRANSFORM_CHANGED, counting);
2533         clone.addEventHandler(TransformChangedEvent.TRANSFORM_CHANGED, checking);
2534 
2535         eventCounter = 0;
2536         listenerCalled = false;
2537         if (TransformHelper.modify(clone, 42)) {
2538             if (t == rotateZeroAxis || t == noRotate) {
2539                 // needs two changes to be further usable
2540                 eventCounter--;
2541             }
2542             assertEquals(1, eventCounter);
2543             assertTrue(listenerCalled);
2544 
2545             listenerCalled = false;
2546             TransformHelper.modify(clone, 42);
2547             assertEquals(1, eventCounter);
2548             assertFalse(listenerCalled);
2549 
2550             clone.removeEventHandler(TransformChangedEvent.TRANSFORM_CHANGED, checking);
2551 
2552             TransformHelper.modify(clone, 43);
2553             assertEquals(2, eventCounter);
2554             assertFalse(listenerCalled);
2555         } else {
2556             assertEquals(0, eventCounter);
2557         }
2558     }
2559 
2560     @Test
2561     public void testAddRemoveEventFilter() {
2562         Transform clone = t.clone();
2563 
2564         EventHandler<TransformChangedEvent> counting =
2565                 event -> {
2566                     eventCounter++;
2567                     event.consume();
2568                 };
2569 
2570         EventHandler<TransformChangedEvent> checking =
2571                 event -> {
2572                     listenerCalled = true;
2573                 };
2574 
2575         clone.addEventFilter(TransformChangedEvent.TRANSFORM_CHANGED, counting);
2576         clone.addEventHandler(TransformChangedEvent.TRANSFORM_CHANGED, checking);
2577 
2578         eventCounter = 0;
2579         listenerCalled = false;
2580         if (TransformHelper.modify(clone, 42)) {
2581             if (t == rotateZeroAxis || t == noRotate) {
2582                 // needs two changes to be further usable
2583                 eventCounter--;
2584             }
2585             assertEquals(1, eventCounter);
2586             assertFalse(listenerCalled); // consumed by filter
2587 
2588             listenerCalled = false;
2589             TransformHelper.modify(clone, 42);
2590             assertEquals(1, eventCounter);
2591             assertFalse(listenerCalled);
2592 
2593             clone.removeEventFilter(TransformChangedEvent.TRANSFORM_CHANGED, counting);
2594 
2595             TransformHelper.modify(clone, 43);
2596             assertEquals(1, eventCounter);
2597             assertTrue(listenerCalled);
2598         } else {
2599             assertEquals(0, eventCounter);
2600         }
2601     }
2602 }