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