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.awt.geom.AffineTransform;
  29 //import com.sun.javafx.geom.transform.Affine3D;
  30 import java.util.Collection;
  31 import javafx.beans.Observable;
  32 import org.junit.runners.Parameterized.Parameters;
  33 import org.junit.runners.Parameterized;
  34 import java.util.Arrays;
  35 import javafx.geometry.Point3D;
  36 import org.junit.Test;
  37 import org.junit.runner.RunWith;
  38 import com.sun.javafx.test.TransformHelper;
  39 import javafx.beans.InvalidationListener;
  40 import javafx.event.EventHandler;
  41 import javafx.geometry.Point2D;
  42 import static org.junit.Assert.*;
  43 
  44 @RunWith(Parameterized.class)
  45 public class AffineOperationsTest {
  46 
  47     private static final Affine identity = new Affine();
  48     private static final Affine translate = new Affine(1, 0, 2,
  49                                                        0, 1, 3);
  50     private static final Affine scale = new Affine(4, 0, 0,
  51                                                    0, 5, 0);
  52     private static final Affine sc_tr = new Affine(6, 0, 8,
  53                                                    0, 7, 9);
  54     private static final Affine shear = new Affine( 0, 10, 0,
  55                                                    11,  0, 0);
  56     private static final Affine sh_tr = new Affine( 0, 12, 14,
  57                                                    13,  0, 15);
  58     private static final Affine sh_sc_simple = new Affine( 1, 18, 0,
  59                                                           19, 1, 0);
  60     private static final Affine sh_sc = new Affine(16, 18, 0,
  61                                                    19, 17, 0);
  62     private static final Affine sh_sc_tr = new Affine(20, 21, 22,
  63                                                       23, 24, 25);
  64     private static final Affine a3d_tr = new Affine(1, 0, 0, 0,
  65                                                     0, 1, 0, 0,
  66                                                     0, 0, 1, 30);
  67     private static final Affine a3d_sc = new Affine(1, 0, 0, 0,
  68                                                     0, 1, 0, 0,
  69                                                     0, 0, 3, 0);
  70     private static final Affine a3d_sc_tr = new Affine(1, 0, 0, 0,
  71                                                        0, 1, 0, 0,
  72                                                        0, 0, 3, 30);
  73     private static final Affine a3d_sc2_tr3 = new Affine(1, 0, 0, 0,
  74                                                          0, 3, 0, 0,
  75                                                          0, 0, 1, 30);
  76     private static final Affine a3d_sc3_tr2 = new Affine(1, 0, 0, 25,
  77                                                          0, 1, 0, 0,
  78                                                          0, 0, 3, 0);
  79     private static final Affine a3d_withShear = new Affine(1, 5, 0, 0,
  80                                                            0, 1, 0, 0,
  81                                                            0, 0, 3, 30);
  82     private static final Affine a3d_only3d = new Affine( 1,  0, 20, 0,
  83                                                          0,  1, 30, 0,
  84                                                         11, 12, 13, 0);
  85     private static final Affine a3d_translate_only = new Affine(0, 0, 0, 10,
  86                                                                 0, 0, 0, 20,
  87                                                                 0, 0, 0, 30);
  88     private static final Affine a3d_complex = new Affine( 2,  3,  4,  5,
  89                                                           6,  7,  8,  9,
  90                                                          10, 11, 12, 13);
  91     private static final Affine a3d_complex_noninvertible =
  92                                                      new Affine( 2,  3,  4,  5,
  93                                                                  6,  7,  8,  9,
  94                                                                 10, 11, 12, 13);
  95     private static final Affine shearRotatesToIdentity1 = new Affine(0, -1, 0,
  96                                                                      1,  0, 0);
  97     private static final Affine shearRotatesToIdentity2 = new Affine(0, 1, 0,
  98                                                                     -1,  0, 0);
  99     private static final Affine scaleRotatesToIdentity = new Affine(-1,  0, 0,
 100                                                                      0, -1, 0);
 101     private static final Affine scr_tr_rotatesToTr = new Affine(-1,  0, 0, 0,
 102                                                                  0, -1, 0, 0,
 103                                                                  0,  0, 1, 12);
 104     private static final Affine translate_only = new Affine(0, 0, 2,
 105                                                             0, 0, 3);
 106     private static final Affine nonInv_translate = new Affine(0, 0, 2,
 107                                                               0, 0, 0);
 108     private static final Affine nonInv_scale = new Affine(0, 0, 0,
 109                                                           0, 2, 0);
 110     private static final Affine nonInv_shear = new Affine(0, 3, 0,
 111                                                           0, 0, 0);
 112     private static final Affine nonInv_sh_sc_tr = new Affine(0, 0, 0,
 113                                                              2, 3, 4);
 114     private static final Affine nonInv_sh_sc = new Affine(0, 0, 0,
 115                                                           2, 3, 0);
 116     private static final Affine nonInv_sh_tr = new Affine(0, 2, 0,
 117                                                           0, 0, 5);
 118     private static final Affine nonInv_sc_tr = new Affine(0, 0, 0,
 119                                                           0, 6, 5);
 120     private static final Affine zero = new Affine(0, 0, 0,
 121                                                   0, 0, 0);
 122 
 123 
 124     private static final double[] array2d = new double[] {
 125          0, 1,
 126          2,  3,  4,
 127          6,  7,  8,
 128          0,  0,  1 };
 129 
 130     private static final double[] array3d = new double[] {
 131          0, 1,
 132          2,  3,  4,  5,
 133          6,  7,  8,  9,
 134         10, 11, 12, 13,
 135          0,  0,  0,  1 };
 136 
 137     private static final double[] arrayZeros = new double[] {
 138          0, 0, 0, 0,
 139          0, 0, 0, 0,
 140          0, 0, 0, 0,
 141          0, 0, 0, 0, 1, 0, 0, 0, 0 };
 142 
 143     @Parameters
 144     public static Collection getParams() {
 145         return Arrays.asList(new Object[][] {
 146             { identity },                   //  0
 147             { translate },                  //  1
 148             { scale },                      //  2
 149             { sc_tr },                      //  3
 150             { shear },                      //  4
 151             { sh_tr },                      //  5
 152             { sh_sc },                      //  6
 153             { sh_sc_simple },               //  7
 154             { sh_sc_tr },                   //  8
 155             { a3d_tr },                     //  9
 156             { a3d_sc },                     // 10
 157             { a3d_sc_tr },                  // 11
 158             { a3d_sc2_tr3 },                // 12
 159             { a3d_sc3_tr2 },                // 13
 160             { a3d_withShear },              // 14
 161             { a3d_only3d },                 // 15
 162             { a3d_translate_only },         // 16
 163             { a3d_complex },                // 17
 164             { a3d_complex_noninvertible },  // 18
 165             { shearRotatesToIdentity1 },    // 19
 166             { shearRotatesToIdentity2 },    // 20
 167             { scaleRotatesToIdentity },     // 21
 168             { scr_tr_rotatesToTr },         // 22
 169             { translate_only },             // 23
 170             { nonInv_translate },           // 24
 171             { nonInv_scale },               // 25
 172             { nonInv_shear },               // 26
 173             { nonInv_sh_sc_tr },            // 27
 174             { nonInv_sh_sc },               // 28
 175             { nonInv_sh_tr },               // 29
 176             { nonInv_sc_tr },               // 30
 177             { zero },                       // 31
 178         });
 179     }
 180 
 181     private Affine affine;
 182 
 183     public AffineOperationsTest(Affine a) {
 184         this.affine = a;
 185     }
 186 
 187     private int eventCounter;
 188     private int listener1Counter;
 189     private int listener2Counter;
 190     private double memMyx, memTy;
 191 
 192     private void assertAffineOk(Transform expected, Affine a) {
 193         TransformHelper.assertMatrix(a, expected);
 194         assertStateOk(a);
 195     }
 196 
 197     private void assertAffineOk(String message, Transform expected, Affine a) {
 198         TransformHelper.assertMatrix(message, a, expected);
 199         assertStateOk(message, a);
 200     }
 201 
 202     private void assertStateOk(Affine a) {
 203         TransformHelper.assertStateOk(a, a.getState3d(), a.getState2d());
 204     }
 205 
 206     private void assertStateOk(String message, Affine a) {
 207         TransformHelper.assertStateOk(message, a, a.getState3d(), a.getState2d());
 208     }
 209 
 210     private void testOperationIsAtomic(Affine a,
 211             final Runnable op, final Runnable check) {
 212 
 213         a.setOnTransformChanged(event -> {
 214             eventCounter++;
 215             check.run();
 216         });
 217 
 218         a.myxProperty().addListener(observable -> {
 219             listener1Counter++;
 220             check.run();
 221         });
 222 
 223         a.tyProperty().addListener(observable -> {
 224             listener2Counter++;
 225             check.run();
 226         });
 227 
 228         memMyx = a.getMyx();
 229         memTy = a.getTy();
 230         eventCounter = 0;
 231         listener1Counter = 0;
 232         listener2Counter = 0;
 233         op.run();
 234 
 235         assertTrue(listener1Counter == (memMyx == a.getMyx() ? 0 : 1));
 236         assertTrue(listener2Counter == (memTy == a.getTy() ? 0 : 1));
 237         assertEquals(1, eventCounter);
 238         assertFalse(a.atomicChangeRuns());
 239     }
 240 
 241     @Test
 242     public void SetToTransformShouldBeAtomic() {
 243         final Affine a = affine.clone();
 244         final Shear sh = new Shear(12, 15);
 245 
 246         testOperationIsAtomic(a,
 247                 () -> a.setToTransform(sh),
 248                 () -> assertAffineOk(sh, a)
 249         );
 250     }
 251 
 252     @Test
 253     public void SetToTransform2DShouldBeAtomic() {
 254         final Affine a = affine.clone();
 255         final Shear sh = new Shear(12, 15);
 256 
 257         testOperationIsAtomic(a,
 258                 () -> a.setToTransform(1, 12, 0, 15, 1, 0),
 259                 () -> assertAffineOk(sh, a)
 260         );
 261     }
 262 
 263     @Test
 264     public void testSetToIdentity() {
 265         final Affine a = affine.clone();
 266         a.setToIdentity();
 267         TransformHelper.assertMatrix(a, new Affine());
 268     }
 269 
 270     @Test
 271     public void SetToIdentityShouldBeAtomic() {
 272         final Affine a = affine.clone();
 273 
 274         testOperationIsAtomic(a,
 275                 () -> a.setToIdentity(),
 276                 () -> assertAffineOk(new Affine(), a)
 277         );
 278     }
 279 
 280     @Test
 281     public void testAppendTranslation2D() {
 282         Affine a = affine.clone();
 283         assertStateOk(a);
 284 
 285         Transform res = TransformHelper.concatenate(a, new Translate(8, 9));
 286         a.appendTranslation(8, 9);
 287 
 288         assertAffineOk(res, a);
 289 
 290         a = affine.clone();
 291         a.append(new Translate(8, 9));
 292         assertAffineOk(res, a);
 293     }
 294 
 295     @Test
 296     public void testAppendZeroTranslation2D() {
 297         Affine a = affine.clone();
 298         assertStateOk(a);
 299 
 300         Transform res = TransformHelper.concatenate(a, new Translate(0, 0));
 301         a.appendTranslation(0, 0);
 302 
 303         assertAffineOk(res, a);
 304 
 305         a = affine.clone();
 306         a.append(new Translate(0, 0));
 307         assertAffineOk(res, a);
 308     }
 309 
 310     @Test
 311     public void testAppendTranslation2DWhichEliminatesTranslation() {
 312         Affine a = affine.clone();
 313         assertStateOk(a);
 314 
 315         if (!a.isType2D()) {
 316             // not interested
 317             return;
 318         }
 319 
 320         final double ty = (a.getTx() * a.getMyx() / a.getMxx() - a.getTy())
 321                 / (a.getMyy() - a.getMyx() * a.getMxy() / a.getMxx());
 322         final double tx = (- ty * a.getMxy() - a.getTx()) / a.getMxx();
 323 
 324         if (Double.isNaN(tx) || Double.isNaN(ty)) {
 325             // impossible to eliminate translation
 326             return;
 327         }
 328 
 329         Transform res = TransformHelper.concatenate(a, new Translate(tx, ty));
 330         a.appendTranslation(tx, ty);
 331 
 332         assertEquals(0, a.getTx(), 1e-10);
 333         assertEquals(0, a.getTy(), 1e-10);
 334         assertAffineOk(res, a);
 335 
 336         a = affine.clone();
 337         a.append(new Translate(tx, ty));
 338         assertAffineOk(res, a);
 339     }
 340 
 341     @Test
 342     public void testAppendTranslation3DWhichMakesTranslation2D() {
 343         Affine a = affine.clone();
 344         assertStateOk(a);
 345 
 346         if (a.getMzz() == 0.0) {
 347             return;
 348         }
 349 
 350         final double tz = - a.getTz() / a.getMzz();
 351 
 352         Transform res = TransformHelper.concatenate(a, new Translate(0, 0, tz));
 353         a.appendTranslation(0, 0, tz);
 354 
 355         assertEquals(0, a.getTz(), 1e-10);
 356         assertAffineOk(res, a);
 357 
 358         a = affine.clone();
 359         a.append(new Translate(0, 0, tz));
 360         assertAffineOk(res, a);
 361     }
 362 
 363     @Test
 364     public void appendTranslation2DShouldBeAtomic() {
 365         final Affine a = affine.clone();
 366         final Transform res = TransformHelper.concatenate(a, new Translate(8, 9));
 367 
 368         testOperationIsAtomic(a, () -> a.appendTranslation(8, 9), () -> assertAffineOk(res, a));
 369     }
 370 
 371     @Test
 372     public void testPrependTranslation2D() {
 373         Affine a = affine.clone();
 374         assertStateOk(a);
 375 
 376         Transform res = TransformHelper.concatenate(new Translate(8, 9), a);
 377         a.prependTranslation(8, 9);
 378 
 379         assertAffineOk(res, a);
 380     }
 381 
 382     @Test
 383     public void testPrependZeroTranslation() {
 384         Affine a = affine.clone();
 385         assertStateOk(a);
 386 
 387         Transform res = TransformHelper.concatenate(new Translate(0, 0), a);
 388         a.prependTranslation(0, 0);
 389 
 390         assertAffineOk(res, a);
 391     }
 392 
 393     @Test
 394     public void prependTranslation2DShouldBeAtomic() {
 395         final Affine a = affine.clone();
 396         final Transform res = TransformHelper.concatenate(new Translate(8, 9), a);
 397 
 398         testOperationIsAtomic(a, () -> a.prependTranslation(8, 9), () -> assertAffineOk(res, a));
 399     }
 400 
 401     @Test
 402     public void testAppendTranslation3D() {
 403         Affine a = affine.clone();
 404         assertStateOk(a);
 405 
 406         Transform res = TransformHelper.concatenate(a, new Translate(8, 9, 10));
 407         a.appendTranslation(8, 9, 10);
 408 
 409         assertAffineOk(res, a);
 410     }
 411 
 412     @Test
 413     public void appendTranslation3DShouldBeAtomic() {
 414         final Affine a = affine.clone();
 415         final Transform res = TransformHelper.concatenate(a, new Translate(8, 9, 10));
 416 
 417         testOperationIsAtomic(a, () -> a.appendTranslation(8, 9, 10), () -> assertAffineOk(res, a));
 418     }
 419 
 420     @Test
 421     public void testPrependTranslation3D() {
 422         Affine a = affine.clone();
 423         assertStateOk(a);
 424 
 425         Transform res = TransformHelper.concatenate(new Translate(8, 9, 10), a);
 426         a.prependTranslation(8, 9, 10);
 427 
 428         assertAffineOk(res, a);
 429     }
 430 
 431     @Test
 432     public void testPrependTranslation3DWhichMakesIt2D() {
 433         Affine a = affine.clone();
 434         assertStateOk(a);
 435 
 436         Transform res = TransformHelper.concatenate(new Translate(3, 5, -a.getTz()), a);
 437         a.prependTranslation(3, 5, -a.getTz());
 438 
 439         assertAffineOk(res, a);
 440     }
 441 
 442     @Test
 443     public void prependTranslation3DShouldBeAtomic() {
 444         final Affine a = affine.clone();
 445         final Transform res = TransformHelper.concatenate(new Translate(8, 9, 10), a);
 446 
 447         testOperationIsAtomic(a, () -> a.prependTranslation(8, 9, 10), () -> assertAffineOk(res, a));
 448     }
 449 
 450     @Test
 451     public void testAppendScale2D() {
 452         Affine a = affine.clone();
 453         assertStateOk(a);
 454 
 455         Transform res = TransformHelper.concatenate(a, new Scale(8, 9));
 456         a.appendScale(8, 9);
 457 
 458         assertAffineOk(res, a);
 459     }
 460 
 461     @Test(expected=NullPointerException.class)
 462     public void testAppendNullPivotedScale2D() {
 463         Affine a = affine.clone();
 464         a.appendScale(8, 9, null);
 465     }
 466 
 467     @Test
 468     public void testAppendZeroPivotScale2D() {
 469         Affine a = affine.clone();
 470         assertStateOk(a);
 471 
 472         Transform res = TransformHelper.concatenate(a, new Scale(8, 9));
 473         a.appendScale(8, 9, 0, 0);
 474         assertAffineOk(res, a);
 475 
 476         a = affine.clone();
 477         res = TransformHelper.concatenate(a, new Scale(8, 9, 0, 120));
 478         a.appendScale(8, 9, 0, 120);
 479         assertAffineOk(res, a);
 480 
 481         a = affine.clone();
 482         res = TransformHelper.concatenate(a, new Scale(8, 9, 150, 0));
 483         a.appendScale(8, 9, 150, 0);
 484         assertAffineOk(res, a);
 485     }
 486 
 487     @Test
 488     public void testAppendZeroScale2D() {
 489         Affine a = affine.clone();
 490         assertStateOk(a);
 491 
 492         Transform res = TransformHelper.concatenate(a, new Scale(0, 0));
 493         a.appendScale(0, 0);
 494         assertAffineOk(res, a);
 495 
 496         a = affine.clone();
 497         res = TransformHelper.concatenate(a, new Scale(0, 3));
 498         a.appendScale(0, 3);
 499         assertAffineOk(res, a);
 500 
 501         a = affine.clone();
 502         res = TransformHelper.concatenate(a, new Scale(3, 0));
 503         a.appendScale(3, 0);
 504         assertAffineOk(res, a);
 505     }
 506 
 507     @Test
 508     public void testAppendNoScale2D() {
 509         Affine a = affine.clone();
 510         assertStateOk(a);
 511 
 512         Transform res = TransformHelper.concatenate(a, new Scale(1, 1));
 513         a.appendScale(1, 1);
 514 
 515         assertAffineOk(res, a);
 516     }
 517 
 518     @Test
 519     public void testAppendScale2DWhichMayEliminateScale() {
 520         Affine a = affine.clone();
 521 
 522         if (a.getMxx() == 0 || a.getMyy() == 0) {
 523             // doesn't make sense for this one
 524             return;
 525         }
 526 
 527         final double sx = 1.0 / a.getMxx();
 528         final double sy = 1.0 / a.getMyy();
 529 
 530         Transform res = TransformHelper.concatenate(a, new Scale(sx, sy));
 531         a.appendScale(sx, sy);
 532 
 533         assertAffineOk(res, a);
 534     }
 535 
 536     @Test
 537     public void testAppendScale3DWhichMakesIt2D() {
 538         Affine a = affine.clone();
 539 
 540         if (a.getMzz() == 0.0) {
 541             // doesn't make sense for this one
 542             return;
 543         }
 544 
 545         final double sz = 1.0 / a.getMzz();
 546 
 547         Transform res = TransformHelper.concatenate(a, new Scale(2, 3, sz));
 548         a.appendScale(2, 3, sz);
 549 
 550         assertAffineOk(res, a);
 551     }
 552 
 553     @Test
 554     public void appendScale2DShouldBeAtomic() {
 555         final Affine a = affine.clone();
 556         final Transform res = TransformHelper.concatenate(a, new Scale(8, 9));
 557 
 558         testOperationIsAtomic(a, () -> a.appendScale(8, 9), () -> assertAffineOk(res, a));
 559     }
 560 
 561     @Test
 562     public void testPrependScale2D() {
 563         Affine a = affine.clone();
 564         assertStateOk(a);
 565 
 566         Transform res = TransformHelper.concatenate(new Scale(8, 9), a);
 567         a.prependScale(8, 9);
 568 
 569         assertAffineOk(res, a);
 570     }
 571 
 572     @Test(expected=NullPointerException.class)
 573     public void testPrependNullPivotedScale2D() {
 574         Affine a = affine.clone();
 575         a.appendScale(8, 9, null);
 576     }
 577 
 578     @Test
 579     public void testPrependZeroPivotScale2D() {
 580         Affine a = affine.clone();
 581         assertStateOk(a);
 582 
 583         Transform res = TransformHelper.concatenate(new Scale(8, 9), a);
 584         a.prependScale(8, 9, 0, 0);
 585         assertAffineOk(res, a);
 586 
 587         a = affine.clone();
 588         res = TransformHelper.concatenate(new Scale(8, 9, 0, 120), a);
 589         a.prependScale(8, 9, 0, 120);
 590         assertAffineOk(res, a);
 591 
 592         a = affine.clone();
 593         res = TransformHelper.concatenate(new Scale(8, 9, 150, 0), a);
 594         a.prependScale(8, 9, 150, 0);
 595         assertAffineOk(res, a);
 596     }
 597 
 598     @Test
 599     public void testPrependZeroScale2D() {
 600         Affine a = affine.clone();
 601         assertStateOk(a);
 602 
 603         Transform res = TransformHelper.concatenate(new Scale(0, 0), a);
 604         a.prependScale(0, 0);
 605         assertAffineOk(res, a);
 606 
 607         a = affine.clone();
 608         res = TransformHelper.concatenate(new Scale(0, 3), a);
 609         a.prependScale(0, 3);
 610         assertAffineOk(res, a);
 611 
 612         a = affine.clone();
 613         res = TransformHelper.concatenate(new Scale(3, 0), a);
 614         a.prependScale(3, 0);
 615         assertAffineOk(res, a);
 616     }
 617 
 618     @Test
 619     public void testPrependHalfZeroScale2D() {
 620         Affine a = affine.clone();
 621         assertStateOk(a);
 622 
 623         Transform res = TransformHelper.concatenate(new Scale(4, 0), a);
 624         a.prependScale(4, 0);
 625 
 626         assertAffineOk(res, a);
 627     }
 628 
 629     @Test
 630     public void testPrependOtherHalfZeroScale2D() {
 631         Affine a = affine.clone();
 632         assertStateOk(a);
 633 
 634         Transform res = TransformHelper.concatenate(new Scale(0, 5), a);
 635         a.prependScale(0, 5);
 636 
 637         assertAffineOk(res, a);
 638     }
 639 
 640     @Test
 641     public void testPrependNoScale2D() {
 642         Affine a = affine.clone();
 643         assertStateOk(a);
 644 
 645         Transform res = TransformHelper.concatenate(new Scale(1, 1), a);
 646         a.prependScale(1, 1);
 647 
 648         assertAffineOk(res, a);
 649     }
 650 
 651     @Test
 652     public void testPrependOppositeScale2D() {
 653         Affine a = affine.clone();
 654 
 655         final double sx = a.getMxx() == 0 ? 0 : 1 / a.getMxx();
 656         final double sy = a.getMyy() == 0 ? 0 : 1 / a.getMyy();
 657 
 658         Transform res = TransformHelper.concatenate(new Scale(sx, sy), a);
 659         a.prependScale(sx, sy);
 660         assertAffineOk(res, a);
 661 
 662         a = affine.clone();
 663         res = TransformHelper.concatenate(new Scale(1.0, sy), a);
 664         a.prependScale(1.0, sy);
 665         assertAffineOk(res, a);
 666 
 667         a = affine.clone();
 668         res = TransformHelper.concatenate(new Scale(sx, 1.0), a);
 669         a.prependScale(sx, 1.0);
 670         assertAffineOk(res, a);
 671     }
 672 
 673     @Test
 674     public void testPrependOppositeScale3D() {
 675         Affine a = affine.clone();
 676         assertStateOk(a);
 677 
 678         final double sx = a.getMxx() == 0 ? 0 : 1 / a.getMxx();
 679         final double sy = a.getMyy() == 0 ? 0 : 1 / a.getMyy();
 680         final double sz = a.getMzz() == 0 ? 0 : 1 / a.getMzz();
 681 
 682         Transform res = TransformHelper.concatenate(new Scale(sx, sy, sz), a);
 683         a.prependScale(sx, sy, sz);
 684         assertAffineOk(res, a);
 685 
 686         a = affine.clone();
 687         res = TransformHelper.concatenate(new Scale(1.0, 1.0, sz), a);
 688         a.prependScale(1.0, 1.0, sz);
 689         assertAffineOk(res, a);
 690     }
 691 
 692     @Test
 693     public void prependScale2DShouldBeAtomic() {
 694         final Affine a = affine.clone();
 695         final Transform res = TransformHelper.concatenate(new Scale(8, 9), a);
 696 
 697         testOperationIsAtomic(a, () -> a.prependScale(8, 9), () -> assertAffineOk(res, a));
 698     }
 699 
 700     @Test
 701     public void testAppendPivotedScale2D() {
 702         Affine a = affine.clone();
 703         assertStateOk(a);
 704 
 705         Transform res = TransformHelper.concatenate(a, new Scale(8, 9, 10, 11));
 706         a.appendScale(8, 9, 10, 11);
 707 
 708         assertAffineOk(res, a);
 709     }
 710 
 711     @Test
 712     public void testAppendPointPivotedScale2D() {
 713         Affine a = affine.clone();
 714         assertStateOk(a);
 715 
 716         Transform res = TransformHelper.concatenate(a, new Scale(8, 9, 10, 11));
 717         a.appendScale(8, 9, new Point2D(10, 11));
 718 
 719         assertAffineOk(res, a);
 720     }
 721 
 722     @Test
 723     public void appendPivotedScale2DShouldBeAtomic() {
 724         final Affine a = affine.clone();
 725         final Transform res = TransformHelper.concatenate(a, new Scale(8, 9, 10, 11));
 726 
 727         testOperationIsAtomic(a, () -> a.appendScale(8, 9, 10, 11), () -> assertAffineOk(res, a));
 728     }
 729 
 730     @Test
 731     public void testPrependPivotedScale2D() {
 732         Affine a = affine.clone();
 733         assertStateOk(a);
 734 
 735         Transform res = TransformHelper.concatenate(new Scale(8, 9, 10, 11), a);
 736         a.prependScale(8, 9, 10, 11);
 737 
 738         assertAffineOk(res, a);
 739     }
 740 
 741     @Test
 742     public void testPrependPointPivotedScale2D() {
 743         Affine a = affine.clone();
 744         assertStateOk(a);
 745 
 746         Transform res = TransformHelper.concatenate(new Scale(8, 9, 10, 11), a);
 747         a.prependScale(8, 9, new Point2D(10, 11));
 748 
 749         assertAffineOk(res, a);
 750     }
 751 
 752     @Test
 753     public void prependPivotedScale2DShouldBeAtomic() {
 754         final Affine a = affine.clone();
 755         final Transform res = TransformHelper.concatenate(new Scale(8, 9, 10, 11), a);
 756 
 757         testOperationIsAtomic(a, () -> a.prependScale(8, 9, 10, 11), () -> assertAffineOk(res, a));
 758     }
 759 
 760     @Test
 761     public void testAppendScale3D() {
 762         Affine a = affine.clone();
 763         assertStateOk(a);
 764 
 765         Transform res = TransformHelper.concatenate(a, new Scale(8, 9, 10));
 766         a.appendScale(8, 9, 10);
 767 
 768         assertAffineOk(res, a);
 769     }
 770 
 771     @Test(expected=NullPointerException.class)
 772     public void testAppendNullPivotedScale3D() {
 773         Affine a = affine.clone();
 774         a.appendScale(8, 9, 10, null);
 775     }
 776 
 777     @Test
 778     public void testAppendOppositeScale2D() {
 779         Affine a = affine.clone();
 780 
 781         final double sx = a.getMxx() == 0 ? 0 : 1 / a.getMxx();
 782         final double sy = a.getMyy() == 0 ? 0 : 1 / a.getMyy();
 783 
 784         Transform res = TransformHelper.concatenate(a, new Scale(sx, sy));
 785         a.appendScale(sx, sy);
 786         assertAffineOk(res, a);
 787 
 788         a = affine.clone();
 789         res = TransformHelper.concatenate(a, new Scale(1.0, sy));
 790         a.appendScale(1.0, sy);
 791         assertAffineOk(res, a);
 792 
 793         a = affine.clone();
 794         res = TransformHelper.concatenate(a, new Scale(sx, 1.0));
 795         a.appendScale(sx, 1.0);
 796         assertAffineOk(res, a);
 797     }
 798 
 799     @Test
 800     public void testAppendOppositeScale3D() {
 801         Affine a = affine.clone();
 802         assertStateOk(a);
 803 
 804         if (a.getMxx() == 0.0 || a.getMyy() == 0.0 || a.getMzz() == 0.0) {
 805             // doesn't make sense
 806             return;
 807         }
 808         
 809         final double sx = 1.0 / a.getMxx();
 810         final double sy = 1.0 / a.getMyy();
 811         final double sz = 1.0 / a.getMzz();
 812 
 813         Transform res = TransformHelper.concatenate(a, new Scale(sx, sy, sz));
 814         a.appendScale(sx, sy, sz);
 815         assertAffineOk(res, a);
 816 
 817         a = affine.clone();
 818         res = TransformHelper.concatenate(new Scale(1.0, 1.0, sz), a);
 819         a.prependScale(1.0, 1.0, sz);
 820         assertAffineOk(res, a);
 821 
 822     }
 823 
 824     @Test
 825     public void appendScale3DShouldBeAtomic() {
 826         final Affine a = affine.clone();
 827         final Transform res = TransformHelper.concatenate(a, new Scale(8, 9, 10));
 828 
 829         testOperationIsAtomic(a, () -> a.appendScale(8, 9, 10), () -> assertAffineOk(res, a));
 830     }
 831 
 832     @Test
 833     public void testPrependScale3D() {
 834         Affine a = affine.clone();
 835         assertStateOk(a);
 836 
 837         Transform res = TransformHelper.concatenate(new Scale(8, 9, 10), a);
 838         a.prependScale(8, 9, 10);
 839 
 840         assertAffineOk(res, a);
 841     }
 842 
 843     @Test(expected=NullPointerException.class)
 844     public void testPrependNullPivotedScale3D() {
 845         Affine a = affine.clone();
 846         a.prependScale(8, 9, 10, null);
 847     }
 848 
 849     @Test
 850     public void testPrependScale3DWichMakesIt2D() {
 851         Affine a = affine.clone();
 852         assertStateOk(a);
 853 
 854         if (a.getMzz() == 1.0 || a.getMzz() == 0.0) {
 855             // dosen't make sense for this one
 856             return;
 857         }
 858 
 859         final double sz = 1.0 / a.getMzz();
 860 
 861         Transform res = TransformHelper.concatenate(new Scale(2, 3, sz), a);
 862         a.prependScale(2, 3, sz);
 863 
 864         assertAffineOk(res, a);
 865     }
 866 
 867     @Test
 868     public void prependScale3DShouldBeAtomic() {
 869         final Affine a = affine.clone();
 870         final Transform res = TransformHelper.concatenate(new Scale(8, 9, 10), a);
 871 
 872         testOperationIsAtomic(a, () -> a.prependScale(8, 9, 10), () -> assertAffineOk(res, a));
 873     }
 874 
 875     @Test
 876     public void testAppendPivotedScale3D() {
 877         Affine a = affine.clone();
 878         assertStateOk(a);
 879 
 880         Transform res = TransformHelper.concatenate(a, new Scale(8, 9, 10, 11, 12, 13));
 881         a.appendScale(8, 9, 10, 11, 12, 13);
 882 
 883         assertAffineOk(res, a);
 884     }
 885 
 886     @Test
 887     public void testAppendPointPivotedScale3D() {
 888         Affine a = affine.clone();
 889         assertStateOk(a);
 890 
 891         Transform res = TransformHelper.concatenate(a, new Scale(8, 9, 10, 11, 12, 13));
 892         a.appendScale(8, 9, 10, new Point3D(11, 12, 13));
 893 
 894         assertAffineOk(res, a);
 895     }
 896 
 897     @Test
 898     public void appendPivotedScale3DShouldBeAtomic() {
 899         final Affine a = affine.clone();
 900         final Transform res = TransformHelper.concatenate(a, new Scale(8, 9, 10, 11, 12, 13));
 901 
 902         testOperationIsAtomic(a, () -> a.appendScale(8, 9, 10, 11, 12, 13), () -> assertAffineOk(res, a));
 903     }
 904 
 905     @Test
 906     public void testPrependPivotedScale3D() {
 907         Affine a = affine.clone();
 908         assertStateOk(a);
 909 
 910         Transform res = TransformHelper.concatenate(new Scale(8, 9, 10, 11, 12, 13), a);
 911         a.prependScale(8, 9, 10, 11, 12, 13);
 912 
 913         assertAffineOk(res, a);
 914     }
 915 
 916     @Test
 917     public void testPrependPointPivotedScale3D() {
 918         Affine a = affine.clone();
 919         assertStateOk(a);
 920 
 921         Transform res = TransformHelper.concatenate(new Scale(8, 9, 10, 11, 12, 13), a);
 922         a.prependScale(8, 9, 10, new Point3D(11, 12, 13));
 923 
 924         assertAffineOk(res, a);
 925     }
 926 
 927     @Test
 928     public void prependPivotedScale3DShouldBeAtomic() {
 929         final Affine a = affine.clone();
 930         final Transform res = TransformHelper.concatenate(new Scale(8, 9, 10, 11, 12, 13), a);
 931 
 932         testOperationIsAtomic(a, () -> a.prependScale(8, 9, 10, 11, 12, 13), () -> assertAffineOk(res, a));
 933     }
 934 
 935     @Test
 936     public void testAppendShear2D() {
 937         Affine a = affine.clone();
 938         assertStateOk(a);
 939 
 940         Transform res = TransformHelper.concatenate(a, new Shear(8, 9));
 941         a.appendShear(8, 9);
 942 
 943         assertAffineOk(res, a);
 944     }
 945 
 946     @Test(expected=NullPointerException.class)
 947     public void testAppendNullPivotedShear2D() {
 948         Affine a = affine.clone();
 949         a.appendShear(8, 9, null);
 950     }
 951 
 952     @Test(expected=NullPointerException.class)
 953     public void testPrependNullPivotedShear2D() {
 954         Affine a = affine.clone();
 955         a.prependShear(8, 9, null);
 956     }
 957 
 958     @Test
 959     public void testAppendZeroPivotedShear2D() {
 960         Affine a = affine.clone();
 961         assertStateOk(a);
 962 
 963         Transform res = TransformHelper.concatenate(a, new Shear(8, 9));
 964         a.appendShear(8, 9, 0, 0);
 965         assertAffineOk(res, a);
 966 
 967         a = affine.clone();
 968         res = TransformHelper.concatenate(a, new Shear(8, 9, 0, 120));
 969         a.appendShear(8, 9, 0, 120);
 970         assertAffineOk(res, a);
 971 
 972         a = affine.clone();
 973         res = TransformHelper.concatenate(a, new Shear(8, 9, 150, 0));
 974         a.appendShear(8, 9, 150, 0);
 975         assertAffineOk(res, a);
 976     }
 977 
 978     @Test
 979     public void testPrependZeroPivotedShear2D() {
 980         Affine a = affine.clone();
 981         assertStateOk(a);
 982 
 983         Transform res = TransformHelper.concatenate(new Shear(8, 9), a);
 984         a.prependShear(8, 9, 0, 0);
 985         assertAffineOk(res, a);
 986 
 987         a = affine.clone();
 988         res = TransformHelper.concatenate(new Shear(8, 9, 0, 120), a);
 989         a.prependShear(8, 9, 0, 120);
 990         assertAffineOk(res, a);
 991 
 992         a = affine.clone();
 993         res = TransformHelper.concatenate(new Shear(8, 9, 150, 0), a);
 994         a.prependShear(8, 9, 150, 0);
 995         assertAffineOk(res, a);
 996     }
 997 
 998     @Test
 999     public void testAppendZeroShear2D() {
1000         Affine a = affine.clone();
1001         assertStateOk(a);
1002 
1003         Transform res = TransformHelper.concatenate(a, new Shear(0, 0));
1004         a.appendShear(0, 0);
1005 
1006         assertAffineOk(res, a);
1007     }
1008 
1009     @Test
1010     public void appendShear2DShouldBeAtomic() {
1011         final Affine a = affine.clone();
1012         final Transform res = TransformHelper.concatenate(a, new Shear(8, 9));
1013 
1014         testOperationIsAtomic(a, () -> a.appendShear(8, 9), () -> assertAffineOk(res, a));
1015     }
1016 
1017     @Test
1018     public void testPrependShear2D() {
1019         Affine a = affine.clone();
1020         assertStateOk(a);
1021 
1022         Transform res = TransformHelper.concatenate(new Shear(8, 9), a);
1023         a.prependShear(8, 9);
1024 
1025         assertAffineOk(res, a);
1026     }
1027 
1028     @Test
1029     public void testPrependShearWhichMayEliminateTranslation() {
1030         Affine a = affine.clone();
1031         assertStateOk(a);
1032 
1033         final double shx = a.getTy() == 0 ? 0 : - a.getTx() / a.getTy();
1034         final double shy = a.getTx() == 0 ? 0 : - a.getTy() / a.getTx();
1035 
1036         Transform res = TransformHelper.concatenate(new Shear(shx, shy), a);
1037         a.prependShear(shx, shy);
1038 
1039         assertAffineOk(res, a);
1040     }
1041 
1042     @Test
1043     public void testPrependZeroShear2D() {
1044         Affine a = affine.clone();
1045         assertStateOk(a);
1046 
1047         Transform res = TransformHelper.concatenate(new Shear(0, 0), a);
1048         a.prependShear(0, 0);
1049 
1050         assertAffineOk(res, a);
1051     }
1052 
1053     @Test
1054     public void prependShear2DShouldBeAtomic() {
1055         final Affine a = affine.clone();
1056         final Transform res = TransformHelper.concatenate(new Shear(8, 9), a);
1057 
1058         testOperationIsAtomic(a, () -> a.prependShear(8, 9), () -> assertAffineOk(res, a));
1059     }
1060 
1061     @Test
1062     public void testAppendPivotedShear2D() {
1063         Affine a = affine.clone();
1064         assertStateOk(a);
1065 
1066         Transform res = TransformHelper.concatenate(a, new Shear(8, 9, 10, 11));
1067         a.appendShear(8, 9, 10, 11);
1068 
1069         assertAffineOk(res, a);
1070     }
1071 
1072     @Test
1073     public void testAppendPointPivotedShear2D() {
1074         Affine a = affine.clone();
1075         assertStateOk(a);
1076 
1077         Transform res = TransformHelper.concatenate(a, new Shear(8, 9, 10, 11));
1078         a.appendShear(8, 9, new Point2D(10, 11));
1079 
1080         assertAffineOk(res, a);
1081     }
1082 
1083     @Test
1084     public void appendPivotedShear2DShouldBeAtomic() {
1085         final Affine a = affine.clone();
1086         final Transform res = TransformHelper.concatenate(a, new Shear(8, 9, 10, 11));
1087 
1088         testOperationIsAtomic(a, () -> a.appendShear(8, 9, 10, 11), () -> assertAffineOk(res, a));
1089     }
1090 
1091     @Test
1092     public void testPrependPivotedShear2D() {
1093         Affine a = affine.clone();
1094         assertStateOk(a);
1095 
1096         Transform res = TransformHelper.concatenate(new Shear(8, 9, 10, 11), a);
1097         a.prependShear(8, 9, 10, 11);
1098 
1099         assertAffineOk(res, a);
1100     }
1101 
1102     @Test
1103     public void testPrependPointPivotedShear2D() {
1104         Affine a = affine.clone();
1105         assertStateOk(a);
1106 
1107         Transform res = TransformHelper.concatenate(new Shear(8, 9, 10, 11), a);
1108         a.prependShear(8, 9, new Point2D(10, 11));
1109 
1110         assertAffineOk(res, a);
1111     }
1112 
1113     @Test
1114     public void prependPivotedShear2DShouldBeAtomic() {
1115         final Affine a = affine.clone();
1116         final Transform res = TransformHelper.concatenate(new Shear(8, 9, 10, 11), a);
1117 
1118         testOperationIsAtomic(a, () -> a.prependShear(8, 9, 10, 11), () -> assertAffineOk(res, a));
1119     }
1120 
1121     @Test
1122     public void testAppendRotation2D() {
1123         Affine a = affine.clone();
1124         assertStateOk(a);
1125 
1126         Transform res = TransformHelper.concatenate(a, new Rotate(37));
1127         a.appendRotation(37);
1128 
1129         assertAffineOk(res, a);
1130     }
1131 
1132     @Test
1133     public void appendRotate2DShouldBeAtomic() {
1134         final Affine a = affine.clone();
1135         final Transform res = TransformHelper.concatenate(a, new Rotate(37));
1136 
1137         testOperationIsAtomic(a, () -> a.appendRotation(37), () -> assertAffineOk(res, a));
1138     }
1139 
1140     @Test
1141     public void testPrependRotation2D() {
1142         Affine a = affine.clone();
1143         assertStateOk(a);
1144 
1145         Transform res = TransformHelper.concatenate(new Rotate(37), a);
1146         a.prependRotation(37);
1147 
1148         assertAffineOk(res, a);
1149     }
1150 
1151     @Test
1152     public void prependRotate2DShouldBeAtomic() {
1153         final Affine a = affine.clone();
1154         final Transform res = TransformHelper.concatenate(new Rotate(37), a);
1155 
1156         testOperationIsAtomic(a, () -> a.prependRotation(37), () -> assertAffineOk(res, a));
1157     }
1158 
1159     @Test
1160     public void testAppendRotation90() {
1161         Affine a = affine.clone();
1162         assertStateOk(a);
1163 
1164         Transform res = TransformHelper.concatenate(a, new Rotate(90));
1165         a.appendRotation(90);
1166 
1167         assertAffineOk(res, a);
1168     }
1169 
1170     @Test
1171     public void appendRotate90ShouldBeAtomic() {
1172         final Affine a = affine.clone();
1173         final Transform res = TransformHelper.concatenate(a, new Rotate(90));
1174 
1175         testOperationIsAtomic(a, () -> a.appendRotation(90), () -> assertAffineOk(res, a));
1176     }
1177 
1178     @Test
1179     public void testPrependRotation90() {
1180         Affine a = affine.clone();
1181         assertStateOk(a);
1182 
1183         Transform res = TransformHelper.concatenate(new Rotate(90), a);
1184         a.prependRotation(90);
1185 
1186         assertAffineOk(res, a);
1187     }
1188 
1189     @Test
1190     public void prependRotate90ShouldBeAtomic() {
1191         final Affine a = affine.clone();
1192         final Transform res = TransformHelper.concatenate(a, new Rotate(90));
1193 
1194         testOperationIsAtomic(a, () -> a.appendRotation(90), () -> assertAffineOk(res, a));
1195     }
1196 
1197     @Test
1198     public void testAppendRotation180() {
1199         Affine a = affine.clone();
1200         assertStateOk(a);
1201 
1202         Transform res = TransformHelper.concatenate(a, new Rotate(180));
1203         a.appendRotation(180);
1204 
1205         assertAffineOk(res, a);
1206     }
1207 
1208     @Test
1209     public void appendRotate180ShouldBeAtomic() {
1210         final Affine a = affine.clone();
1211         final Transform res = TransformHelper.concatenate(a, new Rotate(180));
1212 
1213         testOperationIsAtomic(a, () -> a.appendRotation(180), () -> assertAffineOk(res, a));
1214     }
1215 
1216     @Test
1217     public void testPrependRotation180() {
1218         Affine a = affine.clone();
1219         assertStateOk(a);
1220 
1221         Transform res = TransformHelper.concatenate(new Rotate(180), a);
1222         a.prependRotation(180);
1223 
1224         assertAffineOk(res, a);
1225     }
1226 
1227     @Test
1228     public void prependRotate180ShouldBeAtomic() {
1229         final Affine a = affine.clone();
1230         final Transform res = TransformHelper.concatenate(a, new Rotate(180));
1231 
1232         testOperationIsAtomic(a, () -> a.appendRotation(180), () -> assertAffineOk(res, a));
1233     }
1234 
1235     @Test
1236     public void testAppendRotation270() {
1237         Affine a = affine.clone();
1238         assertStateOk(a);
1239 
1240         Transform res = TransformHelper.concatenate(a, new Rotate(270));
1241         a.appendRotation(270);
1242 
1243         assertAffineOk(res, a);
1244     }
1245 
1246     @Test
1247     public void appendRotate270ShouldBeAtomic() {
1248         final Affine a = affine.clone();
1249         final Transform res = TransformHelper.concatenate(a, new Rotate(270));
1250 
1251         testOperationIsAtomic(a, () -> a.appendRotation(270), () -> assertAffineOk(res, a));
1252     }
1253 
1254     @Test
1255     public void testPrependRotation270() {
1256         Affine a = affine.clone();
1257         assertStateOk(a);
1258 
1259         Transform res = TransformHelper.concatenate(new Rotate(270), a);
1260         a.prependRotation(270);
1261 
1262         assertAffineOk(res, a);
1263     }
1264 
1265     @Test
1266     public void prependRotate270ShouldBeAtomic() {
1267         final Affine a = affine.clone();
1268         final Transform res = TransformHelper.concatenate(a, new Rotate(270));
1269 
1270         testOperationIsAtomic(a, () -> a.appendRotation(270), () -> assertAffineOk(res, a));
1271     }
1272 
1273     @Test
1274     public void testAppendRotationMinus450() {
1275         Affine a = affine.clone();
1276         assertStateOk(a);
1277 
1278         Transform res = TransformHelper.concatenate(a, new Rotate(-450));
1279         a.appendRotation(-450);
1280 
1281         assertAffineOk(res, a);
1282     }
1283 
1284     @Test
1285     public void testPrependRotationMinus450() {
1286         Affine a = affine.clone();
1287         assertStateOk(a);
1288 
1289         Transform res = TransformHelper.concatenate(new Rotate(-450), a);
1290         a.prependRotation(-450);
1291 
1292         assertAffineOk(res, a);
1293     }
1294 
1295 
1296     @Test
1297     public void testAppendPivotedRotate2D() {
1298         Affine a = affine.clone();
1299         assertStateOk(a);
1300 
1301         Transform res = TransformHelper.concatenate(a, new Rotate(37, 10, 11));
1302         a.appendRotation(37, 10, 11);
1303 
1304         assertAffineOk(res, a);
1305     }
1306 
1307     @Test(expected=NullPointerException.class)
1308     public void testAppendNullPivotedRotate2D() {
1309         Affine a = affine.clone();
1310         a.appendRotation(8, null);
1311     }
1312 
1313     @Test(expected=NullPointerException.class)
1314     public void testPrependNullPivotedRotate2D() {
1315         Affine a = affine.clone();
1316         a.prependRotation(8, null);
1317     }
1318 
1319     @Test
1320     public void testAppendZeroPivotedRotate2D() {
1321         Affine a = affine.clone();
1322         assertStateOk(a);
1323 
1324         Transform res = TransformHelper.concatenate(a, new Rotate(37));
1325         a.appendRotation(37, 0, 0);
1326         assertAffineOk(res, a);
1327 
1328         a = affine.clone();
1329         res = TransformHelper.concatenate(a, new Rotate(37, 120, 0));
1330         a.appendRotation(37, 120, 0);
1331         assertAffineOk(res, a);
1332 
1333         a = affine.clone();
1334         res = TransformHelper.concatenate(a, new Rotate(37, 0, 150));
1335         a.appendRotation(37, 0, 150);
1336         assertAffineOk(res, a);
1337     }
1338 
1339     @Test
1340     public void testAppendPointPivotedRotate2D() {
1341         Affine a = affine.clone();
1342         assertStateOk(a);
1343 
1344         Transform res = TransformHelper.concatenate(a, new Rotate(37, 10, 11));
1345         a.appendRotation(37, new Point2D(10, 11));
1346 
1347         assertAffineOk(res, a);
1348     }
1349 
1350     @Test
1351     public void appendPivotedRotate2DShouldBeAtomic() {
1352         final Affine a = affine.clone();
1353         final Transform res = TransformHelper.concatenate(a, new Rotate(37, 10, 11));
1354 
1355         testOperationIsAtomic(a, () -> a.appendRotation(37, 10, 11), () -> assertAffineOk(res, a));
1356     }
1357 
1358     @Test
1359     public void testPrependPivotedRotate2D() {
1360         Affine a = affine.clone();
1361         assertStateOk(a);
1362 
1363         Transform res = TransformHelper.concatenate(new Rotate(37, 10, 11), a);
1364         a.prependRotation(37, 10, 11);
1365 
1366         assertAffineOk(res, a);
1367     }
1368 
1369     @Test
1370     public void testPrependZeroPivotedRotate2D() {
1371         Affine a = affine.clone();
1372         assertStateOk(a);
1373 
1374         Transform res = TransformHelper.concatenate(new Rotate(37), a);
1375         a.prependRotation(37, 0, 0);
1376 
1377         a = affine.clone();
1378         res = TransformHelper.concatenate(new Rotate(37, 120, 0), a);
1379         a.prependRotation(37, 120, 0);
1380         assertAffineOk(res, a);
1381 
1382         a = affine.clone();
1383         res = TransformHelper.concatenate(new Rotate(37, 0, 150), a);
1384         a.prependRotation(37, 0, 150);
1385         assertAffineOk(res, a);
1386 
1387         assertAffineOk(res, a);
1388     }
1389 
1390     @Test
1391     public void testPrependPointPivotedRotate2D() {
1392         Affine a = affine.clone();
1393         assertStateOk(a);
1394 
1395         Transform res = TransformHelper.concatenate(new Rotate(37, 10, 11), a);
1396         a.prependRotation(37, new Point2D(10, 11));
1397 
1398         assertAffineOk(res, a);
1399     }
1400 
1401     @Test
1402     public void prependPivotedRotate2DShouldBeAtomic() {
1403         final Affine a = affine.clone();
1404         final Transform res = TransformHelper.concatenate(new Rotate(37, 10, 11), a);
1405 
1406         testOperationIsAtomic(a, () -> a.prependRotation(37, 10, 11), () -> assertAffineOk(res, a));
1407     }
1408 
1409     @Test
1410     public void testAppendNoRotation() {
1411         Affine a = affine.clone();
1412         assertStateOk(a);
1413 
1414         Transform res = TransformHelper.concatenate(a,
1415                 new Rotate(0));
1416         a.appendRotation(0);
1417 
1418         assertAffineOk(res, a);
1419     }
1420 
1421     @Test
1422     public void testAppendRotation3D() {
1423         Affine a = affine.clone();
1424         assertStateOk(a);
1425 
1426         Transform res = TransformHelper.concatenate(a,
1427                 new Rotate(37, 8, 9, 10, new Point3D(12, 123, 521)));
1428         a.appendRotation(37, 8, 9, 10, 12, 123, 521);
1429 
1430         assertAffineOk(res, a);
1431     }
1432 
1433     @Test(expected=NullPointerException.class)
1434     public void testAppendNullAxisRotation3D() {
1435         Affine a = affine.clone();
1436         a.appendRotation(8, 100, 110, 120, null);
1437     }
1438 
1439     @Test(expected=NullPointerException.class)
1440     public void testPrependNullAxisRotation3D() {
1441         Affine a = affine.clone();
1442         a.prependRotation(8, 100, 110, 120, null);
1443     }
1444 
1445     @Test(expected=NullPointerException.class)
1446     public void testAppendNullAxisPointPivotRotation3D() {
1447         Affine a = affine.clone();
1448         a.appendRotation(8, new Point3D(100, 110, 120), null);
1449     }
1450 
1451     @Test(expected=NullPointerException.class)
1452     public void testPrependNullAxisPointPivotRotation3D() {
1453         Affine a = affine.clone();
1454         a.prependRotation(8, new Point3D(100, 110, 120), null);
1455     }
1456 
1457     @Test(expected=NullPointerException.class)
1458     public void testAppendNullPivotRotation3D() {
1459         Affine a = affine.clone();
1460         a.appendRotation(8, null, Rotate.Z_AXIS);
1461     }
1462 
1463     @Test(expected=NullPointerException.class)
1464     public void testPrependNullPivotRotation3D() {
1465         Affine a = affine.clone();
1466         a.prependRotation(8, null, Rotate.Z_AXIS);
1467     }
1468 
1469     @Test
1470     public void testAppendRotation3Dbeing2D() {
1471         Affine a = affine.clone();
1472         assertStateOk(a);
1473 
1474         Transform res = TransformHelper.concatenate(a,
1475                 new Rotate(37, 8, 9, 0, new Point3D(0, 0, 10)));
1476         a.appendRotation(37, 8, 9, 0, 0, 0, 10);
1477 
1478         assertAffineOk(res, a);
1479     }
1480 
1481     @Test
1482     public void testAppendRotation3DbeingUpsideDown2D() {
1483         Affine a = affine.clone();
1484         assertStateOk(a);
1485 
1486         Transform res = TransformHelper.concatenate(a,
1487                 new Rotate(37, 8, 9, 0, new Point3D(0, 0,- 10)));
1488         a.appendRotation(37, 8, 9, 0, 0, 0, -10);
1489 
1490         assertAffineOk(res, a);
1491     }
1492 
1493     @Test
1494     public void testAppendRotationWithZeroAxis() {
1495         Affine a = affine.clone();
1496         assertStateOk(a);
1497 
1498         a.appendRotation(37, 8, 9, 10, 0, 0, 0);
1499 
1500         assertAffineOk(affine, a);
1501     }
1502 
1503     @Test
1504     public void testAppendRotationWithAlmostZeroAxis() {
1505         Affine a = affine.clone();
1506         assertStateOk(a);
1507 
1508         a.appendRotation(37, 8, 9, 10, 0, Double.MIN_VALUE, 0);
1509 
1510         assertAffineOk(affine, a);
1511     }
1512 
1513     @Test
1514     public void testAppendPointedAxisRotation3D() {
1515         Affine a = affine.clone();
1516         assertStateOk(a);
1517 
1518         Transform res = TransformHelper.concatenate(a,
1519                 new Rotate(37, 8, 9, 10, new Point3D(12, 123, 521)));
1520         a.appendRotation(37, 8, 9, 10, new Point3D(12, 123, 521));
1521 
1522         assertAffineOk(res, a);
1523     }
1524 
1525     @Test
1526     public void testAppendPointedAxisPointedPivotRotation3D() {
1527         Affine a = affine.clone();
1528         assertStateOk(a);
1529 
1530         Transform res = TransformHelper.concatenate(a,
1531                 new Rotate(37, 8, 9, 10, new Point3D(12, 123, 521)));
1532         a.appendRotation(37, new Point3D(8, 9, 10), new Point3D(12, 123, 521));
1533 
1534         assertAffineOk(res, a);
1535     }
1536 
1537     @Test
1538     public void appendRotate3DShouldBeAtomic() {
1539         final Affine a = affine.clone();
1540         final Transform res = TransformHelper.concatenate(a,
1541                 new Rotate(37, 8, 9, 10, new Point3D(12, 123, 521)));
1542 
1543         testOperationIsAtomic(a, () -> a.appendRotation(37, 8, 9, 10, 12, 123, 521), () -> assertAffineOk(res, a));
1544     }
1545 
1546     @Test
1547     public void testPrependRotation3D() {
1548         Affine a = affine.clone();
1549         assertStateOk(a);
1550 
1551         Transform res = TransformHelper.concatenate(
1552                 new Rotate(37, 8, 9, 10, new Point3D(12, 123, 521)), a);
1553         a.prependRotation(37, 8, 9, 10, 12, 123, 521);
1554 
1555         assertAffineOk(res, a);
1556     }
1557 
1558     @Test
1559     public void testPrependNoRotation() {
1560         Affine a = affine.clone();
1561         assertStateOk(a);
1562 
1563         Transform res = TransformHelper.concatenate(new Rotate(0), a);
1564         a.prependRotation(0);
1565 
1566         assertAffineOk(res, a);
1567     }
1568 
1569     @Test
1570     public void testPrependRotation3Dbeing2D() {
1571         Affine a = affine.clone();
1572         assertStateOk(a);
1573 
1574         Transform res = TransformHelper.concatenate(
1575                 new Rotate(37, 8, 9, 0, new Point3D(0, 0, 10)), a);
1576         a.prependRotation(37, 8, 9, 0, 0, 0, 10);
1577 
1578         assertAffineOk(res, a);
1579     }
1580 
1581     @Test
1582     public void testPrependRotation3DbeingUpsideDown2D() {
1583         Affine a = affine.clone();
1584         assertStateOk(a);
1585 
1586         Transform res = TransformHelper.concatenate(
1587                 new Rotate(37, 8, 9, 0, new Point3D(0, 0, -10)), a);
1588         a.prependRotation(37, 8, 9, 0, 0, 0, -10);
1589 
1590         assertAffineOk(res, a);
1591     }
1592 
1593     @Test
1594     public void testPrependRotationWithZeroAxis() {
1595         Affine a = affine.clone();
1596         assertStateOk(a);
1597 
1598         a.prependRotation(37, 8, 9, 10, 0, 0, 0);
1599 
1600         assertAffineOk(affine, a);
1601     }
1602 
1603     @Test
1604     public void testPrependRotationWithAlmostZeroAxis() {
1605         Affine a = affine.clone();
1606         assertStateOk(a);
1607 
1608         a.prependRotation(37, 8, 9, 10, 0, Double.MIN_VALUE, 0);
1609 
1610         assertAffineOk(affine, a);
1611     }
1612 
1613     @Test
1614     public void testPrependPointedAxisRotation3D() {
1615         Affine a = affine.clone();
1616         assertStateOk(a);
1617 
1618         Transform res = TransformHelper.concatenate(
1619                 new Rotate(37, 8, 9, 10, new Point3D(12, 123, 521)), a);
1620         a.prependRotation(37, 8, 9, 10, new Point3D(12, 123, 521));
1621 
1622         assertAffineOk(res, a);
1623     }
1624 
1625     @Test
1626     public void testPrependPointedAxisPointedPivotRotation3D() {
1627         Affine a = affine.clone();
1628         assertStateOk(a);
1629 
1630         Transform res = TransformHelper.concatenate(
1631                 new Rotate(37, 8, 9, 10, new Point3D(12, 123, 521)), a);
1632         a.prependRotation(37, new Point3D(8, 9, 10), new Point3D(12, 123, 521));
1633 
1634         assertAffineOk(res, a);
1635     }
1636 
1637 
1638     @Test
1639     public void prependRotate3DShouldBeAtomic() {
1640         final Affine a = affine.clone();
1641         final Transform res = TransformHelper.concatenate(
1642                 new Rotate(37, 8, 9, 10, new Point3D(12, 123, 521)), a);
1643 
1644         testOperationIsAtomic(a, () -> a.prependRotation(37, 8, 9, 10, 12, 123, 521), () -> assertAffineOk(res, a));
1645     }
1646 
1647     @Test
1648     public void testAppend2D() {
1649         Affine a = affine.clone();
1650         assertStateOk(a);
1651 
1652         Affine other = new Affine(20, 22, 24,
1653                                   28, 30, 32);
1654 
1655         Transform res = TransformHelper.concatenate(a, other);
1656         a.append(20, 22, 24,
1657                   28, 30, 32);
1658 
1659         assertAffineOk(res, a);
1660     }
1661 
1662     @Test
1663     public void append2DShouldBeAtomic() {
1664         final Affine a = affine.clone();
1665         final Transform res = TransformHelper.concatenate(a,
1666                 new Affine(20, 22, 24,
1667                            28, 30, 32));
1668 
1669         testOperationIsAtomic(a, () -> a.append(20, 22, 24,
1670                  28, 30, 32), () -> assertAffineOk(res, a));
1671     }
1672 
1673     @Test
1674     public void testPrepend2D() {
1675         Affine a = affine.clone();
1676         assertStateOk(a);
1677 
1678         Affine other = new Affine(20, 22, 24,
1679                                   28, 30, 32);
1680 
1681         Transform res = TransformHelper.concatenate(other, a);
1682         a.prepend(20, 22, 24,
1683                  28, 30, 32);
1684 
1685         assertAffineOk(res, a);
1686     }
1687 
1688     @Test
1689     public void prepend2DShouldBeAtomic() {
1690         final Affine a = affine.clone();
1691         final Transform res = TransformHelper.concatenate(
1692                 new Affine(20, 22, 24,
1693                            28, 30, 32), a);
1694 
1695         testOperationIsAtomic(a, () -> a.prepend(20, 22, 24,
1696                  28, 30, 32), () -> assertAffineOk(res, a));
1697     }
1698 
1699     @Test
1700     public void testAppend3D() {
1701         Affine a = affine.clone();
1702         assertStateOk(a);
1703 
1704         Affine other = new Affine(20, 22, 24, 26,
1705                                   28, 30, 32, 34,
1706                                   36, 38, 40, 42);
1707 
1708         Transform res = TransformHelper.concatenate(a, other);
1709         a.append(20, 22, 24, 26,
1710                   28, 30, 32, 34,
1711                   36, 38, 40, 42);
1712 
1713         assertAffineOk(res, a);
1714     }
1715 
1716     @Test
1717     public void append3DShouldBeAtomic() {
1718         final Affine a = affine.clone();
1719         final Transform res = TransformHelper.concatenate(a,
1720                 new Affine(20, 22, 24, 26,
1721                            28, 30, 32, 34,
1722                            36, 38, 40, 42));
1723 
1724         testOperationIsAtomic(a, () -> a.append(20, 22, 24, 26,
1725                  28, 30, 32, 34,
1726                  36, 38, 40, 42), () -> assertAffineOk(res, a));
1727     }
1728 
1729     @Test
1730     public void testPrepend3D() {
1731         Affine a = affine.clone();
1732         assertStateOk(a);
1733 
1734         Affine other = new Affine(20, 22, 24, 26,
1735                                   28, 30, 32, 34,
1736                                   36, 38, 40, 42);
1737 
1738         Transform res = TransformHelper.concatenate(other, a);
1739         a.prepend(20, 22, 24, 26,
1740                   28, 30, 32, 34,
1741                   36, 38, 40, 42);
1742 
1743         assertAffineOk(res, a);
1744     }
1745 
1746     @Test
1747     public void prepend3DShouldBeAtomic() {
1748         final Affine a = affine.clone();
1749         final Transform res = TransformHelper.concatenate(
1750                 new Affine(20, 22, 24, 26,
1751                            28, 30, 32, 34,
1752                            36, 38, 40, 42), a);
1753 
1754         testOperationIsAtomic(a, () -> a.prepend(20, 22, 24, 26,
1755                  28, 30, 32, 34,
1756                  36, 38, 40, 42), () -> assertAffineOk(res, a));
1757     }
1758 
1759     @Test
1760     public void testAppendTransform() {
1761         int counter = 0;
1762         for (Object o : TransformOperationsTest.getParams()) {
1763             Object[] arr = (Object[]) o;
1764             Transform other = (Transform) arr[0];
1765 
1766             Affine a = affine.clone();
1767             Transform res = TransformHelper.concatenate(a, other);
1768             a.append(other);
1769 
1770             assertAffineOk("Appending #" + (counter++) +
1771                     " from TransformOperationsTest", res, a);
1772         }
1773     }
1774 
1775     @Test(expected=NullPointerException.class)
1776     public void testAppendNullTransform() {
1777         Affine a = affine.clone();
1778         a.append((Transform) null);
1779     }
1780 
1781     @Test
1782     public void appendTransformShouldBeAtomic() {
1783         final Affine a = affine.clone();
1784         final Transform res = TransformHelper.concatenate(a,
1785                 new Affine(20, 22, 24, 26,
1786                            28, 30, 32, 34,
1787                            36, 38, 40, 42));
1788 
1789         testOperationIsAtomic(a, () -> a.append(new Affine(
1790                  20, 22, 24, 26,
1791                  28, 30, 32, 34,
1792                  36, 38, 40, 42)), () -> assertAffineOk(res, a));
1793     }
1794 
1795     @Test
1796     public void testPrependTransform() {
1797         int counter = 0;
1798         for (Object o : TransformOperationsTest.getParams()) {
1799             Object[] arr = (Object[]) o;
1800             Transform other = (Transform) arr[0];
1801 
1802             Affine a = affine.clone();
1803             assertStateOk(a);
1804 
1805             Transform res = TransformHelper.concatenate(other, a);
1806             a.prepend(other);
1807 
1808             assertAffineOk("Prepending #" + (counter++) +
1809                     " from TransformOperationsTest", res, a);
1810         }
1811     }
1812 
1813     @Test(expected=NullPointerException.class)
1814     public void testPrependNullTransform() {
1815         Affine a = affine.clone();
1816         a.prepend((Transform) null);
1817     }
1818 
1819     @Test
1820     public void prependTransformShouldBeAtomic() {
1821         final Affine a = affine.clone();
1822         final Transform res = TransformHelper.concatenate(
1823                 new Affine(20, 22, 24, 26,
1824                            28, 30, 32, 34,
1825                            36, 38, 40, 42), a);
1826 
1827         testOperationIsAtomic(a, () -> a.prepend(new Affine(
1828                  20, 22, 24, 26,
1829                  28, 30, 32, 34,
1830                  36, 38, 40, 42)), () -> assertAffineOk(res, a));
1831     }
1832 
1833     @Test
1834     public void testAppendArray() {
1835 
1836         Affine a = affine.clone();
1837         a.append(array2d, MatrixType.MT_2D_2x3, 2);
1838         TransformHelper.assertMatrix(a, TransformHelper.concatenate(affine,
1839                 new Affine(
1840                     2,  3,  0,  4,
1841                     6,  7,  0,  8,
1842                     0,  0,  1,  0)));
1843 
1844         a = affine.clone();
1845         a.append(array2d, MatrixType.MT_2D_3x3, 2);
1846         TransformHelper.assertMatrix(a, TransformHelper.concatenate(affine,
1847                 new Affine(
1848                     2,  3,  0,  4,
1849                     6,  7,  0,  8,
1850                     0,  0,  1,  0)));
1851 
1852         a = affine.clone();
1853         a.append(array3d, MatrixType.MT_3D_3x4, 2);
1854         TransformHelper.assertMatrix(a, TransformHelper.concatenate(affine,
1855                 new Affine(
1856                     2,  3,  4,  5,
1857                     6,  7,  8,  9,
1858                    10, 11, 12, 13)));
1859 
1860         a = affine.clone();
1861         a.append(array3d, MatrixType.MT_3D_4x4, 2);
1862         TransformHelper.assertMatrix(a, TransformHelper.concatenate(affine,
1863                 new Affine(
1864                     2,  3,  4,  5,
1865                     6,  7,  8,  9,
1866                    10, 11, 12, 13)));
1867     }
1868 
1869     @Test(expected=NullPointerException.class)
1870     public void testAppendArrayNullMatrix() {
1871         Affine a = new Affine();
1872         a.append(new double[] { 1, 2, 3 }, null, 0);
1873     }
1874 
1875     @Test(expected=NullPointerException.class)
1876     public void testAppendArrayNullType() {
1877         Affine a = new Affine();
1878         a.append(null, MatrixType.MT_2D_2x3, 0);
1879     }
1880 
1881     @Test
1882     public void appendArray2x3ShouldBeAtomic() {
1883         final Affine a = affine.clone();
1884         final Transform res = TransformHelper.concatenate(a,
1885                 new Affine(2,  3,  0,  4,
1886                            6,  7,  0,  8,
1887                            0,  0,  1,  0));
1888 
1889         testOperationIsAtomic(a, () -> a.append(array2d, MatrixType.MT_2D_2x3, 2), () -> assertAffineOk(res, a));
1890     }
1891 
1892     @Test
1893     public void appendArray3x3ShouldBeAtomic() {
1894         final Affine a = affine.clone();
1895         final Transform res = TransformHelper.concatenate(a,
1896                 new Affine(2,  3,  0,  4,
1897                            6,  7,  0,  8,
1898                            0,  0,  1,  0));
1899 
1900         testOperationIsAtomic(a, () -> a.append(array2d, MatrixType.MT_2D_3x3, 2), () -> assertAffineOk(res, a));
1901     }
1902 
1903     @Test
1904     public void appendArray3x4ShouldBeAtomic() {
1905         final Affine a = affine.clone();
1906         final Transform res = TransformHelper.concatenate(a,
1907                 new Affine(2,  3,  4,  5,
1908                            6,  7,  8,  9,
1909                           10, 11, 12, 13));
1910 
1911         testOperationIsAtomic(a, () -> a.append(array3d, MatrixType.MT_3D_3x4, 2), () -> assertAffineOk(res, a));
1912     }
1913 
1914     @Test
1915     public void appendArray4x4ShouldBeAtomic() {
1916         final Affine a = affine.clone();
1917         final Transform res = TransformHelper.concatenate(a,
1918                 new Affine(2,  3,  4,  5,
1919                            6,  7,  8,  9,
1920                           10, 11, 12, 13));
1921 
1922         testOperationIsAtomic(a, () -> a.append(array3d, MatrixType.MT_3D_4x4, 2), () -> assertAffineOk(res, a));
1923     }
1924 
1925     @Test(expected=IndexOutOfBoundsException.class)
1926     public void testAppendArray2x3ShortArray() {
1927         Affine a = affine.clone();
1928         try {
1929             a.append(array2d, MatrixType.MT_2D_2x3, 6);
1930         } catch(IndexOutOfBoundsException e) {
1931             TransformHelper.assertMatrix(a, affine);
1932             throw e;
1933         }
1934     }
1935 
1936     @Test(expected=IndexOutOfBoundsException.class)
1937     public void testAppendArray3x3ShortArray() {
1938         Affine a = affine.clone();
1939         try {
1940             a.append(array2d, MatrixType.MT_2D_3x3, 4);
1941         } catch(IndexOutOfBoundsException e) {
1942             TransformHelper.assertMatrix(a, affine);
1943             throw e;
1944         }
1945     }
1946 
1947     @Test(expected=IndexOutOfBoundsException.class)
1948     public void testAppendArray3x4ShortArray() {
1949         Affine a = affine.clone();
1950         try {
1951             a.append(array3d, MatrixType.MT_3D_3x4, 7);
1952         } catch(IndexOutOfBoundsException e) {
1953             TransformHelper.assertMatrix(a, affine);
1954             throw e;
1955         }
1956     }
1957 
1958     @Test(expected=IndexOutOfBoundsException.class)
1959     public void testAppendArray4x4ShortArray() {
1960         Affine a = affine.clone();
1961         try {
1962             a.append(array3d, MatrixType.MT_3D_4x4, 4);
1963         } catch(IndexOutOfBoundsException e) {
1964             TransformHelper.assertMatrix(a, affine);
1965             throw e;
1966         }
1967     }
1968 
1969     @Test(expected=IllegalArgumentException.class)
1970     public void testAppendArray3x3NotAffineX() {
1971         Affine a = affine.clone();
1972         try {
1973             a.append(arrayZeros, MatrixType.MT_2D_3x3, 10);
1974         } catch(IllegalArgumentException e) {
1975             TransformHelper.assertMatrix(a, affine);
1976             throw e;
1977         }
1978     }
1979 
1980     @Test(expected=IllegalArgumentException.class)
1981     public void testAppendArray3x3NotAffineY() {
1982         Affine a = affine.clone();
1983         try {
1984             a.append(arrayZeros, MatrixType.MT_2D_3x3, 9);
1985         } catch(IllegalArgumentException e) {
1986             TransformHelper.assertMatrix(a, affine);
1987             throw e;
1988         }
1989     }
1990 
1991     @Test(expected=IllegalArgumentException.class)
1992     public void testAppendArray3x3NotAffineT() {
1993         Affine a = affine.clone();
1994         try {
1995             a.append(arrayZeros, MatrixType.MT_2D_3x3, 0);
1996         } catch(IllegalArgumentException e) {
1997             TransformHelper.assertMatrix(a, affine);
1998             throw e;
1999         }
2000     }
2001 
2002     @Test(expected=IllegalArgumentException.class)
2003     public void testAppendArray4x4NotAffineX() {
2004         Affine a = affine.clone();
2005         try {
2006             a.append(arrayZeros, MatrixType.MT_3D_4x4, 4);
2007         } catch(IllegalArgumentException e) {
2008             TransformHelper.assertMatrix(a, affine);
2009             throw e;
2010         }
2011     }
2012 
2013     @Test(expected=IllegalArgumentException.class)
2014     public void testAppendArray4x4NotAffineY() {
2015         Affine a = affine.clone();
2016         try {
2017             a.append(arrayZeros, MatrixType.MT_3D_4x4, 3);
2018         } catch(IllegalArgumentException e) {
2019             TransformHelper.assertMatrix(a, affine);
2020             throw e;
2021         }
2022     }
2023 
2024     @Test(expected=IllegalArgumentException.class)
2025     public void testAppendArray4x4NotAffineZ() {
2026         Affine a = affine.clone();
2027         try {
2028             a.append(arrayZeros, MatrixType.MT_3D_4x4, 2);
2029         } catch(IllegalArgumentException e) {
2030             TransformHelper.assertMatrix(a, affine);
2031             throw e;
2032         }
2033     }
2034 
2035     @Test(expected=IllegalArgumentException.class)
2036     public void testAppendArray4x4NotAffineT() {
2037         Affine a = affine.clone();
2038         try {
2039             a.append(arrayZeros, MatrixType.MT_3D_4x4, 0);
2040         } catch(IllegalArgumentException e) {
2041             TransformHelper.assertMatrix(a, affine);
2042             throw e;
2043         }
2044     }
2045 
2046     @Test
2047     public void testPrependArray() {
2048 
2049         Affine a = affine.clone();
2050         a.prepend(array2d, MatrixType.MT_2D_2x3, 2);
2051         TransformHelper.assertMatrix(a, TransformHelper.concatenate(
2052                 new Affine(
2053                     2,  3,  0,  4,
2054                     6,  7,  0,  8,
2055                     0,  0,  1,  0), affine));
2056 
2057         a = affine.clone();
2058         a.prepend(array2d, MatrixType.MT_2D_3x3, 2);
2059         TransformHelper.assertMatrix(a, TransformHelper.concatenate(
2060                 new Affine(
2061                     2,  3,  0,  4,
2062                     6,  7,  0,  8,
2063                     0,  0,  1,  0), affine));
2064 
2065         a = affine.clone();
2066         a.prepend(array3d, MatrixType.MT_3D_3x4, 2);
2067         TransformHelper.assertMatrix(a, TransformHelper.concatenate(
2068                 new Affine(
2069                     2,  3,  4,  5,
2070                     6,  7,  8,  9,
2071                    10, 11, 12, 13), affine));
2072 
2073         a = affine.clone();
2074         a.prepend(array3d, MatrixType.MT_3D_4x4, 2);
2075         TransformHelper.assertMatrix(a, TransformHelper.concatenate(
2076                 new Affine(
2077                     2,  3,  4,  5,
2078                     6,  7,  8,  9,
2079                    10, 11, 12, 13), affine));
2080     }
2081 
2082     @Test(expected=NullPointerException.class)
2083     public void testPrependArrayNullMatrix() {
2084         Affine a = new Affine();
2085         a.prepend(new double[] { 1, 2, 3 }, null, 0);
2086     }
2087 
2088     @Test(expected=NullPointerException.class)
2089     public void testPrependArrayNullType() {
2090         Affine a = new Affine();
2091         a.prepend(null, MatrixType.MT_2D_2x3, 0);
2092     }
2093 
2094     @Test
2095     public void prependArray2x3ShouldBeAtomic() {
2096         final Affine a = affine.clone();
2097         final Transform res = TransformHelper.concatenate(
2098                 new Affine(2,  3,  0,  4,
2099                            6,  7,  0,  8,
2100                            0,  0,  1,  0), a);
2101 
2102         testOperationIsAtomic(a, () -> a.prepend(array2d, MatrixType.MT_2D_2x3, 2), () -> assertAffineOk(res, a));
2103     }
2104 
2105     @Test
2106     public void prependArray3x3ShouldBeAtomic() {
2107         final Affine a = affine.clone();
2108         final Transform res = TransformHelper.concatenate(
2109                 new Affine(2,  3,  0,  4,
2110                            6,  7,  0,  8,
2111                            0,  0,  1,  0), a);
2112 
2113         testOperationIsAtomic(a, () -> a.prepend(array2d, MatrixType.MT_2D_3x3, 2), () -> assertAffineOk(res, a));
2114     }
2115 
2116     @Test
2117     public void prependArray3x4ShouldBeAtomic() {
2118         final Affine a = affine.clone();
2119         final Transform res = TransformHelper.concatenate(
2120                 new Affine(2,  3,  4,  5,
2121                            6,  7,  8,  9,
2122                           10, 11, 12, 13), a);
2123 
2124         testOperationIsAtomic(a, () -> a.prepend(array3d, MatrixType.MT_3D_3x4, 2), () -> assertAffineOk(res, a));
2125     }
2126 
2127     @Test
2128     public void prependArray4x4ShouldBeAtomic() {
2129         final Affine a = affine.clone();
2130         final Transform res = TransformHelper.concatenate(
2131                 new Affine(2,  3,  4,  5,
2132                            6,  7,  8,  9,
2133                           10, 11, 12, 13), a);
2134 
2135         testOperationIsAtomic(a, () -> a.prepend(array3d, MatrixType.MT_3D_4x4, 2), () -> assertAffineOk(res, a));
2136     }
2137 
2138     @Test(expected=IndexOutOfBoundsException.class)
2139     public void testPrependArray2x3ShortArray() {
2140         Affine a = affine.clone();
2141         try {
2142             a.prepend(array2d, MatrixType.MT_2D_2x3, 6);
2143         } catch(IndexOutOfBoundsException e) {
2144             TransformHelper.assertMatrix(a, affine);
2145             throw e;
2146         }
2147     }
2148 
2149     @Test(expected=IndexOutOfBoundsException.class)
2150     public void testPrependdArray3x3ShortArray() {
2151         Affine a = affine.clone();
2152         try {
2153             a.prepend(array2d, MatrixType.MT_2D_3x3, 4);
2154         } catch(IndexOutOfBoundsException e) {
2155             TransformHelper.assertMatrix(a, affine);
2156             throw e;
2157         }
2158     }
2159 
2160     @Test(expected=IndexOutOfBoundsException.class)
2161     public void testPrependArray3x4ShortArray() {
2162         Affine a = affine.clone();
2163         try {
2164             a.prepend(array3d, MatrixType.MT_3D_3x4, 7);
2165         } catch(IndexOutOfBoundsException e) {
2166             TransformHelper.assertMatrix(a, affine);
2167             throw e;
2168         }
2169     }
2170 
2171     @Test(expected=IndexOutOfBoundsException.class)
2172     public void testPrependArray4x4ShortArray() {
2173         Affine a = affine.clone();
2174         try {
2175             a.prepend(array3d, MatrixType.MT_3D_4x4, 4);
2176         } catch(IndexOutOfBoundsException e) {
2177             TransformHelper.assertMatrix(a, affine);
2178             throw e;
2179         }
2180     }
2181 
2182     @Test(expected=IllegalArgumentException.class)
2183     public void prestPrependArray3x3NotAffineX() {
2184         Affine a = affine.clone();
2185         try {
2186             a.prepend(arrayZeros, MatrixType.MT_2D_3x3, 10);
2187         } catch(IllegalArgumentException e) {
2188             TransformHelper.assertMatrix(a, affine);
2189             throw e;
2190         }
2191     }
2192 
2193     @Test(expected=IllegalArgumentException.class)
2194     public void prestPrependArray3x3NotAffineY() {
2195         Affine a = affine.clone();
2196         try {
2197             a.prepend(arrayZeros, MatrixType.MT_2D_3x3, 9);
2198         } catch(IllegalArgumentException e) {
2199             TransformHelper.assertMatrix(a, affine);
2200             throw e;
2201         }
2202     }
2203 
2204     @Test(expected=IllegalArgumentException.class)
2205     public void prestPrependArray3x3NotAffineT() {
2206         Affine a = affine.clone();
2207         try {
2208             a.prepend(arrayZeros, MatrixType.MT_2D_3x3, 0);
2209         } catch(IllegalArgumentException e) {
2210             TransformHelper.assertMatrix(a, affine);
2211             throw e;
2212         }
2213     }
2214 
2215     @Test(expected=IllegalArgumentException.class)
2216     public void prestPrependArray4x4NotAffineX() {
2217         Affine a = affine.clone();
2218         try {
2219             a.prepend(arrayZeros, MatrixType.MT_3D_4x4, 4);
2220         } catch(IllegalArgumentException e) {
2221             TransformHelper.assertMatrix(a, affine);
2222             throw e;
2223         }
2224     }
2225 
2226     @Test(expected=IllegalArgumentException.class)
2227     public void prestPrependArray4x4NotAffineY() {
2228         Affine a = affine.clone();
2229         try {
2230             a.prepend(arrayZeros, MatrixType.MT_3D_4x4, 3);
2231         } catch(IllegalArgumentException e) {
2232             TransformHelper.assertMatrix(a, affine);
2233             throw e;
2234         }
2235     }
2236 
2237     @Test(expected=IllegalArgumentException.class)
2238     public void prestPrependArray4x4NotAffineZ() {
2239         Affine a = affine.clone();
2240         try {
2241             a.prepend(arrayZeros, MatrixType.MT_3D_4x4, 2);
2242         } catch(IllegalArgumentException e) {
2243             TransformHelper.assertMatrix(a, affine);
2244             throw e;
2245         }
2246     }
2247 
2248     @Test(expected=IllegalArgumentException.class)
2249     public void prestPrependArray4x4NotAffineT() {
2250         Affine a = affine.clone();
2251         try {
2252             a.prepend(arrayZeros, MatrixType.MT_3D_4x4, 0);
2253         } catch(IllegalArgumentException e) {
2254             TransformHelper.assertMatrix(a, affine);
2255             throw e;
2256         }
2257     }
2258 
2259     @Test
2260     public void testInvert() {
2261         Affine a = affine.clone();
2262         Transform expected = null;
2263 
2264         boolean exception = false;
2265 
2266         try {
2267             expected = TransformHelper.invert(a);
2268         } catch (NonInvertibleTransformException e) {
2269             exception = true;
2270         }
2271 
2272         try {
2273             a.invert();
2274         } catch (NonInvertibleTransformException e) {
2275             if (!exception) {
2276                 fail("Should not have thrown NonInvertibleTransformException");
2277             }
2278             return;
2279         }
2280 
2281         if (exception) {
2282             fail("Should have thrown NonInvertibleTransformException");
2283             return;
2284         }
2285 
2286         TransformHelper.assertMatrix(a, expected);
2287         assertStateOk(a);
2288     }
2289 
2290     @Test
2291     public void invertShouldBeAtomic() {
2292         final Affine a = affine.clone();
2293 
2294         try {
2295             final Transform res = TransformHelper.invert(a);
2296             testOperationIsAtomic(a, () -> {
2297                         try {
2298                             a.invert();
2299                         } catch (NonInvertibleTransformException e) {
2300                             fail("Should be invertible");
2301                         }
2302                     }, () -> assertAffineOk(res, a));
2303         } catch (NonInvertibleTransformException e) {
2304                     try {
2305                         a.invert();
2306                     } catch (NonInvertibleTransformException ee) {
2307                         assertAffineOk(affine, a);
2308                         return;
2309                     }
2310         }
2311 
2312     }
2313 
2314     @Test
2315     public void testAppendInverse() {
2316         final Affine a = affine.clone();
2317         final Affine i = affine.clone();
2318 
2319         try {
2320             i.invert();
2321         } catch (NonInvertibleTransformException e) {
2322             // nothing to test
2323             return;
2324         }
2325 
2326         a.append(i);
2327         assertAffineOk(new Affine(1, 0, 0, 0,
2328                                   0, 1, 0, 0,
2329                                   0, 0, 1, 0), a);
2330     }
2331 
2332     private void assertSetElement(Affine a, MatrixType type, int row, int col,
2333             double previous, boolean iae, boolean iob) {
2334         double res = Double.MIN_VALUE;
2335 
2336         try {
2337             a.setElement(type, row, col, previous + 1000);
2338         } catch (IllegalArgumentException e) {
2339             if (iae) {
2340                 // ok
2341                 return;
2342             }
2343         } catch (IndexOutOfBoundsException e) {
2344             if (iob) {
2345                 // ok
2346                 return;
2347             }
2348         }
2349 
2350         if (iae) {
2351             fail("Should have thrown IAE");
2352         }
2353         if (iob) {
2354             fail("Should have thrown IOB");
2355         }
2356     }
2357 
2358     @Test
2359     public void testSetElement() {
2360         Affine a = affine.clone();
2361         boolean is2d = affine.computeIs2D();
2362 
2363         assertSetElement(a, MatrixType.MT_2D_2x3, 0, 0, a.getMxx(), !is2d, false);
2364         assertSetElement(a, MatrixType.MT_2D_2x3, 0, 1, a.getMxy(), !is2d, false);
2365         assertSetElement(a, MatrixType.MT_2D_2x3, 0, 2, a.getTx(), !is2d, false);
2366         assertSetElement(a, MatrixType.MT_2D_2x3, 1, 0, a.getMyx(), !is2d, false);
2367         assertSetElement(a, MatrixType.MT_2D_2x3, 1, 1, a.getMyy(), !is2d, false);
2368         assertSetElement(a, MatrixType.MT_2D_2x3, 1, 2, a.getTy(), !is2d, false);
2369         assertSetElement(a, MatrixType.MT_2D_2x3, -1, 0, 0, !is2d, true);
2370         assertSetElement(a, MatrixType.MT_2D_2x3, 2, 0, -1000, !is2d, true);
2371         assertSetElement(a, MatrixType.MT_2D_2x3, 2, 1, -1000, !is2d, true);
2372         assertSetElement(a, MatrixType.MT_2D_2x3, 1, 3, 0, !is2d, true);
2373         assertSetElement(a, MatrixType.MT_2D_2x3, 0, -1, 0, !is2d, true);
2374         if (is2d) {
2375             assertAffineOk(new Affine(
2376                     affine.getMxx() + 1000,
2377                     affine.getMxy() + 1000,
2378                     affine.getMxz(),
2379                     affine.getTx() + 1000,
2380                     affine.getMyx() + 1000,
2381                     affine.getMyy() + 1000,
2382                     affine.getMyz(),
2383                     affine.getTy() + 1000,
2384                     affine.getMzx(),
2385                     affine.getMzy(),
2386                     affine.getMzz(),
2387                     affine.getTz()), a);
2388         }
2389 
2390         a = affine.clone();
2391         assertSetElement(a, MatrixType.MT_2D_3x3, 0, 0, a.getMxx(), !is2d, false);
2392         assertSetElement(a, MatrixType.MT_2D_3x3, 0, 1, a.getMxy(), !is2d, false);
2393         assertSetElement(a, MatrixType.MT_2D_3x3, 0, 2, a.getTx(), !is2d, false);
2394         assertSetElement(a, MatrixType.MT_2D_3x3, 1, 0, a.getMyx(), !is2d, false);
2395         assertSetElement(a, MatrixType.MT_2D_3x3, 1, 1, a.getMyy(), !is2d, false);
2396         assertSetElement(a, MatrixType.MT_2D_3x3, 1, 2, a.getTy(), !is2d, false);
2397         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 0, 0, true, false);
2398         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 0, -1000, !is2d, false);
2399         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 0, -999, true, false);
2400         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 0, 0, true, false);
2401         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 1, -1000, !is2d, false);
2402         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 1, -999, true, false);
2403         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 1, 0, true, false);
2404         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 2, -1000, true, false);
2405         assertSetElement(a, MatrixType.MT_2D_3x3, 2, 2, -999, !is2d, false);
2406         assertSetElement(a, MatrixType.MT_2D_3x3, -1, 0, 0, !is2d, true);
2407         assertSetElement(a, MatrixType.MT_2D_3x3, 3, 1, 0, !is2d, true);
2408         assertSetElement(a, MatrixType.MT_2D_3x3, 1, 3, 0, !is2d, true);
2409         assertSetElement(a, MatrixType.MT_2D_3x3, 1, -1, 0, !is2d, true);
2410         if (is2d) {
2411             assertAffineOk(new Affine(
2412                     affine.getMxx() + 1000,
2413                     affine.getMxy() + 1000,
2414                     affine.getMxz(),
2415                     affine.getTx() + 1000,
2416                     affine.getMyx() + 1000,
2417                     affine.getMyy() + 1000,
2418                     affine.getMyz(),
2419                     affine.getTy() + 1000,
2420                     affine.getMzx(),
2421                     affine.getMzy(),
2422                     affine.getMzz(),
2423                     affine.getTz()), a);
2424         }
2425 
2426         a = affine.clone();
2427         assertSetElement(a, MatrixType.MT_3D_3x4, 0, 0, a.getMxx(), false, false);
2428         assertSetElement(a, MatrixType.MT_3D_3x4, 0, 1, a.getMxy(), false, false);
2429         assertSetElement(a, MatrixType.MT_3D_3x4, 0, 2, a.getMxz(), false, false);
2430         assertSetElement(a, MatrixType.MT_3D_3x4, 0, 3, a.getTx(), false, false);
2431         assertSetElement(a, MatrixType.MT_3D_3x4, 1, 0, a.getMyx(), false, false);
2432         assertSetElement(a, MatrixType.MT_3D_3x4, 1, 1, a.getMyy(), false, false);
2433         assertSetElement(a, MatrixType.MT_3D_3x4, 1, 2, a.getMyz(), false, false);
2434         assertSetElement(a, MatrixType.MT_3D_3x4, 1, 3, a.getTy(), false, false);
2435         assertSetElement(a, MatrixType.MT_3D_3x4, 2, 0, a.getMzx(), false, false);
2436         assertSetElement(a, MatrixType.MT_3D_3x4, 2, 1, a.getMzy(), false, false);
2437         assertSetElement(a, MatrixType.MT_3D_3x4, 2, 2, a.getMzz(), false, false);
2438         assertSetElement(a, MatrixType.MT_3D_3x4, 2, 3, a.getTz(), false, false);
2439         assertSetElement(a, MatrixType.MT_3D_3x4, -1, 0, 0, false, true);
2440         assertSetElement(a, MatrixType.MT_3D_3x4, 3, 1, 0, false, true);
2441         assertSetElement(a, MatrixType.MT_3D_3x4, 1, 4, 0, false, true);
2442         assertSetElement(a, MatrixType.MT_3D_3x4, 1, -1, 0, false, true);
2443         assertAffineOk(new Affine(
2444                 affine.getMxx() + 1000,
2445                 affine.getMxy() + 1000,
2446                 affine.getMxz() + 1000,
2447                 affine.getTx() + 1000,
2448                 affine.getMyx() + 1000,
2449                 affine.getMyy() + 1000,
2450                 affine.getMyz() + 1000,
2451                 affine.getTy() + 1000,
2452                 affine.getMzx() + 1000,
2453                 affine.getMzy() + 1000,
2454                 affine.getMzz() + 1000,
2455                 affine.getTz() + 1000), a);
2456 
2457         a = affine.clone();
2458         assertSetElement(a, MatrixType.MT_3D_4x4, 0, 0, a.getMxx(), false, false);
2459         assertSetElement(a, MatrixType.MT_3D_4x4, 0, 1, a.getMxy(), false, false);
2460         assertSetElement(a, MatrixType.MT_3D_4x4, 0, 2, a.getMxz(), false, false);
2461         assertSetElement(a, MatrixType.MT_3D_4x4, 0, 3, a.getTx(), false, false);
2462         assertSetElement(a, MatrixType.MT_3D_4x4, 1, 0, a.getMyx(), false, false);
2463         assertSetElement(a, MatrixType.MT_3D_4x4, 1, 1, a.getMyy(), false, false);
2464         assertSetElement(a, MatrixType.MT_3D_4x4, 1, 2, a.getMyz(), false, false);
2465         assertSetElement(a, MatrixType.MT_3D_4x4, 1, 3, a.getTy(), false, false);
2466         assertSetElement(a, MatrixType.MT_3D_4x4, 2, 0, a.getMzx(), false, false);
2467         assertSetElement(a, MatrixType.MT_3D_4x4, 2, 1, a.getMzy(), false, false);
2468         assertSetElement(a, MatrixType.MT_3D_4x4, 2, 2, a.getMzz(), false, false);
2469         assertSetElement(a, MatrixType.MT_3D_4x4, 2, 3, a.getTz(), false, false);
2470         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 0, 0, true, false);
2471         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 0, -1000, false, false);
2472         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 0, -999, true, false);
2473         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 1, 0, true, false);
2474         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 1, -1000, false, false);
2475         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 1, -999, true, false);
2476         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 2, 0, true, false);
2477         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 2, -1000, false, false);
2478         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 2, -999, true, false);
2479         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 3, 0, true, false);
2480         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 3, -1000, true, false);
2481         assertSetElement(a, MatrixType.MT_3D_4x4, 3, 3, -999, false, false);
2482         assertSetElement(a, MatrixType.MT_3D_4x4, -1, 0, 0, false, true);
2483         assertSetElement(a, MatrixType.MT_3D_4x4, 4, 1, 0, false, true);
2484         assertSetElement(a, MatrixType.MT_3D_4x4, 1, 4, 0, false, true);
2485         assertSetElement(a, MatrixType.MT_3D_4x4, 0, -1, 0, false, true);
2486 
2487         assertAffineOk(new Affine(
2488                 affine.getMxx() + 1000,
2489                 affine.getMxy() + 1000,
2490                 affine.getMxz() + 1000,
2491                 affine.getTx() + 1000,
2492                 affine.getMyx() + 1000,
2493                 affine.getMyy() + 1000,
2494                 affine.getMyz() + 1000,
2495                 affine.getTy() + 1000,
2496                 affine.getMzx() + 1000,
2497                 affine.getMzy() + 1000,
2498                 affine.getMzz() + 1000,
2499                 affine.getTz() + 1000), a);
2500     }
2501 
2502     @Test(expected=NullPointerException.class)
2503     public void testSetElementNullType() {
2504         Affine a = affine.clone();
2505         a.setElement(null, 0, 0, 0);
2506     }
2507 
2508     @Test public void nonInvertibleExceptionShoudCancelAtomicOperation() {
2509         Affine a = affine.clone();
2510         try {
2511             a.invert();
2512         } catch (NonInvertibleTransformException e) {
2513             try {
2514                 a.append(a);
2515             } catch (Throwable t) {
2516                 fail("Internal state broken: " + t);
2517             }
2518         }
2519     }
2520 }