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.spherical.oned;
    18
    19  import java.util.List;
    20
    21  import org.apache.commons.geometry.core.GeometryTestUtils;
    22  import org.apache.commons.geometry.core.RegionLocation;
    23  import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
    24  import org.apache.commons.geometry.core.partitioning.HyperplaneLocation;
    25  import org.apache.commons.geometry.core.partitioning.Split;
    26  import org.apache.commons.geometry.spherical.SphericalTestUtils;
    27  import org.apache.commons.numbers.angle.Angle;
    28  import org.apache.commons.numbers.core.Precision;
    29  import org.junit.jupiter.api.Assertions;
    30  import org.junit.jupiter.api.Test;
    31
    32  class CutAngleTest {
    33
    34      private static final double TEST_EPS = 1e-10;
    35
    36      private static final Precision.DoubleEquivalence TEST_PRECISION =
    37              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
    38
    39      @Test
    40      void testFromAzimuthAndDirection() {
    41          // act/assert
    42          checkCutAngle(CutAngles.fromAzimuthAndDirection(0.0, true, TEST_PRECISION),
    43                  0.0, true);
    44          checkCutAngle(CutAngles.fromAzimuthAndDirection(Math.PI, true, TEST_PRECISION),
    45                  Math.PI, true);
    46          checkCutAngle(CutAngles.fromAzimuthAndDirection(-Angle.PI_OVER_TWO, true, TEST_PRECISION),
    47                  -Angle.PI_OVER_TWO, true);
    48
    49          checkCutAngle(CutAngles.fromAzimuthAndDirection(0.0, false, TEST_PRECISION),
    50                  0.0, false);
    51          checkCutAngle(CutAngles.fromAzimuthAndDirection(Math.PI, false, TEST_PRECISION),
    52                  Math.PI, false);
    53          checkCutAngle(CutAngles.fromAzimuthAndDirection(-Angle.PI_OVER_TWO, false, TEST_PRECISION),
    54                  -Angle.PI_OVER_TWO, false);
    55      }
    56
    57      @Test
    58      void testFromPointAndDirection() {
    59          // arrange
    60          final Point1S pt = Point1S.of(-Angle.PI_OVER_TWO);
    61
    62          // act/assert
    63          checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION),
    64                  0.0, true);
    65          checkCutAngle(CutAngles.fromPointAndDirection(Point1S.PI, true, TEST_PRECISION),
    66                  Math.PI, true);
    67          checkCutAngle(CutAngles.fromPointAndDirection(pt, true, TEST_PRECISION),
    68                  -Angle.PI_OVER_TWO, true);
    69
    70          checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION),
    71                  0.0, false);
    72          checkCutAngle(CutAngles.fromPointAndDirection(Point1S.PI, false, TEST_PRECISION),
    73                  Math.PI, false);
    74          checkCutAngle(CutAngles.fromPointAndDirection(pt, false, TEST_PRECISION),
    75                  -Angle.PI_OVER_TWO, false);
    76      }
    77
    78      @Test
    79      void testCreatePositiveFacing() {
    80          // act/assert
    81          checkCutAngle(CutAngles.createPositiveFacing(Point1S.ZERO, TEST_PRECISION),
    82                  0.0, true);
    83          checkCutAngle(CutAngles.createPositiveFacing(Point1S.PI, TEST_PRECISION),
    84                  Math.PI, true);
    85          checkCutAngle(CutAngles.createPositiveFacing(-Angle.PI_OVER_TWO, TEST_PRECISION),
    86                  -Angle.PI_OVER_TWO, true);
    87      }
    88
    89      @Test
    90      void testCreateNegativeFacing() {
    91          // act/assert
    92          checkCutAngle(CutAngles.createNegativeFacing(Point1S.ZERO, TEST_PRECISION),
    93                  0.0, false);
    94          checkCutAngle(CutAngles.createNegativeFacing(Point1S.PI, TEST_PRECISION),
    95                  Math.PI, false);
    96          checkCutAngle(CutAngles.createNegativeFacing(-Angle.PI_OVER_TWO, TEST_PRECISION),
    97                  -Angle.PI_OVER_TWO, false);
    98      }
    99
    100     @Test
    101     void testOffset() {
    102         // arrange
    103         final CutAngle zeroPos = CutAngles.createPositiveFacing(0.0, TEST_PRECISION);
    104         final CutAngle zeroNeg = CutAngles.createNegativeFacing(0.0, TEST_PRECISION);
    105         final CutAngle negPiPos = CutAngles.createPositiveFacing(-Math.PI, TEST_PRECISION);
    106
    107         final CutAngle piNeg = CutAngles.createNegativeFacing(Math.PI, TEST_PRECISION);
    108         final CutAngle twoAndAHalfPiPos = CutAngles.createPositiveFacing(2.5 * Math.PI, TEST_PRECISION);
    109
    110         // act/assert
    111         checkOffset(zeroPos, 0, 0);
    112         checkOffset(zeroPos, Angle.TWO_PI, 0);
    113         checkOffset(zeroPos, 2.5 * Math.PI, Angle.PI_OVER_TWO);
    114         checkOffset(zeroPos, Math.PI, Math.PI);
    115         checkOffset(zeroPos, 3.5 * Math.PI, 1.5 * Math.PI);
    116
    117         checkOffset(zeroNeg, 0, 0);
    118         checkOffset(zeroNeg, Angle.TWO_PI, 0);
    119         checkOffset(zeroNeg, 2.5 * Math.PI, -Angle.PI_OVER_TWO);
    120         checkOffset(zeroNeg, Math.PI, -Math.PI);
    121         checkOffset(zeroNeg, 3.5 * Math.PI, -1.5 * Math.PI);
    122
    123         checkOffset(negPiPos, 0, -Math.PI);
    124         checkOffset(negPiPos, Angle.TWO_PI, -Math.PI);
    125         checkOffset(negPiPos, 2.5 * Math.PI, -Angle.PI_OVER_TWO);
    126         checkOffset(negPiPos, Math.PI, 0);
    127         checkOffset(negPiPos, 3.5 * Math.PI, Angle.PI_OVER_TWO);
    128
    129         checkOffset(piNeg, 0, Math.PI);
    130         checkOffset(piNeg, Angle.TWO_PI, Math.PI);
    131         checkOffset(piNeg, 2.5 * Math.PI, Angle.PI_OVER_TWO);
    132         checkOffset(piNeg, Math.PI, 0);
    133         checkOffset(piNeg, 3.5 * Math.PI, -Angle.PI_OVER_TWO);
    134
    135         checkOffset(twoAndAHalfPiPos, 0, -Angle.PI_OVER_TWO);
    136         checkOffset(twoAndAHalfPiPos, Angle.TWO_PI, -Angle.PI_OVER_TWO);
    137         checkOffset(twoAndAHalfPiPos, 2.5 * Math.PI, 0);
    138         checkOffset(twoAndAHalfPiPos, Math.PI, Angle.PI_OVER_TWO);
    139         checkOffset(twoAndAHalfPiPos, 3.5 * Math.PI, Math.PI);
    140     }
    141
    142     @Test
    143     void testClassify() {
    144         // arrange
    145         final CutAngle zeroPos = CutAngles.createPositiveFacing(0.0, TEST_PRECISION);
    146         final CutAngle zeroNeg = CutAngles.createNegativeFacing(0.0, TEST_PRECISION);
    147         final CutAngle negPiPos = CutAngles.createPositiveFacing(-Math.PI, TEST_PRECISION);
    148
    149         // act/assert
    150         checkClassify(zeroPos, HyperplaneLocation.ON,
    151                 0, 1e-16, -1e-16,
    152                 Angle.TWO_PI - 1e-11, Angle.TWO_PI + 1e-11);
    153         checkClassify(zeroPos, HyperplaneLocation.PLUS,
    154                 0.5, 2.5 * Math.PI,
    155                 -0.5, -Angle.PI_OVER_TWO);
    156
    157         checkClassify(zeroNeg, HyperplaneLocation.ON,
    158                 0, 1e-16, -1e-16,
    159                 Angle.TWO_PI - 1e-11, Angle.TWO_PI + 1e-11);
    160         checkClassify(zeroNeg, HyperplaneLocation.MINUS,
    161                 0.5, 2.5 * Math.PI,
    162                 -0.5, -Angle.PI_OVER_TWO);
    163
    164         checkClassify(negPiPos, HyperplaneLocation.ON, Math.PI, Math.PI + 1e-11);
    165         checkClassify(negPiPos, HyperplaneLocation.MINUS, 0.5, 2.5 * Math.PI,
    166                 0, 1e-11, Angle.TWO_PI, Angle.TWO_PI - 1e-11);
    167         checkClassify(negPiPos, HyperplaneLocation.PLUS, -0.5, -Angle.PI_OVER_TWO);
    168     }
    169
    170     @Test
    171     void testContains() {
    172         // arrange
    173         final CutAngle pt = CutAngles.createNegativeFacing(Angle.PI_OVER_TWO, TEST_PRECISION);
    174
    175         // act/assert
    176         Assertions.assertFalse(pt.contains(Point1S.ZERO));
    177         Assertions.assertFalse(pt.contains(Point1S.of(Angle.TWO_PI)));
    178
    179         Assertions.assertFalse(pt.contains(Point1S.of(Math.PI)));
    180         Assertions.assertFalse(pt.contains(Point1S.of(0.25 * Math.PI)));
    181         Assertions.assertFalse(pt.contains(Point1S.of(-0.25 * Math.PI)));
    182
    183         Assertions.assertTrue(pt.contains(Point1S.of(Angle.PI_OVER_TWO)));
    184         Assertions.assertTrue(pt.contains(Point1S.of(Angle.PI_OVER_TWO + 1e-11)));
    185         Assertions.assertTrue(pt.contains(Point1S.of(2.5 * Math.PI)));
    186         Assertions.assertTrue(pt.contains(Point1S.of(-3.5 * Math.PI)));
    187     }
    188
    189     @Test
    190     void testReverse() {
    191         // arrange
    192         final CutAngle pt = CutAngles.createNegativeFacing(Angle.PI_OVER_TWO, TEST_PRECISION);
    193
    194         // act
    195         final CutAngle result = pt.reverse();
    196
    197         // assert
    198         checkCutAngle(result, Angle.PI_OVER_TWO, true);
    199         Assertions.assertSame(TEST_PRECISION, result.getPrecision());
    200
    201         checkCutAngle(result.reverse(), Angle.PI_OVER_TWO, false);
    202     }
    203
    204     @Test
    205     void testProject() {
    206         // arrange
    207         final CutAngle pt = CutAngles.createNegativeFacing(Angle.PI_OVER_TWO, TEST_PRECISION);
    208
    209         // act/assert
    210         for (double az = -Angle.TWO_PI; az <= Angle.TWO_PI; az += 0.2) {
    211             Assertions.assertEquals(Angle.PI_OVER_TWO, pt.project(Point1S.of(az)).getAzimuth(), TEST_EPS);
    212         }
    213     }
    214
    215     @Test
    216     void testSimilarOrientation() {
    217         // arrange
    218         final CutAngle a = CutAngles.createPositiveFacing(0.0, TEST_PRECISION);
    219         final CutAngle b = CutAngles.createNegativeFacing(0.0, TEST_PRECISION);
    220         final CutAngle c = CutAngles.createPositiveFacing(-Angle.PI_OVER_TWO, TEST_PRECISION);
    221
    222         // act/assert
    223         Assertions.assertTrue(a.similarOrientation(a));
    224         Assertions.assertFalse(a.similarOrientation(b));
    225         Assertions.assertTrue(a.similarOrientation(c));
    226     }
    227
    228     @Test
    229     void testTransform_rotate() {
    230         // arrange
    231         final Transform1S transform = Transform1S.createRotation(Angle.PI_OVER_TWO);
    232
    233         // act
    234         checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION).transform(transform),
    235                 Angle.PI_OVER_TWO, true);
    236         checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION).transform(transform),
    237                 Angle.PI_OVER_TWO, false);
    238
    239         checkCutAngle(CutAngles.fromPointAndDirection(Point1S.of(1.5 * Math.PI), true, TEST_PRECISION).transform(transform),
    240                 Angle.TWO_PI, true);
    241         checkCutAngle(CutAngles.fromPointAndDirection(Point1S.of(-Angle.PI_OVER_TWO), false, TEST_PRECISION).transform(transform),
    242                 0.0, false);
    243     }
    244
    245     @Test
    246     void testTransform_negate() {
    247         // arrange
    248         final Transform1S transform = Transform1S.createNegation();
    249
    250         // act
    251         checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION).transform(transform),
    252                 0.0, false);
    253         checkCutAngle(CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION).transform(transform),
    254                 0.0, true);
    255
    256         checkCutAngle(CutAngles.fromPointAndDirection(Point1S.of(1.5 * Math.PI), true, TEST_PRECISION).transform(transform),
    257                 -1.5 * Math.PI, false);
    258         checkCutAngle(CutAngles.fromPointAndDirection(Point1S.of(-Angle.PI_OVER_TWO), false, TEST_PRECISION).transform(transform),
    259                 Angle.PI_OVER_TWO, true);
    260     }
    261
    262     @Test
    263     void testSpan() {
    264         // arrange
    265         final CutAngle pt = CutAngles.fromPointAndDirection(Point1S.of(1.0), false, TEST_PRECISION);
    266
    267         // act
    268         final HyperplaneConvexSubset<Point1S> result = pt.span();
    269
    270         // assert
    271         Assertions.assertSame(pt, result.getHyperplane());
    272     }
    273
    274     @Test
    275     void testEq() {
    276         // arrange
    277         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
    278
    279         final CutAngle a = CutAngles.fromPointAndDirection(Point1S.ZERO, true, precision);
    280
    281         final CutAngle b = CutAngles.fromPointAndDirection(Point1S.PI, true, precision);
    282         final CutAngle c = CutAngles.fromPointAndDirection(Point1S.ZERO, false, precision);
    283         final CutAngle d = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
    284
    285         final CutAngle e = CutAngles.fromPointAndDirection(Point1S.ZERO, true, precision);
    286         final CutAngle f = CutAngles.fromPointAndDirection(Point1S.of(Angle.TWO_PI), true, precision);
    287         final CutAngle g = CutAngles.fromPointAndDirection(Point1S.of(1e-4), true, precision);
    288         final CutAngle h = CutAngles.fromPointAndDirection(Point1S.of(-1e-4), true, precision);
    289
    290         // act/assert
    291         Assertions.assertTrue(a.eq(a, precision));
    292
    293         Assertions.assertFalse(a.eq(b, precision));
    294         Assertions.assertFalse(a.eq(c, precision));
    295
    296         Assertions.assertTrue(a.eq(d, precision));
    297         Assertions.assertTrue(a.eq(e, precision));
    298         Assertions.assertTrue(a.eq(f, precision));
    299         Assertions.assertTrue(a.eq(g, precision));
    300         Assertions.assertTrue(a.eq(h, precision));
    301     }
    302
    303     @Test
    304     void testHashCode() {
    305         // arrange
    306         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
    307
    308         final CutAngle a = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
    309         final CutAngle b = CutAngles.fromPointAndDirection(Point1S.PI, true, TEST_PRECISION);
    310         final CutAngle c = CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION);
    311         final CutAngle d = CutAngles.fromPointAndDirection(Point1S.ZERO, true, precision);
    312         final CutAngle e = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
    313
    314         final int hash = a.hashCode();
    315
    316         // act/assert
    317         Assertions.assertEquals(hash, a.hashCode());
    318
    319         Assertions.assertNotEquals(hash, b.hashCode());
    320         Assertions.assertNotEquals(hash, c.hashCode());
    321         Assertions.assertNotEquals(hash, d.hashCode());
    322
    323         Assertions.assertEquals(hash, e.hashCode());
    324     }
    325
    326     @Test
    327     void testEquals() {
    328         // arrange
    329         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
    330
    331         final CutAngle a = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
    332         final CutAngle b = CutAngles.fromPointAndDirection(Point1S.PI, true, TEST_PRECISION);
    333         final CutAngle c = CutAngles.fromPointAndDirection(Point1S.ZERO, false, TEST_PRECISION);
    334         final CutAngle d = CutAngles.fromPointAndDirection(Point1S.ZERO, true, precision);
    335         final CutAngle e = CutAngles.fromPointAndDirection(Point1S.ZERO, true, TEST_PRECISION);
    336
    337         // act/assert
    338         GeometryTestUtils.assertSimpleEqualsCases(a);
    339
    340         Assertions.assertNotEquals(a, b);
    341         Assertions.assertNotEquals(a, c);
    342         Assertions.assertNotEquals(a, d);
    343
    344         Assertions.assertEquals(a, e);
    345     }
    346
    347     @Test
    348     void testToString() {
    349         // arrange
    350         final CutAngle pt = CutAngles.createPositiveFacing(0.0, TEST_PRECISION);
    351
    352         // act
    353         final String str = pt.toString();
    354
    355         // assert
    356         Assertions.assertTrue(str.startsWith("CutAngle["));
    357         Assertions.assertTrue(str.contains("point= ") && str.contains("positiveFacing= "));
    358     }
    359
    360     @Test
    361     void testSubset_split() {
    362         // arrange
    363         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-3);
    364
    365         final CutAngle pt = CutAngles.createPositiveFacing(-1.5, precision);
    366         final HyperplaneConvexSubset<Point1S> sub = pt.span();
    367
    368         // act/assert
    369         checkSplit(sub, CutAngles.createPositiveFacing(1.0, precision), false, true);
    370         checkSplit(sub, CutAngles.createPositiveFacing(-1.5 + 1e-2, precision), true, false);
    371
    372         checkSplit(sub, CutAngles.createNegativeFacing(1.0, precision), true, false);
    373         checkSplit(sub, CutAngles.createNegativeFacing(-1.5 + 1e-2, precision), false, true);
    374
    375         checkSplit(sub, CutAngles.createNegativeFacing(-1.5, precision), false, false);
    376         checkSplit(sub, CutAngles.createNegativeFacing(-1.5 + 1e-4, precision), false, false);
    377         checkSplit(sub, CutAngles.createNegativeFacing(-1.5 - 1e-4, precision), false, false);
    378     }
    379
    380     private void checkSplit(final HyperplaneConvexSubset<Point1S> sub, final CutAngle splitter, final boolean minus, final boolean plus) {
    381         final Split<? extends HyperplaneConvexSubset<Point1S>> split = sub.split(splitter);
    382
    383         Assertions.assertSame(minus ? sub : null, split.getMinus());
    384         Assertions.assertSame(plus ? sub : null, split.getPlus());
    385     }
    386
    387     @Test
    388     void testSubset_simpleMethods() {
    389         // arrange
    390         final CutAngle pt = CutAngles.createPositiveFacing(1, TEST_PRECISION);
    391         final HyperplaneConvexSubset<Point1S> sub = pt.span();
    392
    393         // act/assert
    394         Assertions.assertSame(pt, sub.getHyperplane());
    395         Assertions.assertFalse(sub.isFull());
    396         Assertions.assertFalse(sub.isEmpty());
    397         Assertions.assertFalse(sub.isInfinite());
    398         Assertions.assertTrue(sub.isFinite());
    399         Assertions.assertEquals(0.0, sub.getSize(), TEST_EPS);
    400         SphericalTestUtils.assertPointsEqual(Point1S.of(1), sub.getCentroid(), TEST_EPS);
    401
    402         final List<? extends HyperplaneConvexSubset<Point1S>> list = sub.toConvex();
    403         Assertions.assertEquals(1, list.size());
    404         Assertions.assertSame(sub, list.get(0));
    405     }
    406
    407     @Test
    408     void testSubset_classify() {
    409         // arrange
    410         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-1);
    411         final CutAngle pt = CutAngles.createPositiveFacing(1, precision);
    412         final HyperplaneConvexSubset<Point1S> sub = pt.span();
    413
    414         // act/assert
    415         Assertions.assertEquals(RegionLocation.BOUNDARY, sub.classify(Point1S.of(0.95)));
    416         Assertions.assertEquals(RegionLocation.BOUNDARY, sub.classify(Point1S.of(1)));
    417         Assertions.assertEquals(RegionLocation.BOUNDARY, sub.classify(Point1S.of(1.05)));
    418
    419         Assertions.assertEquals(RegionLocation.OUTSIDE, sub.classify(Point1S.of(1.11)));
    420         Assertions.assertEquals(RegionLocation.OUTSIDE, sub.classify(Point1S.of(0.89)));
    421
    422         Assertions.assertEquals(RegionLocation.OUTSIDE, sub.classify(Point1S.of(-3)));
    423         Assertions.assertEquals(RegionLocation.OUTSIDE, sub.classify(Point1S.of(10)));
    424     }
    425
    426     @Test
    427     void testSubset_contains() {
    428         // arrange
    429         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-1);
    430         final CutAngle pt = CutAngles.createPositiveFacing(1, precision);
    431         final HyperplaneConvexSubset<Point1S> sub = pt.span();
    432
    433         // act/assert
    434         Assertions.assertTrue(sub.contains(Point1S.of(0.95)));
    435         Assertions.assertTrue(sub.contains(Point1S.of(1)));
    436         Assertions.assertTrue(sub.contains(Point1S.of(1.05)));
    437
    438         Assertions.assertFalse(sub.contains(Point1S.of(1.11)));
    439         Assertions.assertFalse(sub.contains(Point1S.of(0.89)));
    440
    441         Assertions.assertFalse(sub.contains(Point1S.of(-3)));
    442         Assertions.assertFalse(sub.contains(Point1S.of(10)));
    443     }
    444
    445     @Test
    446     void testSubset_closestContained() {
    447         // arrange
    448         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-1);
    449         final CutAngle pt = CutAngles.createPositiveFacing(1, precision);
    450         final HyperplaneConvexSubset<Point1S> sub = pt.span();
    451
    452         final Point1S expected = Point1S.of(1);
    453
    454         // act/assert
    455         Assertions.assertEquals(expected, sub.closest(Point1S.ZERO));
    456         Assertions.assertEquals(expected, sub.closest(Point1S.of(Angle.PI_OVER_TWO)));
    457         Assertions.assertEquals(expected, sub.closest(Point1S.PI));
    458         Assertions.assertEquals(expected, sub.closest(Point1S.of(-Angle.PI_OVER_TWO)));
    459         Assertions.assertEquals(expected, sub.closest(Point1S.of(Angle.TWO_PI)));
    460     }
    461
    462     @Test
    463     void testSubset_transform() {
    464         // arrange
    465         final CutAngle pt = CutAngles.fromPointAndDirection(Point1S.of(Angle.PI_OVER_TWO), true, TEST_PRECISION);
    466
    467         final Transform1S transform = Transform1S.createNegation().rotate(Math.PI);
    468
    469         // act
    470         final HyperplaneConvexSubset<Point1S> result = pt.span().transform(transform);
    471
    472         // assert
    473         checkCutAngle((CutAngle) result.getHyperplane(), Angle.PI_OVER_TWO, false);
    474     }
    475
    476     @Test
    477     void testSubset_reverse() {
    478         // arrange
    479         final CutAngle pt = CutAngles.createPositiveFacing(2.0, TEST_PRECISION);
    480         final HyperplaneConvexSubset<Point1S> sub = pt.span();
    481
    482         // act
    483         final HyperplaneConvexSubset<Point1S> result = sub.reverse();
    484
    485         // assert
    486         Assertions.assertEquals(2.0, ((CutAngle) result.getHyperplane()).getAzimuth(), TEST_EPS);
    487         Assertions.assertFalse(((CutAngle) result.getHyperplane()).isPositiveFacing());
    488
    489         Assertions.assertEquals(sub.getHyperplane(), result.reverse().getHyperplane());
    490     }
    491
    492     @Test
    493     void testSubset_toString() {
    494         // arrange
    495         final CutAngle pt = CutAngles.createPositiveFacing(2, TEST_PRECISION);
    496         final HyperplaneConvexSubset<Point1S> sub = pt.span();
    497
    498         // act
    499         final String str = sub.toString();
    500
    501         //assert
    502         Assertions.assertTrue(str.contains("CutAngleConvexSubset["));
    503         Assertions.assertTrue(str.contains("point= "));
    504         Assertions.assertTrue(str.contains("positiveFacing= "));
    505     }
    506
    507     private static void checkCutAngle(final CutAngle angle, final double az, final boolean positiveFacing) {
    508         checkCutAngle(angle, az, positiveFacing, TEST_PRECISION);
    509     }
    510
    511     private static void checkCutAngle(final CutAngle angle, final double az, final boolean positiveFacing, final Precision.DoubleEquivalence precision) {
    512         Assertions.assertEquals(az, angle.getAzimuth(), TEST_EPS);
    513         Assertions.assertEquals(Angle.Rad.WITHIN_0_AND_2PI.applyAsDouble(az), angle.getNormalizedAzimuth(), TEST_EPS);
    514         Assertions.assertEquals(az, angle.getPoint().getAzimuth(), TEST_EPS);
    515         Assertions.assertEquals(positiveFacing, angle.isPositiveFacing());
    516
    517         Assertions.assertSame(precision, angle.getPrecision());
    518     }
    519
    520     private static void checkOffset(final CutAngle pt, final double az, final double offset) {
    521         Assertions.assertEquals(offset, pt.offset(Point1S.of(az)), TEST_EPS);
    522     }
    523
    524     private static void checkClassify(final CutAngle pt, final HyperplaneLocation loc, final double... azimuths) {
    525         for (final double az : azimuths) {
    526             Assertions.assertEquals(loc, pt.classify(Point1S.of(az)), "Unexpected location for azimuth " + az);
    527         }
    528     }
    529 }