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