View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.geometry.euclidean.oned;
    18
    19  import java.util.function.UnaryOperator;
    20
    21  import org.apache.commons.geometry.core.GeometryTestUtils;
    22  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
    23  import org.apache.commons.numbers.angle.PlaneAngleRadians;
    24  import org.junit.Assert;
    25  import org.junit.Test;
    26
    27  public class AffineTransformMatrix1DTest {
    28
    29      private static final double EPS = 1e-12;
    30
    31      @Test
    32      public void testOf() {
    33          // act
    34          final AffineTransformMatrix1D transform = AffineTransformMatrix1D.of(1, 2);
    35
    36          // assert
    37          Assert.assertTrue(transform.preservesOrientation());
    38
    39          final double[] result = transform.toArray();
    40          Assert.assertArrayEquals(new double[] {1, 2}, result, 0.0);
    41      }
    42
    43      @Test
    44      public void testOf_invalidDimensions() {
    45          // act/assert
    46          GeometryTestUtils.assertThrows(() -> AffineTransformMatrix1D.of(1),
    47                  IllegalArgumentException.class, "Dimension mismatch: 1 != 2");
    48      }
    49
    50      @Test
    51      public void testFrom() {
    52          // act/assert
    53          Assert.assertArrayEquals(new double[] {1, 0},
    54                  AffineTransformMatrix1D.from(UnaryOperator.identity()).toArray(), EPS);
    55          Assert.assertArrayEquals(new double[] {1, 2},
    56                  AffineTransformMatrix1D.from(v -> v.add(Vector1D.of(2))).toArray(), EPS);
    57          Assert.assertArrayEquals(new double[] {3, 0},
    58                  AffineTransformMatrix1D.from(v -> v.multiply(3)).toArray(), EPS);
    59          Assert.assertArrayEquals(new double[] {3, 6},
    60                  AffineTransformMatrix1D.from(v -> v.add(Vector1D.of(2)).multiply(3)).toArray(), EPS);
    61      }
    62
    63      @Test
    64      public void testFrom_invalidFunction() {
    65          // act/assert
    66          GeometryTestUtils.assertThrows(() -> {
    67              AffineTransformMatrix1D.from(v -> v.multiply(0));
    68          }, IllegalArgumentException.class);
    69      }
    70
    71      @Test
    72      public void testIdentity() {
    73          // act
    74          final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity();
    75
    76          // assert
    77          Assert.assertTrue(transform.preservesOrientation());
    78
    79          final double[] expected = {1, 0};
    80          Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
    81      }
    82
    83      @Test
    84      public void testCreateTranslation_value() {
    85          // act
    86          final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createTranslation(2);
    87
    88          // assert
    89          Assert.assertTrue(transform.preservesOrientation());
    90
    91          final double[] expected = {1, 2};
    92          Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
    93      }
    94
    95      @Test
    96      public void testCreateTranslation_vector() {
    97          // act
    98          final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createTranslation(Vector1D.of(5));
    99
    100         // assert
    101         Assert.assertTrue(transform.preservesOrientation());
    102
    103         final double[] expected = {1, 5};
    104         Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
    105     }
    106
    107     @Test
    108     public void testTranslate_value() {
    109         // arrange
    110         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 10);
    111
    112         // act
    113         final AffineTransformMatrix1D result = a.translate(4);
    114
    115         // assert
    116         Assert.assertTrue(result.preservesOrientation());
    117
    118         final double[] expected = {2, 14};
    119         Assert.assertArrayEquals(expected, result.toArray(), 0.0);
    120     }
    121
    122     @Test
    123     public void testTranslate_vector() {
    124         // arrange
    125         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 10);
    126
    127         // act
    128         final AffineTransformMatrix1D result = a.translate(Vector1D.of(7));
    129
    130         // assert
    131         Assert.assertTrue(result.preservesOrientation());
    132
    133         final double[] expected = {2, 17};
    134         Assert.assertArrayEquals(expected, result.toArray(), 0.0);
    135     }
    136
    137     @Test
    138     public void testCreateScale_vector() {
    139         // act
    140         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(Vector1D.of(4));
    141
    142         // assert
    143         Assert.assertTrue(transform.preservesOrientation());
    144
    145         final double[] expected = {4, 0};
    146         Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
    147     }
    148
    149     @Test
    150     public void testCreateScale_value() {
    151         // act
    152         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(7);
    153
    154         // assert
    155         Assert.assertTrue(transform.preservesOrientation());
    156
    157         final double[] expected = {7, 0};
    158         Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
    159     }
    160
    161     @Test
    162     public void testScale_value() {
    163         // arrange
    164         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 10);
    165
    166         // act
    167         final AffineTransformMatrix1D result = a.scale(4);
    168
    169         // assert
    170         Assert.assertTrue(result.preservesOrientation());
    171
    172         final double[] expected = {8, 40};
    173         Assert.assertArrayEquals(expected, result.toArray(), 0.0);
    174     }
    175
    176     @Test
    177     public void testScale_vector() {
    178         // arrange
    179         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 10);
    180
    181         // act
    182         final AffineTransformMatrix1D result = a.scale(Vector1D.of(7));
    183
    184         // assert
    185         Assert.assertTrue(result.preservesOrientation());
    186
    187         final double[] expected = {14, 70};
    188         Assert.assertArrayEquals(expected, result.toArray(), 0.0);
    189     }
    190
    191     @Test
    192     public void testApply_identity() {
    193         // arrange
    194         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity();
    195
    196         // act/assert
    197         runWithCoordinates(x -> {
    198             final Vector1D v = Vector1D.of(x);
    199
    200             EuclideanTestUtils.assertCoordinatesEqual(v, transform.apply(v), EPS);
    201         });
    202     }
    203
    204     @Test
    205     public void testApply_translate() {
    206         // arrange
    207         final Vector1D translation = Vector1D.of(-PlaneAngleRadians.PI);
    208
    209         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    210                 .translate(translation);
    211
    212         // act/assert
    213         runWithCoordinates(x -> {
    214             final Vector1D vec = Vector1D.of(x);
    215
    216             final Vector1D expectedVec = vec.add(translation);
    217
    218             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
    219         });
    220     }
    221
    222     @Test
    223     public void testApply_scale() {
    224         // arrange
    225         final Vector1D factor = Vector1D.of(2.0);
    226
    227         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    228                 .scale(factor);
    229
    230         // act/assert
    231         runWithCoordinates(x -> {
    232             final Vector1D vec = Vector1D.of(x);
    233
    234             final Vector1D expectedVec = Vector1D.of(factor.getX() * x);
    235
    236             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
    237         });
    238     }
    239
    240     @Test
    241     public void testApply_translateThenScale() {
    242         // arrange
    243         final Vector1D translation = Vector1D.of(-2.0);
    244         final Vector1D scale = Vector1D.of(5.0);
    245
    246         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    247                 .translate(translation)
    248                 .scale(scale);
    249
    250         // act/assert
    251         EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(-5), transform.apply(Vector1D.of(1)), EPS);
    252
    253         runWithCoordinates(x -> {
    254             final Vector1D vec = Vector1D.of(x);
    255
    256             final Vector1D expectedVec = Vector1D.of(
    257                         (x + translation.getX()) * scale.getX()
    258                     );
    259
    260             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
    261         });
    262     }
    263
    264     @Test
    265     public void testApply_scaleThenTranslate() {
    266         // arrange
    267         final Vector1D scale = Vector1D.of(5.0);
    268         final Vector1D translation = Vector1D.of(-2.0);
    269
    270         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    271                 .scale(scale)
    272                 .translate(translation);
    273
    274         // act/assert
    275         runWithCoordinates(x -> {
    276             final Vector1D vec = Vector1D.of(x);
    277
    278             final Vector1D expectedVec = Vector1D.of(
    279                         (x * scale.getX()) + translation.getX()
    280                     );
    281
    282             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
    283         });
    284     }
    285
    286     @Test
    287     public void testApplyVector_identity() {
    288         // arrange
    289         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity();
    290
    291         // act/assert
    292         runWithCoordinates(x -> {
    293             final Vector1D v = Vector1D.of(x);
    294
    295             EuclideanTestUtils.assertCoordinatesEqual(v, transform.applyVector(v), EPS);
    296         });
    297     }
    298
    299     @Test
    300     public void testApplyVector_translate() {
    301         // arrange
    302         final Vector1D translation = Vector1D.of(-PlaneAngleRadians.PI);
    303
    304         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    305                 .translate(translation);
    306
    307         // act/assert
    308         runWithCoordinates(x -> {
    309             final Vector1D vec = Vector1D.of(x);
    310
    311             EuclideanTestUtils.assertCoordinatesEqual(vec, transform.applyVector(vec), EPS);
    312         });
    313     }
    314
    315     @Test
    316     public void testApplyVector_scale() {
    317         // arrange
    318         final Vector1D factor = Vector1D.of(2.0);
    319
    320         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    321                 .scale(factor);
    322
    323         // act/assert
    324         runWithCoordinates(x -> {
    325             final Vector1D vec = Vector1D.of(x);
    326
    327             final Vector1D expectedVec = Vector1D.of(factor.getX() * x);
    328
    329             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyVector(vec), EPS);
    330         });
    331     }
    332
    333     @Test
    334     public void testApplyVector_representsDisplacement() {
    335         // arrange
    336         final Vector1D p1 = Vector1D.of(PlaneAngleRadians.PI);
    337
    338         final Vector1D translation = Vector1D.of(-2.0);
    339         final Vector1D scale = Vector1D.of(5.0);
    340
    341         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    342                 .translate(translation)
    343                 .scale(scale);
    344
    345         // act/assert
    346         runWithCoordinates(x -> {
    347             final Vector1D p2 = Vector1D.of(x);
    348             final Vector1D input = p1.subtract(p2);
    349
    350             final Vector1D expectedVec = transform.apply(p1).subtract(transform.apply(p2));
    351
    352             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyVector(input), EPS);
    353         });
    354     }
    355
    356     @Test
    357     public void testApplyDirection_identity() {
    358         // arrange
    359         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity();
    360
    361         // act/assert
    362         runWithCoordinates(x -> {
    363             final Vector1D v = Vector1D.of(x);
    364
    365             EuclideanTestUtils.assertCoordinatesEqual(v.normalize(), transform.applyDirection(v), EPS);
    366         }, true);
    367     }
    368
    369     @Test
    370     public void testApplyDirection_translate() {
    371         // arrange
    372         final Vector1D translation = Vector1D.of(-PlaneAngleRadians.PI);
    373
    374         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    375                 .translate(translation);
    376
    377         // act/assert
    378         runWithCoordinates(x -> {
    379             final Vector1D vec = Vector1D.of(x);
    380
    381             EuclideanTestUtils.assertCoordinatesEqual(vec.normalize(), transform.applyDirection(vec), EPS);
    382         }, true);
    383     }
    384
    385     @Test
    386     public void testApplyDirection_scale() {
    387         // arrange
    388         final Vector1D factor = Vector1D.of(2.0);
    389
    390         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    391                 .scale(factor);
    392
    393         // act/assert
    394         runWithCoordinates(x -> {
    395             final Vector1D vec = Vector1D.of(x);
    396
    397             final Vector1D expectedVec = Vector1D.of(factor.getX() * x).normalize();
    398
    399             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyDirection(vec), EPS);
    400         }, true);
    401     }
    402
    403     @Test
    404     public void testApplyDirection_representsNormalizedDisplacement() {
    405         // arrange
    406         final Vector1D p1 = Vector1D.of(PlaneAngleRadians.PI);
    407
    408         final Vector1D translation = Vector1D.of(-2.0);
    409         final Vector1D scale = Vector1D.of(5.0);
    410
    411         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
    412                 .translate(translation)
    413                 .scale(scale);
    414
    415         // act/assert
    416         runWithCoordinates(x -> {
    417             final Vector1D p2 = Vector1D.of(x);
    418             final Vector1D input = p1.subtract(p2);
    419
    420             final Vector1D expectedVec = transform.apply(p1).subtract(transform.apply(p2)).normalize();
    421
    422             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyDirection(input), EPS);
    423         });
    424     }
    425
    426     @Test
    427     public void testApplyDirection_illegalNorm() {
    428         // act/assert
    429         GeometryTestUtils.assertThrows(() -> AffineTransformMatrix1D.createScale(0).applyDirection(Vector1D.Unit.PLUS),
    430                 IllegalArgumentException.class);
    431         GeometryTestUtils.assertThrows(() -> AffineTransformMatrix1D.createScale(2).applyDirection(Vector1D.ZERO),
    432                 IllegalArgumentException.class);
    433     }
    434
    435     @Test
    436     public void testDeterminant() {
    437         // act/assert
    438         Assert.assertEquals(0.0, AffineTransformMatrix1D.of(0, 1).determinant(), EPS);
    439         Assert.assertEquals(1.0, AffineTransformMatrix1D.of(1, 0).determinant(), EPS);
    440         Assert.assertEquals(-1.0, AffineTransformMatrix1D.of(-1, 2).determinant(), EPS);
    441     }
    442
    443     @Test
    444     public void testPreservesOrientation() {
    445         // act/assert
    446         Assert.assertFalse(AffineTransformMatrix1D.of(0, 1).preservesOrientation());
    447         Assert.assertTrue(AffineTransformMatrix1D.of(1, 0).preservesOrientation());
    448         Assert.assertFalse(AffineTransformMatrix1D.of(-1, 2).preservesOrientation());
    449     }
    450
    451     @Test
    452     public void testMultiply() {
    453         // arrange
    454         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 3);
    455         final AffineTransformMatrix1D b = AffineTransformMatrix1D.of(13, 14);
    456
    457         // act
    458         final AffineTransformMatrix1D result = a.multiply(b);
    459
    460         // assert
    461         final double[] arr = result.toArray();
    462         Assert.assertArrayEquals(new double[] {26, 31}, arr, EPS);
    463     }
    464
    465     @Test
    466     public void testMultiply_combinesTransformOperations() {
    467         // arrange
    468         final Vector1D translation1 = Vector1D.of(1);
    469         final double scale = 2.0;
    470         final Vector1D translation2 = Vector1D.of(4);
    471
    472         final AffineTransformMatrix1D a = AffineTransformMatrix1D.createTranslation(translation1);
    473         final AffineTransformMatrix1D b = AffineTransformMatrix1D.createScale(scale);
    474         final AffineTransformMatrix1D c = AffineTransformMatrix1D.identity();
    475         final AffineTransformMatrix1D d = AffineTransformMatrix1D.createTranslation(translation2);
    476
    477         // act
    478         final AffineTransformMatrix1D transform = d.multiply(c).multiply(b).multiply(a);
    479
    480         // assert
    481         runWithCoordinates(x -> {
    482             final Vector1D vec = Vector1D.of(x);
    483
    484             final Vector1D expectedVec = vec
    485                     .add(translation1)
    486                     .multiply(scale)
    487                     .add(translation2);
    488
    489             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
    490         });
    491     }
    492
    493     @Test
    494     public void testPremultiply() {
    495         // arrange
    496         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 3);
    497         final AffineTransformMatrix1D b = AffineTransformMatrix1D.of(13, 14);
    498
    499         // act
    500         final AffineTransformMatrix1D result = b.premultiply(a);
    501
    502         // assert
    503         final double[] arr = result.toArray();
    504         Assert.assertArrayEquals(new double[] {26, 31}, arr, EPS);
    505     }
    506
    507     @Test
    508     public void testPremultiply_combinesTransformOperations() {
    509         // arrange
    510         final Vector1D translation1 = Vector1D.of(1);
    511         final double scale = 2.0;
    512         final Vector1D translation2 = Vector1D.of(4);
    513
    514         final AffineTransformMatrix1D a = AffineTransformMatrix1D.createTranslation(translation1);
    515         final AffineTransformMatrix1D b = AffineTransformMatrix1D.createScale(scale);
    516         final AffineTransformMatrix1D c = AffineTransformMatrix1D.identity();
    517         final AffineTransformMatrix1D d = AffineTransformMatrix1D.createTranslation(translation2);
    518
    519         // act
    520         final AffineTransformMatrix1D transform = a.premultiply(b).premultiply(c).premultiply(d);
    521
    522         // assert
    523         runWithCoordinates(x -> {
    524             final Vector1D vec = Vector1D.of(x);
    525
    526             final Vector1D expectedVec = vec
    527                     .add(translation1)
    528                     .multiply(scale)
    529                     .add(translation2);
    530
    531             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
    532         });
    533     }
    534
    535     @Test
    536     public void testLinear() {
    537         // arrange
    538         final AffineTransformMatrix1D mat = AffineTransformMatrix1D.of(2, 3);
    539
    540         // act
    541         final AffineTransformMatrix1D result = mat.linear();
    542
    543         // assert
    544         Assert.assertArrayEquals(new double[] {2, 0}, result.toArray(), 0.0);
    545     }
    546
    547     @Test
    548     public void testLinearTranspose() {
    549         // arrange
    550         final AffineTransformMatrix1D mat = AffineTransformMatrix1D.of(2, 3);
    551
    552         // act
    553         final AffineTransformMatrix1D result = mat.linearTranspose();
    554
    555         // assert
    556         Assert.assertArrayEquals(new double[] {2, 0}, result.toArray(), 0.0);
    557     }
    558
    559     @Test
    560     public void testNormalTransform() {
    561         // act/assert
    562         checkNormalTransform(AffineTransformMatrix1D.identity());
    563
    564         checkNormalTransform(AffineTransformMatrix1D.createTranslation(4));
    565         checkNormalTransform(AffineTransformMatrix1D.createTranslation(-4));
    566
    567         checkNormalTransform(AffineTransformMatrix1D.createScale(2));
    568         checkNormalTransform(AffineTransformMatrix1D.createScale(-2));
    569
    570         checkNormalTransform(AffineTransformMatrix1D.createScale(2).translate(3));
    571         checkNormalTransform(AffineTransformMatrix1D.createScale(2).translate(-3));
    572         checkNormalTransform(AffineTransformMatrix1D.createTranslation(2).scale(-3));
    573         checkNormalTransform(AffineTransformMatrix1D.createTranslation(-4).scale(-1));
    574     }
    575
    576     private void checkNormalTransform(final AffineTransformMatrix1D transform) {
    577         final AffineTransformMatrix1D normalTransform = transform.normalTransform();
    578
    579         final Vector1D expectedPlus = transform.apply(Vector1D.Unit.PLUS)
    580                 .subtract(transform.apply(Vector1D.ZERO))
    581                 .normalize();
    582
    583         final Vector1D expectedMinus = transform.apply(Vector1D.Unit.MINUS)
    584                 .subtract(transform.apply(Vector1D.ZERO))
    585                 .normalize();
    586
    587         EuclideanTestUtils.assertCoordinatesEqual(expectedPlus,
    588                 normalTransform.apply(Vector1D.Unit.PLUS).normalize(), EPS);
    589         EuclideanTestUtils.assertCoordinatesEqual(expectedMinus,
    590                 normalTransform.apply(Vector1D.Unit.MINUS).normalize(), EPS);
    591     }
    592
    593     @Test
    594     public void testNormalTransform_nonInvertible() {
    595         // act/assert
    596         GeometryTestUtils.assertThrows(() -> {
    597             AffineTransformMatrix1D.createScale(0).normalTransform();
    598         }, IllegalStateException.class);
    599     }
    600
    601     @Test
    602     public void testInverse_identity() {
    603         // act
    604         final AffineTransformMatrix1D inverse = AffineTransformMatrix1D.identity().inverse();
    605
    606         // assert
    607         final double[] expected = {1, 0};
    608         Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
    609     }
    610
    611     @Test
    612     public void testInverse_multiplyByInverse_producesIdentity() {
    613         // arrange
    614         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(1, 3);
    615
    616         final AffineTransformMatrix1D inv = a.inverse();
    617
    618         // act
    619         final AffineTransformMatrix1D result = inv.multiply(a);
    620
    621         // assert
    622         final double[] expected = {1, 0};
    623         Assert.assertArrayEquals(expected, result.toArray(), EPS);
    624     }
    625
    626     @Test
    627     public void testInverse_translate() {
    628         // arrange
    629         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createTranslation(3);
    630
    631         // act
    632         final AffineTransformMatrix1D inverse = transform.inverse();
    633
    634         // assert
    635         final double[] expected = {1, -3};
    636         Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
    637     }
    638
    639     @Test
    640     public void testInverse_scale() {
    641         // arrange
    642         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(10);
    643
    644         // act
    645         final AffineTransformMatrix1D inverse = transform.inverse();
    646
    647         // assert
    648         final double[] expected = {0.1, 0};
    649         Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
    650     }
    651
    652     @Test
    653     public void testInverse_undoesOriginalTransform_translationAndScale() {
    654         // arrange
    655         final Vector1D v1 = Vector1D.ZERO;
    656         final Vector1D v2 = Vector1D.Unit.PLUS;
    657         final Vector1D v3 = Vector1D.of(1.5);
    658         final Vector1D v4 = Vector1D.of(-2);
    659
    660         // act/assert
    661         runWithCoordinates(x -> {
    662             final AffineTransformMatrix1D transform = AffineTransformMatrix1D
    663                         .createTranslation(x)
    664                         .scale(2)
    665                         .translate(x / 3);
    666
    667             final AffineTransformMatrix1D inverse = transform.inverse();
    668
    669             EuclideanTestUtils.assertCoordinatesEqual(v1, inverse.apply(transform.apply(v1)), EPS);
    670             EuclideanTestUtils.assertCoordinatesEqual(v2, inverse.apply(transform.apply(v2)), EPS);
    671             EuclideanTestUtils.assertCoordinatesEqual(v3, inverse.apply(transform.apply(v3)), EPS);
    672             EuclideanTestUtils.assertCoordinatesEqual(v4, inverse.apply(transform.apply(v4)), EPS);
    673         });
    674     }
    675
    676     @Test
    677     public void testInverse_nonInvertible() {
    678         // act/assert
    679         GeometryTestUtils.assertThrows(() -> {
    680             AffineTransformMatrix1D.of(0, 0).inverse();
    681         }, IllegalStateException.class, "Matrix is not invertible; matrix determinant is 0.0");
    682
    683         GeometryTestUtils.assertThrows(() -> {
    684             AffineTransformMatrix1D.of(Double.NaN, 0).inverse();
    685         }, IllegalStateException.class, "Matrix is not invertible; matrix determinant is NaN");
    686
    687         GeometryTestUtils.assertThrows(() -> {
    688             AffineTransformMatrix1D.of(Double.NEGATIVE_INFINITY, 0.0).inverse();
    689         }, IllegalStateException.class, "Matrix is not invertible; matrix determinant is -Infinity");
    690
    691         GeometryTestUtils.assertThrows(() -> {
    692             AffineTransformMatrix1D.of(Double.POSITIVE_INFINITY, 0).inverse();
    693         }, IllegalStateException.class, "Matrix is not invertible; matrix determinant is Infinity");
    694
    695         GeometryTestUtils.assertThrows(() -> {
    696             AffineTransformMatrix1D.of(1, Double.NaN).inverse();
    697         }, IllegalStateException.class, "Matrix is not invertible; invalid matrix element: NaN");
    698
    699         GeometryTestUtils.assertThrows(() -> {
    700             AffineTransformMatrix1D.of(1, Double.NEGATIVE_INFINITY).inverse();
    701         }, IllegalStateException.class, "Matrix is not invertible; invalid matrix element: -Infinity");
    702
    703         GeometryTestUtils.assertThrows(() -> {
    704             AffineTransformMatrix1D.of(1, Double.POSITIVE_INFINITY).inverse();
    705         }, IllegalStateException.class, "Matrix is not invertible; invalid matrix element: Infinity");
    706     }
    707
    708     @Test
    709     public void testHashCode() {
    710         // act
    711         final int orig = AffineTransformMatrix1D.of(1, 2).hashCode();
    712         final int same = AffineTransformMatrix1D.of(1, 2).hashCode();
    713
    714         // assert
    715         Assert.assertEquals(orig, same);
    716
    717         Assert.assertNotEquals(orig, AffineTransformMatrix1D.of(0, 2).hashCode());
    718         Assert.assertNotEquals(orig, AffineTransformMatrix1D.of(1, 0).hashCode());
    719     }
    720
    721     @Test
    722     public void testEquals() {
    723         // arrange
    724         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(1, 2);
    725
    726         // act/assert
    727         Assert.assertEquals(a, a);
    728
    729         Assert.assertFalse(a.equals(null));
    730         Assert.assertFalse(a.equals(new Object()));
    731
    732         Assert.assertNotEquals(a, AffineTransformMatrix1D.of(0, 2));
    733         Assert.assertNotEquals(a, AffineTransformMatrix1D.of(1, 0));
    734     }
    735
    736     @Test
    737     public void testEqualsAndHashCode_signedZeroConsistency() {
    738         // arrange
    739         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(0.0, -0.0);
    740         final AffineTransformMatrix1D b = AffineTransformMatrix1D.of(-0.0, 0.0);
    741         final AffineTransformMatrix1D c = AffineTransformMatrix1D.of(0.0, -0.0);
    742         final AffineTransformMatrix1D d = AffineTransformMatrix1D.of(-0.0, 0.0);
    743
    744         // act/assert
    745         Assert.assertFalse(a.equals(b));
    746
    747         Assert.assertTrue(a.equals(c));
    748         Assert.assertEquals(a.hashCode(), c.hashCode());
    749
    750         Assert.assertTrue(b.equals(d));
    751         Assert.assertEquals(b.hashCode(), d.hashCode());
    752     }
    753
    754     @Test
    755     public void testToString() {
    756         // arrange
    757         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(1, 2);
    758
    759         // act
    760         final String result = a.toString();
    761
    762         // assert
    763         Assert.assertEquals("[ 1.0, 2.0 ]", result);
    764     }
    765
    766     @FunctionalInterface
    767     private interface Coordinate1DTest {
    768
    769         void run(double x);
    770     }
    771
    772     private static void runWithCoordinates(final Coordinate1DTest test) {
    773         runWithCoordinates(test, false);
    774     }
    775
    776     private static void runWithCoordinates(final Coordinate1DTest test, final boolean skipZero) {
    777         runWithCoordinates(test, -1e-2, 1e-2, 5e-3, skipZero);
    778         runWithCoordinates(test, -1e2, 1e2, 5, skipZero);
    779     }
    780
    781     private static void runWithCoordinates(final Coordinate1DTest test, final double min, final double max, final double step, final boolean skipZero) {
    782         for (double x = min; x <= max; x += step) {
    783             if (!skipZero || x != 0.0) {
    784                 test.run(x);
    785             }
    786         }
    787     }
    788 }