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.twod;
    18
    19  import java.util.Arrays;
    20  import java.util.Collection;
    21  import java.util.Collections;
    22  import java.util.Iterator;
    23  import java.util.List;
    24  import java.util.regex.Pattern;
    25  import java.util.stream.Collectors;
    26
    27  import org.apache.commons.geometry.core.GeometryTestUtils;
    28  import org.apache.commons.geometry.core.RegionLocation;
    29  import org.apache.commons.geometry.euclidean.threed.Vector3D;
    30  import org.apache.commons.geometry.spherical.SphericalTestUtils;
    31  import org.apache.commons.numbers.angle.Angle;
    32  import org.apache.commons.numbers.core.Precision;
    33  import org.junit.jupiter.api.Assertions;
    34  import org.junit.jupiter.api.Test;
    35
    36  class GreatArcPathTest {
    37
    38      private static final double TEST_EPS = 1e-10;
    39
    40      private static final Precision.DoubleEquivalence TEST_PRECISION =
    41              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
    42
    43      @Test
    44      void testEmpty() {
    45          // act
    46          final GreatArcPath path = GreatArcPath.empty();
    47
    48          // assert
    49          Assertions.assertTrue(path.isEmpty());
    50          Assertions.assertFalse(path.isClosed());
    51
    52          Assertions.assertNull(path.getStartVertex());
    53          Assertions.assertNull(path.getEndVertex());
    54
    55          Assertions.assertNull(path.getStartArc());
    56          Assertions.assertNull(path.getEndArc());
    57
    58          Assertions.assertEquals(0, path.getArcs().size());
    59          Assertions.assertEquals(0, path.getVertices().size());
    60      }
    61
    62      @Test
    63      void testFromVertices_boolean_empty() {
    64          // act
    65          final GreatArcPath path = GreatArcPath.fromVertices(Collections.emptyList(), true, TEST_PRECISION);
    66
    67          // assert
    68          Assertions.assertTrue(path.isEmpty());
    69
    70          Assertions.assertNull(path.getStartVertex());
    71          Assertions.assertNull(path.getEndVertex());
    72
    73          Assertions.assertNull(path.getStartArc());
    74          Assertions.assertNull(path.getEndArc());
    75
    76          Assertions.assertEquals(0, path.getArcs().size());
    77          Assertions.assertEquals(0, path.getVertices().size());
    78      }
    79
    80      @Test
    81      void testFromVertices_boolean_notClosed() {
    82          // arrange
    83          final List<Point2S> points = Arrays.asList(
    84                  Point2S.PLUS_I,
    85                  Point2S.PLUS_K,
    86                  Point2S.PLUS_J);
    87
    88          // act
    89          final GreatArcPath path = GreatArcPath.fromVertices(points, false, TEST_PRECISION);
    90
    91          // assert
    92          Assertions.assertFalse(path.isEmpty());
    93          Assertions.assertFalse(path.isClosed());
    94
    95          SphericalTestUtils.assertPointsEq(Point2S.PLUS_I, path.getStartVertex(), TEST_EPS);
    96          SphericalTestUtils.assertPointsEq(Point2S.PLUS_J, path.getEndVertex(), TEST_EPS);
    97
    98          final List<GreatArc> arcs = path.getArcs();
    99          Assertions.assertEquals(2, arcs.size());
    100         assertArc(arcs.get(0), Point2S.PLUS_I, Point2S.PLUS_K);
    101         assertArc(arcs.get(1), Point2S.PLUS_K, Point2S.PLUS_J);
    102
    103         assertPoints(points, path.getVertices());
    104     }
    105
    106     @Test
    107     void testFromVertices_boolean_closed() {
    108         // arrange
    109         final List<Point2S> points = Arrays.asList(
    110                 Point2S.PLUS_I,
    111                 Point2S.PLUS_K,
    112                 Point2S.PLUS_J);
    113
    114         // act
    115         final GreatArcPath path = GreatArcPath.fromVertices(points, true, TEST_PRECISION);
    116
    117         // assert
    118         Assertions.assertFalse(path.isEmpty());
    119         Assertions.assertTrue(path.isClosed());
    120
    121         SphericalTestUtils.assertPointsEq(Point2S.PLUS_I, path.getStartVertex(), TEST_EPS);
    122         SphericalTestUtils.assertPointsEq(Point2S.PLUS_I, path.getEndVertex(), TEST_EPS);
    123
    124         final List<GreatArc> arcs = path.getArcs();
    125         Assertions.assertEquals(3, arcs.size());
    126         assertArc(arcs.get(0), Point2S.PLUS_I, Point2S.PLUS_K);
    127         assertArc(arcs.get(1), Point2S.PLUS_K, Point2S.PLUS_J);
    128         assertArc(arcs.get(2), Point2S.PLUS_J, Point2S.PLUS_I);
    129
    130         assertPoints(Arrays.asList(
    131                 Point2S.PLUS_I,
    132                 Point2S.PLUS_K,
    133                 Point2S.PLUS_J,
    134                 Point2S.PLUS_I), path.getVertices());
    135     }
    136
    137     @Test
    138     void testFromVertices_boolean_closed_pointsConsideredEqual() {
    139         // arrange
    140         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
    141
    142         final Point2S almostPlusI = Point2S.of(1e-4, Angle.PI_OVER_TWO);
    143
    144         final List<Point2S> points = Arrays.asList(
    145                 Point2S.PLUS_I,
    146                 Point2S.PLUS_K,
    147                 Point2S.PLUS_J,
    148                 almostPlusI);
    149
    150         // act
    151         final GreatArcPath path = GreatArcPath.fromVertices(points, true, precision);
    152
    153         // assert
    154         Assertions.assertFalse(path.isEmpty());
    155         Assertions.assertTrue(path.isClosed());
    156
    157         SphericalTestUtils.assertPointsEq(Point2S.PLUS_I, path.getStartVertex(), TEST_EPS);
    158         SphericalTestUtils.assertPointsEq(almostPlusI, path.getEndVertex(), TEST_EPS);
    159
    160         final List<GreatArc> arcs = path.getArcs();
    161         Assertions.assertEquals(3, arcs.size());
    162         assertArc(arcs.get(0), Point2S.PLUS_I, Point2S.PLUS_K);
    163         assertArc(arcs.get(1), Point2S.PLUS_K, Point2S.PLUS_J);
    164         assertArc(arcs.get(2), Point2S.PLUS_J, almostPlusI);
    165
    166         assertPoints(Arrays.asList(
    167                 Point2S.PLUS_I,
    168                 Point2S.PLUS_K,
    169                 Point2S.PLUS_J,
    170                 almostPlusI), path.getVertices());
    171     }
    172
    173     @Test
    174     void testFromVertices() {
    175         // arrange
    176         final List<Point2S> points = Arrays.asList(
    177                 Point2S.MINUS_I,
    178                 Point2S.MINUS_J,
    179                 Point2S.PLUS_I);
    180
    181         // act
    182         final GreatArcPath path = GreatArcPath.fromVertices(points, TEST_PRECISION);
    183
    184         // assert
    185         Assertions.assertFalse(path.isEmpty());
    186         Assertions.assertFalse(path.isClosed());
    187
    188         SphericalTestUtils.assertPointsEq(Point2S.MINUS_I, path.getStartVertex(), TEST_EPS);
    189         SphericalTestUtils.assertPointsEq(Point2S.PLUS_I, path.getEndVertex(), TEST_EPS);
    190
    191         final List<GreatArc> arcs = path.getArcs();
    192         Assertions.assertEquals(2, arcs.size());
    193         assertArc(arcs.get(0), Point2S.MINUS_I, Point2S.MINUS_J);
    194         assertArc(arcs.get(1), Point2S.MINUS_J, Point2S.PLUS_I);
    195
    196         assertPoints(points, path.getVertices());
    197     }
    198
    199     @Test
    200     void testFromVertexLoop() {
    201         // arrange
    202         final List<Point2S> points = Arrays.asList(
    203                 Point2S.MINUS_I,
    204                 Point2S.MINUS_J,
    205                 Point2S.MINUS_K);
    206
    207         // act
    208         final GreatArcPath path = GreatArcPath.fromVertexLoop(points, TEST_PRECISION);
    209
    210         // assert
    211         Assertions.assertFalse(path.isEmpty());
    212         Assertions.assertTrue(path.isClosed());
    213
    214         SphericalTestUtils.assertPointsEq(Point2S.MINUS_I, path.getStartVertex(), TEST_EPS);
    215         SphericalTestUtils.assertPointsEq(Point2S.MINUS_I, path.getEndVertex(), TEST_EPS);
    216
    217         final List<GreatArc> arcs = path.getArcs();
    218         Assertions.assertEquals(3, arcs.size());
    219         assertArc(arcs.get(0), Point2S.MINUS_I, Point2S.MINUS_J);
    220         assertArc(arcs.get(1), Point2S.MINUS_J, Point2S.MINUS_K);
    221         assertArc(arcs.get(2), Point2S.MINUS_K, Point2S.MINUS_I);
    222
    223         assertPoints(Arrays.asList(
    224                 Point2S.MINUS_I,
    225                 Point2S.MINUS_J,
    226                 Point2S.MINUS_K,
    227                 Point2S.MINUS_I), path.getVertices());
    228     }
    229
    230     @Test
    231     void testFromArcs() {
    232         // arrange
    233         final Point2S ptA = Point2S.PLUS_I;
    234         final Point2S ptB = Point2S.of(1, Angle.PI_OVER_TWO);
    235         final Point2S ptC = Point2S.of(1, Angle.PI_OVER_TWO - 1);
    236         final Point2S ptD = Point2S.of(2, Angle.PI_OVER_TWO - 1);
    237
    238         final GreatArc a = GreatCircles.arcFromPoints(ptA, ptB, TEST_PRECISION);
    239         final GreatArc b = GreatCircles.arcFromPoints(ptB, ptC, TEST_PRECISION);
    240         final GreatArc c = GreatCircles.arcFromPoints(ptC, ptD, TEST_PRECISION);
    241
    242         // act
    243         final GreatArcPath path = GreatArcPath.fromArcs(a, b, c);
    244
    245         // assert
    246         Assertions.assertFalse(path.isEmpty());
    247         Assertions.assertFalse(path.isClosed());
    248
    249         SphericalTestUtils.assertPointsEq(ptA, path.getStartVertex(), TEST_EPS);
    250         SphericalTestUtils.assertPointsEq(ptD, path.getEndVertex(), TEST_EPS);
    251
    252         final List<GreatArc> arcs = path.getArcs();
    253         Assertions.assertEquals(3, arcs.size());
    254         assertArc(arcs.get(0), ptA, ptB);
    255         assertArc(arcs.get(1), ptB, ptC);
    256         assertArc(arcs.get(2), ptC, ptD);
    257
    258         assertPoints(Arrays.asList(ptA, ptB, ptC, ptD), path.getVertices());
    259     }
    260
    261     @Test
    262     void testFromArcs_full() {
    263         // arrange
    264         final GreatArc fullArc = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION).span();
    265
    266         // act
    267         final GreatArcPath path = GreatArcPath.fromArcs(fullArc);
    268
    269         // assert
    270         Assertions.assertFalse(path.isEmpty());
    271         Assertions.assertFalse(path.isClosed());
    272
    273         Assertions.assertSame(fullArc, path.getStartArc());
    274         Assertions.assertSame(fullArc, path.getEndArc());
    275
    276         Assertions.assertNull(path.getStartVertex());
    277         Assertions.assertNull(path.getEndVertex());
    278
    279         final List<GreatArc> arcs = path.getArcs();
    280         Assertions.assertEquals(1, arcs.size());
    281
    282         Assertions.assertSame(fullArc, arcs.get(0));
    283     }
    284
    285     @Test
    286     void testBoundaryStream() {
    287         // arrange
    288         final GreatArc fullArc = GreatCircles.fromPole(Vector3D.Unit.PLUS_X, TEST_PRECISION).span();
    289         final GreatArcPath path = GreatArcPath.fromArcs(fullArc);
    290
    291         // act
    292         final List<GreatArc> arcs = path.boundaryStream().collect(Collectors.toList());
    293
    294         // assert
    295         Assertions.assertEquals(1, arcs.size());
    296         Assertions.assertSame(fullArc, arcs.get(0));
    297     }
    298
    299     @Test
    300     void testBoundaryStream_noBoundaries() {
    301         // arrange
    302         final GreatArcPath path = GreatArcPath.empty();
    303
    304         // act
    305         final List<GreatArc> arcs = path.boundaryStream().collect(Collectors.toList());
    306
    307         // assert
    308         Assertions.assertEquals(0, arcs.size());
    309     }
    310
    311     @Test
    312     void testToTree_empty() {
    313         // act
    314         final RegionBSPTree2S tree = GreatArcPath.empty().toTree();
    315
    316         // assert
    317         Assertions.assertFalse(tree.isFull());
    318         Assertions.assertTrue(tree.isEmpty());
    319     }
    320
    321     @Test
    322     void testToTree_halfSpace() {
    323         // arrange
    324         final GreatArcPath path = GreatArcPath.builder(TEST_PRECISION)
    325                 .append(Point2S.PLUS_I)
    326                 .append(Point2S.PLUS_J)
    327                 .build();
    328
    329         // act
    330         final RegionBSPTree2S tree = path.toTree();
    331
    332         // assert
    333         Assertions.assertFalse(tree.isFull());
    334         Assertions.assertFalse(tree.isEmpty());
    335
    336         Assertions.assertEquals(Angle.TWO_PI, tree.getSize(), TEST_EPS);
    337         SphericalTestUtils.assertPointsEq(Point2S.PLUS_K, tree.getCentroid(), TEST_EPS);
    338
    339         SphericalTestUtils.checkClassify(tree, RegionLocation.INSIDE, Point2S.PLUS_K);
    340         SphericalTestUtils.checkClassify(tree, RegionLocation.OUTSIDE, Point2S.MINUS_K);
    341     }
    342
    343     @Test
    344     void testToTree_triangle() {
    345         // arrange
    346         final GreatArcPath path = GreatArcPath.builder(TEST_PRECISION)
    347                 .append(Point2S.PLUS_I)
    348                 .append(Point2S.PLUS_J)
    349                 .append(Point2S.PLUS_K)
    350                 .close();
    351
    352         // act
    353         final RegionBSPTree2S tree = path.toTree();
    354
    355         // assert
    356         Assertions.assertFalse(tree.isFull());
    357         Assertions.assertFalse(tree.isEmpty());
    358
    359         Assertions.assertEquals(Angle.PI_OVER_TWO, tree.getSize(), TEST_EPS);
    360
    361         final Point2S bc = Point2S.from(Point2S.PLUS_I.getVector()
    362                 .add(Point2S.PLUS_J.getVector())
    363                 .add(Point2S.PLUS_K.getVector()));
    364
    365         SphericalTestUtils.assertPointsEq(bc, tree.getCentroid(), TEST_EPS);
    366
    367         SphericalTestUtils.checkClassify(tree, RegionLocation.INSIDE, Point2S.of(0.5, 0.5));
    368         SphericalTestUtils.checkClassify(tree, RegionLocation.OUTSIDE,
    369                 Point2S.MINUS_K, Point2S.MINUS_I, Point2S.MINUS_J);
    370     }
    371
    372     @Test
    373     void testBuilder_append() {
    374         // arrange
    375         final Point2S a = Point2S.PLUS_I;
    376         final Point2S b = Point2S.PLUS_J;
    377         final Point2S c = Point2S.PLUS_K;
    378         final Point2S d = Point2S.of(-1, Angle.PI_OVER_TWO);
    379         final Point2S e = Point2S.of(0, 0.6 * Math.PI);
    380
    381         final GreatArcPath.Builder builder = GreatArcPath.builder(TEST_PRECISION);
    382
    383         // act
    384         final GreatArcPath path = builder.append(GreatCircles.arcFromPoints(a, b, TEST_PRECISION))
    385             .appendVertices(c, d)
    386             .append(e)
    387             .append(GreatCircles.arcFromPoints(e, a, TEST_PRECISION))
    388             .build();
    389
    390         // assert
    391         Assertions.assertFalse(path.isEmpty());
    392         Assertions.assertTrue(path.isClosed());
    393
    394         SphericalTestUtils.assertPointsEq(a, path.getStartVertex(), TEST_EPS);
    395         SphericalTestUtils.assertPointsEq(a, path.getEndVertex(), TEST_EPS);
    396
    397         final List<GreatArc> arcs = path.getArcs();
    398         Assertions.assertEquals(5, arcs.size());
    399         assertArc(arcs.get(0), a, b);
    400         assertArc(arcs.get(1), b, c);
    401         assertArc(arcs.get(2), c, d);
    402         assertArc(arcs.get(3), d, e);
    403         assertArc(arcs.get(4), e, a);
    404
    405         assertPoints(Arrays.asList(a, b, c, d, e, a), path.getVertices());
    406     }
    407
    408     @Test
    409     void testBuilder_prepend() {
    410         // arrange
    411         final Point2S a = Point2S.PLUS_I;
    412         final Point2S b = Point2S.PLUS_J;
    413         final Point2S c = Point2S.PLUS_K;
    414         final Point2S d = Point2S.of(-1, Angle.PI_OVER_TWO);
    415         final Point2S e = Point2S.of(0, 0.6 * Math.PI);
    416
    417         final GreatArcPath.Builder builder = GreatArcPath.builder(TEST_PRECISION);
    418
    419         // act
    420         final GreatArcPath path = builder.prepend(GreatCircles.arcFromPoints(e, a, TEST_PRECISION))
    421             .prependPoints(Arrays.asList(c, d))
    422             .prepend(b)
    423             .prepend(GreatCircles.arcFromPoints(a, b, TEST_PRECISION))
    424             .build();
    425
    426         // assert
    427         Assertions.assertFalse(path.isEmpty());
    428         Assertions.assertTrue(path.isClosed());
    429
    430         SphericalTestUtils.assertPointsEq(a, path.getStartVertex(), TEST_EPS);
    431         SphericalTestUtils.assertPointsEq(a, path.getEndVertex(), TEST_EPS);
    432
    433         final List<GreatArc> arcs = path.getArcs();
    434         Assertions.assertEquals(5, arcs.size());
    435         assertArc(arcs.get(0), a, b);
    436         assertArc(arcs.get(1), b, c);
    437         assertArc(arcs.get(2), c, d);
    438         assertArc(arcs.get(3), d, e);
    439         assertArc(arcs.get(4), e, a);
    440
    441         assertPoints(Arrays.asList(a, b, c, d, e, a), path.getVertices());
    442     }
    443
    444     @Test
    445     void testBuilder_appendAndPrepend_points() {
    446         // arrange
    447         final Point2S a = Point2S.PLUS_I;
    448         final Point2S b = Point2S.PLUS_J;
    449         final Point2S c = Point2S.PLUS_K;
    450         final Point2S d = Point2S.of(-1, Angle.PI_OVER_TWO);
    451         final Point2S e = Point2S.of(0, 0.6 * Math.PI);
    452
    453         final GreatArcPath.Builder builder = GreatArcPath.builder(TEST_PRECISION);
    454
    455         // act
    456         final GreatArcPath path = builder.prepend(a)
    457                 .append(b)
    458                 .prepend(e)
    459                 .append(c)
    460                 .prepend(d)
    461                 .build();
    462
    463         // assert
    464         Assertions.assertFalse(path.isEmpty());
    465         Assertions.assertFalse(path.isClosed());
    466
    467         SphericalTestUtils.assertPointsEq(d, path.getStartVertex(), TEST_EPS);
    468         SphericalTestUtils.assertPointsEq(c, path.getEndVertex(), TEST_EPS);
    469
    470         final List<GreatArc> arcs = path.getArcs();
    471         Assertions.assertEquals(4, arcs.size());
    472         assertArc(arcs.get(0), d, e);
    473         assertArc(arcs.get(1), e, a);
    474         assertArc(arcs.get(2), a, b);
    475         assertArc(arcs.get(3), b, c);
    476
    477         assertPoints(Arrays.asList(d, e, a, b, c), path.getVertices());
    478     }
    479
    480     @Test
    481     void testBuilder_appendAndPrepend_mixedArguments() {
    482         // arrange
    483         final Point2S a = Point2S.PLUS_I;
    484         final Point2S b = Point2S.PLUS_J;
    485         final Point2S c = Point2S.PLUS_K;
    486         final Point2S d = Point2S.of(-1, Angle.PI_OVER_TWO);
    487         final Point2S e = Point2S.of(0, 0.6 * Math.PI);
    488
    489         final GreatArcPath.Builder builder = GreatArcPath.builder(TEST_PRECISION);
    490
    491         // act
    492         final GreatArcPath path = builder.append(GreatCircles.arcFromPoints(a, b, TEST_PRECISION))
    493                 .prepend(GreatCircles.arcFromPoints(e, a, TEST_PRECISION))
    494                 .append(c)
    495                 .prepend(d)
    496                 .append(GreatCircles.arcFromPoints(c, d, TEST_PRECISION))
    497                 .build();
    498
    499         // assert
    500         Assertions.assertFalse(path.isEmpty());
    501         Assertions.assertTrue(path.isClosed());
    502
    503         SphericalTestUtils.assertPointsEq(d, path.getStartVertex(), TEST_EPS);
    504         SphericalTestUtils.assertPointsEq(d, path.getEndVertex(), TEST_EPS);
    505
    506         final List<GreatArc> arcs = path.getArcs();
    507         Assertions.assertEquals(5, arcs.size());
    508         assertArc(arcs.get(0), d, e);
    509         assertArc(arcs.get(1), e, a);
    510         assertArc(arcs.get(2), a, b);
    511         assertArc(arcs.get(3), b, c);
    512         assertArc(arcs.get(4), c, d);
    513
    514         assertPoints(Arrays.asList(d, e, a, b, c, d), path.getVertices());
    515     }
    516
    517     @Test
    518     void testBuilder_points_noPrecisionGiven() {
    519         // act/assert
    520         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(null)
    521             .append(Point2S.PLUS_I)
    522             .append(Point2S.PLUS_J), IllegalStateException.class, "Unable to create arc: no point precision specified");
    523
    524         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(null)
    525             .prepend(Point2S.PLUS_I)
    526             .prepend(Point2S.PLUS_J), IllegalStateException.class, "Unable to create arc: no point precision specified");
    527     }
    528
    529     @Test
    530     void testBuilder_arcsNotConnected() {
    531         // act/assert
    532         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(TEST_PRECISION)
    533             .append(Point2S.PLUS_I)
    534             .append(Point2S.PLUS_J)
    535             .append(GreatCircles.arcFromPoints(Point2S.PLUS_K, Point2S.MINUS_J, TEST_PRECISION)), IllegalStateException.class, Pattern.compile("^Path arcs are not connected.*"));
    536
    537         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(TEST_PRECISION)
    538             .prepend(Point2S.PLUS_I)
    539             .prepend(Point2S.PLUS_J)
    540             .prepend(GreatCircles.arcFromPoints(Point2S.PLUS_K, Point2S.MINUS_J, TEST_PRECISION)), IllegalStateException.class, Pattern.compile("^Path arcs are not connected.*"));
    541     }
    542
    543     @Test
    544     void testBuilder_addToFullArc() {
    545         // act/assert
    546         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(TEST_PRECISION)
    547             .append(GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span())
    548             .append(Point2S.PLUS_J), IllegalStateException.class, Pattern.compile("^Cannot add point .* after full arc.*"));
    549
    550         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(TEST_PRECISION)
    551             .prepend(GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span())
    552             .prepend(Point2S.PLUS_J), IllegalStateException.class, Pattern.compile("^Cannot add point .* before full arc.*"));
    553     }
    554
    555     @Test
    556     void testBuilder_onlySinglePointGiven() {
    557         // act/assert
    558         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(TEST_PRECISION)
    559             .append(Point2S.PLUS_J)
    560             .build(), IllegalStateException.class, Pattern.compile("^Unable to create path; only a single point provided.*"));
    561
    562         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(TEST_PRECISION)
    563             .prepend(Point2S.PLUS_J)
    564             .build(), IllegalStateException.class,  Pattern.compile("^Unable to create path; only a single point provided.*"));
    565     }
    566
    567     @Test
    568     void testBuilder_cannotClose() {
    569         // act/assert
    570         GeometryTestUtils.assertThrowsWithMessage(() -> GreatArcPath.builder(TEST_PRECISION)
    571             .append(GreatCircles.fromPoints(Point2S.PLUS_I, Point2S.PLUS_J, TEST_PRECISION).span())
    572             .close(), IllegalStateException.class, "Unable to close path: path is full");
    573     }
    574
    575     @Test
    576     void testToString_empty() {
    577         // arrange
    578         final GreatArcPath path = GreatArcPath.empty();
    579
    580         // act
    581         final String str = path.toString();
    582
    583         // assert
    584         Assertions.assertEquals("GreatArcPath[empty= true]", str);
    585     }
    586
    587     @Test
    588     void testToString_singleFullArc() {
    589         // arrange
    590         final GreatArcPath path = GreatArcPath.fromArcs(GreatCircles.fromPole(Vector3D.Unit.PLUS_Z, TEST_PRECISION).span());
    591
    592         // act
    593         final String str = path.toString();
    594
    595         // assert
    596         GeometryTestUtils.assertContains("GreatArcPath[full= true, circle= GreatCircle[", str);
    597     }
    598
    599     @Test
    600     void testToString_nonFullArcs() {
    601         // arrange
    602         final GreatArcPath path = GreatArcPath.builder(TEST_PRECISION)
    603                 .append(Point2S.PLUS_I)
    604                 .append(Point2S.PLUS_J)
    605                 .build();
    606
    607         // act
    608         final String str = path.toString();
    609
    610         // assert
    611         GeometryTestUtils.assertContains("ArcPath[vertices= [", str);
    612     }
    613
    614     private static void assertArc(final GreatArc arc, final Point2S start, final Point2S end) {
    615         SphericalTestUtils.assertPointsEq(start, arc.getStartPoint(), TEST_EPS);
    616         SphericalTestUtils.assertPointsEq(end, arc.getEndPoint(), TEST_EPS);
    617     }
    618
    619     private static void assertPoints(final Collection<Point2S> expected, final Collection<Point2S> actual) {
    620         Assertions.assertEquals(expected.size(), actual.size());
    621
    622         final Iterator<Point2S> expIt = expected.iterator();
    623         final Iterator<Point2S> actIt = actual.iterator();
    624
    625         while (expIt.hasNext() && actIt.hasNext()) {
    626             SphericalTestUtils.assertPointsEq(expIt.next(), actIt.next(), TEST_EPS);
    627         }
    628     }
    629 }