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.twod.path;
    18
    19  import java.util.ArrayList;
    20  import java.util.Arrays;
    21  import java.util.Collections;
    22  import java.util.List;
    23  import java.util.stream.Collectors;
    24
    25  import org.apache.commons.geometry.core.GeometryTestUtils;
    26  import org.apache.commons.geometry.core.RegionLocation;
    27  import org.apache.commons.geometry.core.partitioning.Split;
    28  import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
    29  import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
    30  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
    31  import org.apache.commons.geometry.euclidean.twod.AffineTransformMatrix2D;
    32  import org.apache.commons.geometry.euclidean.twod.Line;
    33  import org.apache.commons.geometry.euclidean.twod.LineConvexSubset;
    34  import org.apache.commons.geometry.euclidean.twod.LinecastChecker2D;
    35  import org.apache.commons.geometry.euclidean.twod.Lines;
    36  import org.apache.commons.geometry.euclidean.twod.Ray;
    37  import org.apache.commons.geometry.euclidean.twod.RegionBSPTree2D;
    38  import org.apache.commons.geometry.euclidean.twod.ReverseRay;
    39  import org.apache.commons.geometry.euclidean.twod.Segment;
    40  import org.apache.commons.geometry.euclidean.twod.Vector2D;
    41  import org.apache.commons.geometry.euclidean.twod.path.LinePath.Builder;
    42  import org.apache.commons.numbers.angle.PlaneAngleRadians;
    43  import org.junit.Assert;
    44  import org.junit.Test;
    45
    46  public class LinePathTest {
    47
    48      private static final double TEST_EPS = 1e-10;
    49
    50      private static final DoublePrecisionContext TEST_PRECISION =
    51              new EpsilonDoublePrecisionContext(TEST_EPS);
    52
    53      @Test
    54      public void testFrom_empty() {
    55          // act
    56          final LinePath path = LinePath.from(new ArrayList<>());
    57
    58          // assert
    59          Assert.assertTrue(path.isEmpty());
    60          Assert.assertFalse(path.isInfinite());
    61          Assert.assertTrue(path.isFinite());
    62          Assert.assertFalse(path.isClosed());
    63
    64          Assert.assertEquals(0, path.getSize(), TEST_EPS);
    65
    66          Assert.assertNull(path.getStart());
    67          Assert.assertNull(path.getEnd());
    68
    69          Assert.assertEquals(0, path.getElements().size());
    70
    71          Assert.assertEquals(0, path.getVertexSequence().size());
    72      }
    73
    74      @Test
    75      public void testFrom_singleFiniteSegment() {
    76          // arrange
    77          final Segment a = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
    78
    79          // act
    80          final LinePath path = LinePath.from(a);
    81
    82          // assert
    83          Assert.assertFalse(path.isEmpty());
    84          Assert.assertFalse(path.isInfinite());
    85          Assert.assertTrue(path.isFinite());
    86          Assert.assertFalse(path.isClosed());
    87
    88          Assert.assertEquals(1, path.getSize(), TEST_EPS);
    89
    90          Assert.assertSame(a, path.getStart());
    91          Assert.assertSame(a, path.getEnd());
    92
    93          final List<LineConvexSubset> segments = path.getElements();
    94          Assert.assertEquals(1, segments.size());
    95          Assert.assertSame(a, segments.get(0));
    96
    97          Assert.assertEquals(Arrays.asList(Vector2D.ZERO, Vector2D.of(1, 0)), path.getVertexSequence());
    98      }
    99
    100     @Test
    101     public void testFrom_singleInfiniteSegment() {
    102         // arrange
    103         final LineConvexSubset a = Lines.fromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION).span();
    104
    105         // act
    106         final LinePath path = LinePath.from(a);
    107
    108         // assert
    109         Assert.assertFalse(path.isEmpty());
    110         Assert.assertTrue(path.isInfinite());
    111         Assert.assertFalse(path.isFinite());
    112         Assert.assertFalse(path.isClosed());
    113
    114         GeometryTestUtils.assertPositiveInfinity(path.getSize());
    115
    116         Assert.assertSame(a, path.getStart());
    117         Assert.assertSame(a, path.getEnd());
    118
    119         final List<LineConvexSubset> segments = path.getElements();
    120         Assert.assertEquals(1, segments.size());
    121         Assert.assertSame(a, segments.get(0));
    122
    123         Assert.assertEquals(0, path.getVertexSequence().size());
    124     }
    125
    126     @Test
    127     public void testFrom_finiteSegments_notClosed() {
    128         // arrange
    129         final Vector2D p1 = Vector2D.ZERO;
    130         final Vector2D p2 = Vector2D.of(1, 0);
    131         final Vector2D p3 = Vector2D.of(1, 1);
    132
    133         final Segment a = Lines.segmentFromPoints(p1, p2, TEST_PRECISION);
    134         final Segment b = Lines.segmentFromPoints(p2, p3, TEST_PRECISION);
    135
    136         // act
    137         final LinePath path = LinePath.from(a, b);
    138
    139         // assert
    140         Assert.assertFalse(path.isEmpty());
    141         Assert.assertFalse(path.isInfinite());
    142         Assert.assertTrue(path.isFinite());
    143         Assert.assertFalse(path.isClosed());
    144
    145         Assert.assertEquals(2, path.getSize(), TEST_EPS);
    146
    147         Assert.assertSame(a, path.getStart());
    148         Assert.assertSame(b, path.getEnd());
    149
    150         final List<LineConvexSubset> segments = path.getElements();
    151         Assert.assertEquals(2, segments.size());
    152         Assert.assertSame(a, segments.get(0));
    153         Assert.assertSame(b, segments.get(1));
    154
    155         Assert.assertEquals(Arrays.asList(p1, p2, p3), path.getVertexSequence());
    156     }
    157
    158     @Test
    159     public void testFrom_finiteSegments_closed() {
    160         // arrange
    161         final Vector2D p1 = Vector2D.ZERO;
    162         final Vector2D p2 = Vector2D.of(1, 0);
    163         final Vector2D p3 = Vector2D.of(1, 1);
    164
    165         final Segment a = Lines.segmentFromPoints(p1, p2, TEST_PRECISION);
    166         final Segment b = Lines.segmentFromPoints(p2, p3, TEST_PRECISION);
    167         final Segment c = Lines.segmentFromPoints(p3, p1, TEST_PRECISION);
    168
    169         // act
    170         final LinePath path = LinePath.from(Arrays.asList(a, b, c));
    171
    172         // assert
    173         Assert.assertFalse(path.isEmpty());
    174         Assert.assertFalse(path.isInfinite());
    175         Assert.assertTrue(path.isFinite());
    176         Assert.assertTrue(path.isClosed());
    177
    178         Assert.assertSame(a, path.getStart());
    179         Assert.assertSame(c, path.getEnd());
    180
    181         Assert.assertEquals(2 + Math.sqrt(2), path.getSize(), TEST_EPS);
    182
    183         final List<LineConvexSubset> segments = path.getElements();
    184         Assert.assertEquals(3, segments.size());
    185         Assert.assertSame(a, segments.get(0));
    186         Assert.assertSame(b, segments.get(1));
    187         Assert.assertSame(c, segments.get(2));
    188
    189         Assert.assertEquals(Arrays.asList(p1, p2, p3, p1), path.getVertexSequence());
    190     }
    191
    192     @Test
    193     public void testFrom_infiniteSegments() {
    194         // arrange
    195         final ReverseRay a = Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION)
    196                 .reverseRayTo(1.0);
    197         final Ray b = Lines.fromPointAndAngle(Vector2D.of(1, 0), PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION)
    198                 .rayFrom(0.0);
    199
    200         // act
    201         final LinePath path = LinePath.from(Arrays.asList(a, b));
    202
    203         // assert
    204         Assert.assertFalse(path.isEmpty());
    205         Assert.assertTrue(path.isInfinite());
    206         Assert.assertFalse(path.isFinite());
    207         Assert.assertFalse(path.isClosed());
    208
    209         GeometryTestUtils.assertPositiveInfinity(path.getSize());
    210
    211         Assert.assertSame(a, path.getStart());
    212         Assert.assertSame(b, path.getEnd());
    213
    214         final List<LineConvexSubset> segments = path.getElements();
    215         Assert.assertEquals(2, segments.size());
    216         Assert.assertSame(a, segments.get(0));
    217         Assert.assertSame(b, segments.get(1));
    218
    219         Assert.assertEquals(Collections.singletonList(Vector2D.of(1, 0)), path.getVertexSequence());
    220     }
    221
    222     @Test
    223     public void testFrom_finiteAndInfiniteSegments_startInfinite() {
    224         // arrange
    225         final ReverseRay a = Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION).reverseRayTo(1.0);
    226         final Segment b = Lines.segmentFromPoints(Vector2D.of(1, 0), Vector2D.of(1, 1), TEST_PRECISION);
    227
    228         // act
    229         final LinePath path = LinePath.from(Arrays.asList(a, b));
    230
    231         // assert
    232         Assert.assertFalse(path.isEmpty());
    233         Assert.assertTrue(path.isInfinite());
    234         Assert.assertFalse(path.isFinite());
    235         Assert.assertFalse(path.isClosed());
    236
    237         Assert.assertSame(a, path.getStart());
    238         Assert.assertSame(b, path.getEnd());
    239
    240         final List<LineConvexSubset> segments = path.getElements();
    241         Assert.assertEquals(2, segments.size());
    242         Assert.assertSame(a, segments.get(0));
    243         Assert.assertSame(b, segments.get(1));
    244
    245         Assert.assertEquals(Arrays.asList(Vector2D.of(1, 0), Vector2D.of(1, 1)), path.getVertexSequence());
    246     }
    247
    248     @Test
    249     public void testFrom_finiteAndInfiniteSegments_endInfinite() {
    250         // arrange
    251         final Segment a = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
    252         final Ray b = Lines.fromPointAndAngle(Vector2D.of(1, 0), PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION)
    253                 .rayFrom(0.0);
    254
    255         // act
    256         final LinePath path = LinePath.from(Arrays.asList(a, b));
    257
    258         // assert
    259         Assert.assertFalse(path.isEmpty());
    260         Assert.assertTrue(path.isInfinite());
    261         Assert.assertFalse(path.isFinite());
    262         Assert.assertFalse(path.isClosed());
    263
    264         Assert.assertSame(a, path.getStart());
    265         Assert.assertSame(b, path.getEnd());
    266
    267         final List<LineConvexSubset> segments = path.getElements();
    268         Assert.assertEquals(2, segments.size());
    269         Assert.assertSame(a, segments.get(0));
    270         Assert.assertSame(b, segments.get(1));
    271
    272         Assert.assertEquals(Arrays.asList(Vector2D.ZERO, Vector2D.of(1, 0)), path.getVertexSequence());
    273     }
    274
    275     @Test
    276     public void testFrom_segmentsNotConnected() {
    277         // arrange
    278         final Segment a = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
    279         final Segment b = Lines.segmentFromPoints(Vector2D.of(1.01, 0), Vector2D.of(1, 0), TEST_PRECISION);
    280
    281         final LineConvexSubset c = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION).span();
    282         final LineConvexSubset d = Lines.fromPointAndAngle(Vector2D.of(1, 0), PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION).span();
    283
    284         // act/assert
    285         GeometryTestUtils.assertThrows(() -> {
    286             LinePath.from(a, b);
    287         }, IllegalStateException.class);
    288
    289         GeometryTestUtils.assertThrows(() -> {
    290             LinePath.from(c, b);
    291         }, IllegalStateException.class);
    292
    293         GeometryTestUtils.assertThrows(() -> {
    294             LinePath.from(a, d);
    295         }, IllegalStateException.class);
    296     }
    297
    298     @Test
    299     public void testFromVertices_empty() {
    300         // act
    301         final LinePath path = LinePath.fromVertices(new ArrayList<>(), TEST_PRECISION);
    302
    303         // assert
    304         Assert.assertTrue(path.isEmpty());
    305         Assert.assertFalse(path.isInfinite());
    306         Assert.assertTrue(path.isFinite());
    307         Assert.assertFalse(path.isClosed());
    308
    309         Assert.assertNull(path.getStart());
    310         Assert.assertNull(path.getEnd());
    311
    312         Assert.assertEquals(0, path.getElements().size());
    313
    314         Assert.assertEquals(0, path.getVertexSequence().size());
    315     }
    316
    317     @Test
    318     public void testFromVertices_singleVertex_failsToCreatePath() {
    319         // act/assert
    320         GeometryTestUtils.assertThrows(() -> {
    321             LinePath.fromVertices(Collections.singletonList(Vector2D.ZERO), TEST_PRECISION);
    322         }, IllegalStateException.class);
    323     }
    324
    325     @Test
    326     public void testFromVertices_twoVertices() {
    327         // arrange
    328         final Vector2D p1 = Vector2D.ZERO;
    329         final Vector2D p2 = Vector2D.of(1, 0);
    330
    331         // act
    332         final LinePath path = LinePath.fromVertices(Arrays.asList(p1, p2), TEST_PRECISION);
    333
    334         // assert
    335         Assert.assertFalse(path.isEmpty());
    336         Assert.assertFalse(path.isInfinite());
    337         Assert.assertTrue(path.isFinite());
    338         Assert.assertFalse(path.isClosed());
    339
    340         assertFiniteSegment(path.getStart(), p1, p2);
    341         Assert.assertSame(path.getStart(), path.getEnd());
    342
    343         final List<LineConvexSubset> segments = path.getElements();
    344         Assert.assertEquals(1, segments.size());
    345         assertFiniteSegment(segments.get(0), p1, p2);
    346
    347         Assert.assertEquals(Arrays.asList(p1, p2), path.getVertexSequence());
    348     }
    349
    350     @Test
    351     public void testFromVertices_multipleVertices_notClosed() {
    352         // arrange
    353         final Vector2D p1 = Vector2D.ZERO;
    354         final Vector2D p2 = Vector2D.of(1, 0);
    355         final Vector2D p3 = Vector2D.of(1, 1);
    356         final Vector2D p4 = Vector2D.of(0, 1);
    357
    358         // act
    359         final LinePath path = LinePath.fromVertices(Arrays.asList(p1, p2, p3, p4), TEST_PRECISION);
    360
    361         // assert
    362         Assert.assertFalse(path.isEmpty());
    363         Assert.assertFalse(path.isInfinite());
    364         Assert.assertTrue(path.isFinite());
    365         Assert.assertFalse(path.isClosed());
    366
    367         assertFiniteSegment(path.getStart(), p1, p2);
    368         assertFiniteSegment(path.getEnd(), p3, p4);
    369
    370         final List<LineConvexSubset> segments = path.getElements();
    371         Assert.assertEquals(3, segments.size());
    372         assertFiniteSegment(segments.get(0), p1, p2);
    373         assertFiniteSegment(segments.get(1), p2, p3);
    374         assertFiniteSegment(segments.get(2), p3, p4);
    375
    376         Assert.assertEquals(Arrays.asList(p1, p2, p3, p4), path.getVertexSequence());
    377     }
    378
    379     @Test
    380     public void testFromVertices_multipleVertices_closed() {
    381         // arrange
    382         final Vector2D p1 = Vector2D.ZERO;
    383         final Vector2D p2 = Vector2D.of(1, 0);
    384         final Vector2D p3 = Vector2D.of(1, 1);
    385         final Vector2D p4 = Vector2D.of(0, 1);
    386
    387         // act
    388         final LinePath path = LinePath.fromVertices(Arrays.asList(p1, p2, p3, p4, p1), TEST_PRECISION);
    389
    390         // assert
    391         Assert.assertFalse(path.isEmpty());
    392         Assert.assertFalse(path.isInfinite());
    393         Assert.assertTrue(path.isFinite());
    394         Assert.assertTrue(path.isClosed());
    395
    396         assertFiniteSegment(path.getStart(), p1, p2);
    397         assertFiniteSegment(path.getEnd(), p4, p1);
    398
    399         final List<LineConvexSubset> segments = path.getElements();
    400         Assert.assertEquals(4, segments.size());
    401         assertFiniteSegment(segments.get(0), p1, p2);
    402         assertFiniteSegment(segments.get(1), p2, p3);
    403         assertFiniteSegment(segments.get(2), p3, p4);
    404         assertFiniteSegment(segments.get(3), p4, p1);
    405
    406         Assert.assertEquals(Arrays.asList(p1, p2, p3, p4, p1), path.getVertexSequence());
    407     }
    408
    409     @Test
    410     public void testFromVertexLoop_empty() {
    411         // act
    412         final LinePath path = LinePath.fromVertexLoop(new ArrayList<>(), TEST_PRECISION);
    413
    414         // assert
    415         Assert.assertTrue(path.isEmpty());
    416         Assert.assertFalse(path.isInfinite());
    417         Assert.assertTrue(path.isFinite());
    418         Assert.assertFalse(path.isClosed());
    419
    420         Assert.assertNull(path.getStart());
    421         Assert.assertNull(path.getEnd());
    422
    423         Assert.assertEquals(0, path.getElements().size());
    424
    425         Assert.assertEquals(0, path.getVertexSequence().size());
    426     }
    427
    428     @Test
    429     public void testFromVertexLoop_singleVertex_failsToCreatePath() {
    430         // act/assert
    431         GeometryTestUtils.assertThrows(() -> {
    432             LinePath.fromVertexLoop(Collections.singletonList(Vector2D.ZERO), TEST_PRECISION);
    433         }, IllegalStateException.class);
    434     }
    435
    436     @Test
    437     public void testFromVertexLoop_closeRequired() {
    438         // arrange
    439         final Vector2D p1 = Vector2D.ZERO;
    440         final Vector2D p2 = Vector2D.of(1, 0);
    441         final Vector2D p3 = Vector2D.of(1, 1);
    442
    443         // act
    444         final LinePath path = LinePath.fromVertexLoop(Arrays.asList(p1, p2, p3), TEST_PRECISION);
    445
    446         // assert
    447         Assert.assertFalse(path.isEmpty());
    448         Assert.assertFalse(path.isInfinite());
    449         Assert.assertTrue(path.isFinite());
    450         Assert.assertTrue(path.isClosed());
    451
    452         final List<LineConvexSubset> segments = path.getElements();
    453         Assert.assertEquals(3, segments.size());
    454         assertFiniteSegment(segments.get(0), p1, p2);
    455         assertFiniteSegment(segments.get(1), p2, p3);
    456         assertFiniteSegment(segments.get(2), p3, p1);
    457
    458         Assert.assertEquals(Arrays.asList(p1, p2, p3, p1), path.getVertexSequence());
    459     }
    460
    461     @Test
    462     public void testFromVertexLoop_closeNotRequired() {
    463         // arrange
    464         final Vector2D p1 = Vector2D.ZERO;
    465         final Vector2D p2 = Vector2D.of(1, 0);
    466         final Vector2D p3 = Vector2D.of(1, 1);
    467
    468         // act
    469         final LinePath path = LinePath.fromVertexLoop(Arrays.asList(p1, p2, p3, Vector2D.of(0, 0)), TEST_PRECISION);
    470
    471         // assert
    472         Assert.assertFalse(path.isEmpty());
    473         Assert.assertFalse(path.isInfinite());
    474         Assert.assertTrue(path.isFinite());
    475         Assert.assertTrue(path.isClosed());
    476
    477         final List<LineConvexSubset> segments = path.getElements();
    478         Assert.assertEquals(3, segments.size());
    479         assertFiniteSegment(segments.get(0), p1, p2);
    480         assertFiniteSegment(segments.get(1), p2, p3);
    481         assertFiniteSegment(segments.get(2), p3, p1);
    482
    483         Assert.assertEquals(Arrays.asList(p1, p2, p3, p1), path.getVertexSequence());
    484     }
    485
    486     @Test
    487     public void testFromVertices_booleanArg() {
    488         // arrange
    489         final Vector2D p1 = Vector2D.ZERO;
    490         final Vector2D p2 = Vector2D.of(1, 0);
    491         final Vector2D p3 = Vector2D.of(0, 1);
    492
    493         // act
    494         final LinePath open = LinePath.fromVertices(Arrays.asList(p1, p2, p3), false, TEST_PRECISION);
    495         final LinePath closed = LinePath.fromVertices(Arrays.asList(p1, p2, p3), true, TEST_PRECISION);
    496
    497         // assert
    498         Assert.assertFalse(open.isClosed());
    499
    500         final List<LineConvexSubset> openSegments = open.getElements();
    501         Assert.assertEquals(2, openSegments.size());
    502         assertFiniteSegment(openSegments.get(0), p1, p2);
    503         assertFiniteSegment(openSegments.get(1), p2, p3);
    504
    505         Assert.assertTrue(closed.isClosed());
    506
    507         final List<LineConvexSubset> closedSegments = closed.getElements();
    508         Assert.assertEquals(3, closedSegments.size());
    509         assertFiniteSegment(closedSegments.get(0), p1, p2);
    510         assertFiniteSegment(closedSegments.get(1), p2, p3);
    511         assertFiniteSegment(closedSegments.get(2), p3, p1);
    512     }
    513
    514     @Test
    515     public void testGetElements_listIsNotModifiable() {
    516         // arrange
    517         final Segment a = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
    518         final List<LineConvexSubset> inputSegments = new ArrayList<>(Collections.singletonList(a));
    519
    520         // act
    521         final LinePath path = LinePath.from(inputSegments);
    522
    523         inputSegments.clear();
    524
    525         // assert
    526         Assert.assertNotSame(inputSegments, path.getElements());
    527         Assert.assertEquals(1, path.getElements().size());
    528
    529         GeometryTestUtils.assertThrows(() -> {
    530             path.getElements().add(a);
    531         }, UnsupportedOperationException.class);
    532     }
    533
    534     @Test
    535     public void testBoundaryStream() {
    536         // arrange
    537         final Segment seg = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
    538         final LinePath path = LinePath.from(Collections.singletonList(seg));
    539
    540         // act
    541         final List<LineConvexSubset> segments = path.boundaryStream().collect(Collectors.toList());
    542
    543         // assert
    544         Assert.assertEquals(1, segments.size());
    545         Assert.assertSame(seg, segments.get(0));
    546     }
    547
    548     @Test
    549     public void testBoundaryStream_empty() {
    550         // arrange
    551         final LinePath path = LinePath.empty();
    552
    553         // act
    554         final List<LineConvexSubset> segments = path.boundaryStream().collect(Collectors.toList());
    555
    556         // assert
    557         Assert.assertEquals(0, segments.size());
    558     }
    559
    560     @Test
    561     public void testTransform_empty() {
    562         // arrange
    563         final LinePath path = LinePath.empty();
    564         final AffineTransformMatrix2D t = AffineTransformMatrix2D.createTranslation(Vector2D.Unit.PLUS_X);
    565
    566         // act/assert
    567         Assert.assertSame(path, path.transform(t));
    568     }
    569
    570     @Test
    571     public void testTransform_finite() {
    572         // arrange
    573         final LinePath path = LinePath.builder(TEST_PRECISION)
    574                 .append(Vector2D.Unit.ZERO)
    575                 .append(Vector2D.Unit.PLUS_X)
    576                 .append(Vector2D.Unit.PLUS_Y)
    577                 .close();
    578
    579         final AffineTransformMatrix2D t =
    580                 AffineTransformMatrix2D.createRotation(Vector2D.of(1, 1), PlaneAngleRadians.PI_OVER_TWO);
    581
    582         // act
    583         final LinePath result = path.transform(t);
    584
    585         // assert
    586         Assert.assertNotSame(path, result);
    587         Assert.assertTrue(result.isClosed());
    588         Assert.assertTrue(result.isFinite());
    589
    590         final List<LineConvexSubset> segments = result.getElements();
    591
    592         Assert.assertEquals(3, segments.size());
    593         assertFiniteSegment(segments.get(0), Vector2D.of(2, 0), Vector2D.of(2, 1));
    594         assertFiniteSegment(segments.get(1), Vector2D.of(2, 1), Vector2D.Unit.PLUS_X);
    595         assertFiniteSegment(segments.get(2), Vector2D.Unit.PLUS_X, Vector2D.of(2, 0));
    596     }
    597
    598     @Test
    599     public void testTransform_infinite() {
    600         // arrange
    601         final LinePath path = LinePath.from(
    602                 Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_Y, TEST_PRECISION).span());
    603
    604         final AffineTransformMatrix2D t = AffineTransformMatrix2D.createTranslation(Vector2D.Unit.PLUS_X);
    605
    606         // act
    607         final LinePath result = path.transform(t);
    608
    609         // assert
    610         Assert.assertNotSame(path, result);
    611         Assert.assertFalse(result.isClosed());
    612         Assert.assertFalse(result.isFinite());
    613
    614         final List<LineConvexSubset> segments = result.getElements();
    615
    616         Assert.assertEquals(1, segments.size());
    617         final LineConvexSubset segment = segments.get(0);
    618         Assert.assertTrue(segment.isInfinite());
    619         Assert.assertNull(segment.getStartPoint());
    620         Assert.assertNull(segment.getEndPoint());
    621         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.PLUS_X, segment.getLine().getOrigin(), TEST_EPS);
    622         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.PLUS_Y, segment.getLine().getDirection(), TEST_EPS);
    623     }
    624
    625     @Test
    626     public void testReverse_empty() {
    627         // arrange
    628         final LinePath path = LinePath.empty();
    629
    630         // act/assert
    631         Assert.assertSame(path, path.reverse());
    632     }
    633
    634     @Test
    635     public void testReverse() {
    636         // arrange
    637         final LinePath path = LinePath.builder(TEST_PRECISION)
    638                 .append(Vector2D.Unit.ZERO)
    639                 .append(Vector2D.Unit.PLUS_X)
    640                 .append(Vector2D.Unit.PLUS_Y)
    641                 .close();
    642
    643         // act
    644         final LinePath result = path.reverse();
    645
    646         // assert
    647         Assert.assertNotSame(path, result);
    648         Assert.assertTrue(result.isClosed());
    649         Assert.assertTrue(result.isFinite());
    650
    651         final List<LineConvexSubset> segments = result.getElements();
    652
    653         Assert.assertEquals(3, segments.size());
    654         assertFiniteSegment(segments.get(0), Vector2D.Unit.ZERO, Vector2D.Unit.PLUS_Y);
    655         assertFiniteSegment(segments.get(1), Vector2D.Unit.PLUS_Y, Vector2D.Unit.PLUS_X);
    656         assertFiniteSegment(segments.get(2), Vector2D.Unit.PLUS_X, Vector2D.Unit.ZERO);
    657     }
    658
    659     @Test
    660     public void testReverse_singleInfinite() {
    661         // arrange
    662         final LinePath path = LinePath.from(
    663                 Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_Y, TEST_PRECISION).span());
    664
    665         // act
    666         final LinePath result = path.reverse();
    667
    668         // assert
    669         Assert.assertNotSame(path, result);
    670         Assert.assertFalse(result.isClosed());
    671         Assert.assertFalse(result.isFinite());
    672
    673         final List<LineConvexSubset> segments = result.getElements();
    674
    675         Assert.assertEquals(1, segments.size());
    676         final LineConvexSubset segment = segments.get(0);
    677         Assert.assertTrue(segment.isInfinite());
    678         Assert.assertNull(segment.getStartPoint());
    679         Assert.assertNull(segment.getEndPoint());
    680         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.ZERO, segment.getLine().getOrigin(), TEST_EPS);
    681         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_Y, segment.getLine().getDirection(), TEST_EPS);
    682     }
    683
    684     @Test
    685     public void testReverse_doubleInfinite() {
    686         // arrange
    687         final LineConvexSubset a = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_Y, TEST_PRECISION).reverseRayTo(Vector2D.ZERO);
    688         final LineConvexSubset b = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION).rayFrom(Vector2D.ZERO);
    689
    690         final LinePath path = LinePath.from(a, b);
    691
    692         // act
    693         final LinePath result = path.reverse();
    694
    695         // assert
    696         Assert.assertNotSame(path, result);
    697         Assert.assertFalse(result.isClosed());
    698         Assert.assertFalse(result.isFinite());
    699
    700         final List<LineConvexSubset> segments = result.getElements();
    701         Assert.assertEquals(2, segments.size());
    702
    703         final LineConvexSubset bResult = segments.get(0);
    704         Assert.assertTrue(bResult.isInfinite());
    705         Assert.assertNull(bResult.getStartPoint());
    706         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, bResult.getEndPoint(), TEST_EPS);
    707         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, bResult.getLine().getOrigin(), TEST_EPS);
    708         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_X, bResult.getLine().getDirection(), TEST_EPS);
    709
    710         final LineConvexSubset aResult = segments.get(1);
    711         Assert.assertTrue(aResult.isInfinite());
    712         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, aResult.getStartPoint(), TEST_EPS);
    713         Assert.assertNull(aResult.getEndPoint());
    714         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, aResult.getLine().getOrigin(), TEST_EPS);
    715         EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_Y, aResult.getLine().getDirection(), TEST_EPS);
    716     }
    717
    718     @Test
    719     public void testToTree() {
    720         // arrange
    721         final LinePath path = LinePath.builder(TEST_PRECISION)
    722                 .appendVertices(Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1), Vector2D.of(0, 1))
    723                 .close();
    724
    725         // act
    726         final RegionBSPTree2D tree = path.toTree();
    727
    728         // assert
    729         Assert.assertEquals(1, tree.getSize(), TEST_EPS);
    730         Assert.assertEquals(4, tree.getBoundarySize(), TEST_EPS);
    731
    732         Assert.assertEquals(RegionLocation.INSIDE, tree.classify(Vector2D.of(0.5, 0.5)));
    733
    734         Assert.assertEquals(RegionLocation.OUTSIDE, tree.classify(Vector2D.of(0.5, -1)));
    735         Assert.assertEquals(RegionLocation.OUTSIDE, tree.classify(Vector2D.of(0.5, 2)));
    736         Assert.assertEquals(RegionLocation.OUTSIDE, tree.classify(Vector2D.of(-1, 0.5)));
    737         Assert.assertEquals(RegionLocation.OUTSIDE, tree.classify(Vector2D.of(2, 0.5)));
    738     }
    739
    740     @Test
    741     public void testSimplify() {
    742         // arrange
    743         final Builder builder = LinePath.builder(TEST_PRECISION);
    744
    745         final LinePath path = builder.appendVertices(
    746                 Vector2D.of(-1, 0),
    747                 Vector2D.ZERO,
    748                 Vector2D.of(1, 0),
    749                 Vector2D.of(1, 1),
    750                 Vector2D.of(1, 2))
    751             .build();
    752
    753         // act
    754         final LinePath result = path.simplify();
    755
    756         // assert
    757         final List<LineConvexSubset> segments = result.getElements();
    758         Assert.assertEquals(2, segments.size());
    759         assertFiniteSegment(segments.get(0), Vector2D.of(-1, 0), Vector2D.of(1, 0));
    760         assertFiniteSegment(segments.get(1), Vector2D.of(1, 0), Vector2D.of(1, 2));
    761     }
    762
    763     @Test
    764     public void testSimplify_startAndEndCombined() {
    765         // arrange
    766         final Builder builder = LinePath.builder(TEST_PRECISION);
    767
    768         final LinePath path = builder.appendVertices(
    769                 Vector2D.ZERO,
    770                 Vector2D.of(1, 0),
    771                 Vector2D.of(0, 1),
    772                 Vector2D.of(-1, 0))
    773             .close();
    774
    775         // act
    776         final LinePath result = path.simplify();
    777
    778         // assert
    779         Assert.assertNotSame(path, result);
    780         Assert.assertTrue(result.isClosed());
    781         Assert.assertFalse(result.isInfinite());
    782
    783         final List<LineConvexSubset> segments = result.getElements();
    784         Assert.assertEquals(3, segments.size());
    785         assertFiniteSegment(segments.get(0), Vector2D.of(-1, 0), Vector2D.of(1, 0));
    786         assertFiniteSegment(segments.get(1), Vector2D.of(1, 0), Vector2D.of(0, 1));
    787         assertFiniteSegment(segments.get(2), Vector2D.of(0, 1), Vector2D.of(-1, 0));
    788     }
    789
    790     @Test
    791     public void testSimplify_empty() {
    792         // arrange
    793         final Builder builder = LinePath.builder(TEST_PRECISION);
    794
    795         final LinePath path = builder.build();
    796
    797         // act
    798         final LinePath result = path.simplify();
    799
    800         // assert
    801         Assert.assertNotSame(path, result);
    802         Assert.assertFalse(result.isClosed());
    803         Assert.assertFalse(result.isInfinite());
    804
    805         final List<LineConvexSubset> segments = result.getElements();
    806         Assert.assertEquals(0, segments.size());
    807     }
    808
    809     @Test
    810     public void testSimplify_infiniteSegment() {
    811         // arrange
    812         final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
    813
    814         final Builder builder = LinePath.builder(TEST_PRECISION);
    815         final LinePath path = builder
    816                 .append(line.span())
    817                 .build();
    818
    819         // act
    820         final LinePath result = path.simplify();
    821
    822         // assert
    823         Assert.assertNotSame(path, result);
    824         Assert.assertFalse(result.isClosed());
    825         Assert.assertTrue(result.isInfinite());
    826
    827         Assert.assertNotNull(path.getStart());
    828         Assert.assertNotNull(path.getEnd());
    829         Assert.assertSame(path.getStart(), path.getEnd());
    830
    831         final List<LineConvexSubset> segments = result.getElements();
    832         Assert.assertEquals(1, segments.size());
    833         Assert.assertSame(line, segments.get(0).getLine());
    834     }
    835
    836     @Test
    837     public void testSimplify_combinedInfiniteSegment() {
    838         // arrange
    839         final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
    840         final Split<LineConvexSubset> split = line.span().split(
    841                 Lines.fromPointAndAngle(Vector2D.ZERO, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION));
    842
    843         final Builder builder = LinePath.builder(TEST_PRECISION);
    844         final LinePath path = builder
    845                 .append(split.getMinus())
    846                 .append(split.getPlus())
    847                 .build();
    848
    849         // act
    850         final LinePath result = path.simplify();
    851
    852         // assert
    853         Assert.assertNotSame(path, result);
    854         Assert.assertFalse(result.isClosed());
    855         Assert.assertTrue(result.isInfinite());
    856
    857         Assert.assertNotNull(result.getStart());
    858         Assert.assertNotNull(result.getEnd());
    859         Assert.assertSame(result.getStart(), result.getEnd());
    860
    861         final List<LineConvexSubset> segments = result.getElements();
    862         Assert.assertEquals(1, segments.size());
    863         Assert.assertSame(line, segments.get(0).getLine());
    864     }
    865
    866     @Test
    867     public void testSimplify_startAndEndNotCombinedWhenNotClosed() {
    868         // arrange
    869         final Line xAxis = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
    870         final Builder builder = LinePath.builder(TEST_PRECISION);
    871
    872         final LinePath path = builder
    873                 .append(xAxis.segment(0, 1))
    874                 .appendVertices(
    875                         Vector2D.of(2, 1),
    876                         Vector2D.of(3, 0))
    877                 .append(xAxis.segment(3, 4))
    878             .build();
    879
    880         // act
    881         final LinePath result = path.simplify();
    882
    883         // assert
    884         Assert.assertNotSame(path, result);
    885         Assert.assertFalse(result.isClosed());
    886         Assert.assertFalse(result.isInfinite());
    887
    888         final List<LineConvexSubset> segments = result.getElements();
    889         Assert.assertEquals(4, segments.size());
    890         assertFiniteSegment(segments.get(0), Vector2D.ZERO, Vector2D.of(1, 0));
    891         assertFiniteSegment(segments.get(1), Vector2D.of(1, 0), Vector2D.of(2, 1));
    892         assertFiniteSegment(segments.get(2), Vector2D.of(2, 1), Vector2D.of(3, 0));
    893         assertFiniteSegment(segments.get(3), Vector2D.of(3, 0), Vector2D.of(4, 0));
    894     }
    895
    896     @Test
    897     public void testSimplify_subsequentCallsToReturnedObjectReturnSameObject() {
    898         // arrange
    899         final Builder builder = LinePath.builder(TEST_PRECISION);
    900         final LinePath path = builder.appendVertices(
    901                     Vector2D.ZERO,
    902                     Vector2D.of(1, 0),
    903                     Vector2D.of(2, 0))
    904                 .build();
    905
    906         // act
    907         final LinePath result = path.simplify();
    908
    909         // assert
    910         Assert.assertNotSame(path, result);
    911         Assert.assertSame(result, result.simplify());
    912     }
    913
    914     @Test
    915     public void testLinecast_empty() {
    916         // arrange
    917         final LinePath path = LinePath.empty();
    918
    919         // act/assert
    920         LinecastChecker2D.with(path)
    921             .expectNothing()
    922             .whenGiven(Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION));
    923
    924         LinecastChecker2D.with(path)
    925             .expectNothing()
    926             .whenGiven(Lines.segmentFromPoints(Vector2D.Unit.MINUS_X, Vector2D.Unit.PLUS_X, TEST_PRECISION));
    927     }
    928
    929     @Test
    930     public void testLinecast() {
    931         // arrange
    932         final LinePath path = LinePath.fromVertexLoop(Arrays.asList(
    933                     Vector2D.ZERO, Vector2D.of(1, 0),
    934                     Vector2D.of(1, 1), Vector2D.of(0, 1)
    935                 ), TEST_PRECISION);
    936
    937         // act/assert
    938         LinecastChecker2D.with(path)
    939             .expectNothing()
    940             .whenGiven(Lines.fromPoints(Vector2D.of(0, 5), Vector2D.of(1, 6), TEST_PRECISION));
    941
    942         LinecastChecker2D.with(path)
    943             .expect(Vector2D.ZERO, Vector2D.Unit.MINUS_X)
    944             .and(Vector2D.ZERO, Vector2D.Unit.MINUS_Y)
    945             .and(Vector2D.of(1, 1), Vector2D.Unit.PLUS_Y)
    946             .and(Vector2D.of(1, 1), Vector2D.Unit.PLUS_X)
    947             .whenGiven(Lines.fromPoints(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION));
    948
    949         LinecastChecker2D.with(path)
    950             .expect(Vector2D.of(1, 1), Vector2D.Unit.PLUS_Y)
    951             .and(Vector2D.of(1, 1), Vector2D.Unit.PLUS_X)
    952             .whenGiven(Lines.segmentFromPoints(Vector2D.of(0.5, 0.5), Vector2D.of(1, 1), TEST_PRECISION));
    953     }
    954
    955     @Test
    956     public void testToString() {
    957         // arrange
    958         final Line yAxis = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_Y, TEST_PRECISION);
    959         final Line xAxis = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
    960
    961         final LinePath empty = LinePath.empty();
    962
    963         final LinePath singleFullSegment = LinePath.from(xAxis.span());
    964         final LinePath singleFiniteSegment = LinePath.from(
    965                 Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION));
    966
    967         final LinePath startOpenPath = LinePath.builder(TEST_PRECISION)
    968                 .append(xAxis.reverseRayTo(Vector2D.Unit.PLUS_X))
    969                 .append(Vector2D.of(1, 1))
    970                 .build();
    971
    972         final LinePath endOpenPath = LinePath.builder(TEST_PRECISION)
    973                 .append(Vector2D.of(0, 1))
    974                 .append(Vector2D.ZERO)
    975                 .append(xAxis.rayFrom(Vector2D.ZERO))
    976                 .build();
    977
    978         final LinePath doubleOpenPath = LinePath.from(yAxis.reverseRayTo(Vector2D.ZERO),
    979                 xAxis.rayFrom(Vector2D.ZERO));
    980
    981         final LinePath nonOpenPath = LinePath.builder(TEST_PRECISION)
    982                 .append(Vector2D.ZERO)
    983                 .append(Vector2D.Unit.PLUS_X)
    984                 .append(Vector2D.of(1, 1))
    985                 .build();
    986
    987         // act/assert
    988         final String emptyStr = empty.toString();
    989         GeometryTestUtils.assertContains("LinePath[empty= true", emptyStr);
    990
    991         final String singleFullStr = singleFullSegment.toString();
    992         GeometryTestUtils.assertContains("LinePath[single= LineSpanningSubset[", singleFullStr);
    993
    994         final String singleFiniteStr = singleFiniteSegment.toString();
    995         GeometryTestUtils.assertContains("LinePath[single= Segment[", singleFiniteStr);
    996
    997         final String startOpenStr = startOpenPath.toString();
    998         GeometryTestUtils.assertContains("LinePath[startDirection= ", startOpenStr);
    999         GeometryTestUtils.assertContains("vertexSequence=", startOpenStr);
    1000
    1001         final String endOpenStr = endOpenPath.toString();
    1002         GeometryTestUtils.assertContains("LinePath[vertexSequence= ", endOpenStr);
    1003         GeometryTestUtils.assertContains("endDirection= ", endOpenStr);
    1004
    1005         final String doubleOpenStr = doubleOpenPath.toString();
    1006         GeometryTestUtils.assertContains("startDirection= ", doubleOpenStr);
    1007         GeometryTestUtils.assertContains("vertexSequence= ", doubleOpenStr);
    1008         GeometryTestUtils.assertContains("endDirection= ", doubleOpenStr);
    1009
    1010         final String nonOpenStr = nonOpenPath.toString();
    1011         GeometryTestUtils.assertContains("LinePath[vertexSequence= ", nonOpenStr);
    1012     }
    1013
    1014     @Test
    1015     public void testBuilder_prependAndAppend_segments() {
    1016         // arrange
    1017         final Vector2D p1 = Vector2D.ZERO;
    1018         final Vector2D p2 = Vector2D.of(1, 0);
    1019         final Vector2D p3 = Vector2D.of(1, 1);
    1020         final Vector2D p4 = Vector2D.of(1, 0);
    1021
    1022         final Segment a = Lines.segmentFromPoints(p1, p2, TEST_PRECISION);
    1023         final Segment b = Lines.segmentFromPoints(p2, p3, TEST_PRECISION);
    1024         final Segment c = Lines.segmentFromPoints(p3, p4, TEST_PRECISION);
    1025         final Segment d = Lines.segmentFromPoints(p4, p1, TEST_PRECISION);
    1026
    1027         final Builder builder = LinePath.builder(null);
    1028
    1029         // act
    1030         builder.prepend(b)
    1031             .append(c)
    1032             .prepend(a)
    1033             .append(d);
    1034
    1035         final LinePath path = builder.build();
    1036
    1037         // assert
    1038         final List<LineConvexSubset> segments = path.getElements();
    1039         Assert.assertEquals(4, segments.size());
    1040         Assert.assertSame(a, segments.get(0));
    1041         Assert.assertSame(b, segments.get(1));
    1042         Assert.assertSame(c, segments.get(2));
    1043         Assert.assertSame(d, segments.get(3));
    1044     }
    1045
    1046     @Test
    1047     public void testBuilder_prependAndAppend_disconnectedSegments() {
    1048         // arrange
    1049         final Segment a = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
    1050
    1051         final Builder builder = LinePath.builder(null);
    1052         builder.append(a);
    1053
    1054         // act
    1055         GeometryTestUtils.assertThrows(() -> {
    1056             builder.append(a);
    1057         }, IllegalStateException.class);
    1058
    1059         GeometryTestUtils.assertThrows(() -> {
    1060             builder.prepend(a);
    1061         }, IllegalStateException.class);
    1062     }
    1063
    1064     @Test
    1065     public void testBuilder_prependAndAppend_vertices() {
    1066         // arrange
    1067         final Vector2D p1 = Vector2D.ZERO;
    1068         final Vector2D p2 = Vector2D.of(1, 0);
    1069         final Vector2D p3 = Vector2D.of(1, 1);
    1070         final Vector2D p4 = Vector2D.of(1, 0);
    1071
    1072         final Builder builder = LinePath.builder(TEST_PRECISION);
    1073
    1074         // act
    1075         builder.prepend(p2)
    1076             .append(p3)
    1077             .prepend(p1)
    1078             .append(p4)
    1079             .append(p1);
    1080
    1081         final LinePath path = builder.build();
    1082
    1083         // assert
    1084         final List<LineConvexSubset> segments = path.getElements();
    1085         Assert.assertEquals(4, segments.size());
    1086         assertFiniteSegment(segments.get(0), p1, p2);
    1087         assertFiniteSegment(segments.get(1), p2, p3);
    1088         assertFiniteSegment(segments.get(2), p3, p4);
    1089         assertFiniteSegment(segments.get(3), p4, p1);
    1090     }
    1091
    1092     @Test
    1093     public void testBuilder_prependAndAppend_noPrecisionSpecified() {
    1094         // arrange
    1095         final Vector2D p = Vector2D.ZERO;
    1096         final Builder builder = LinePath.builder(null);
    1097
    1098         final String msg = "Unable to create line segment: no vertex precision specified";
    1099
    1100         // act/assert
    1101         GeometryTestUtils.assertThrows(() -> {
    1102             builder.append(p);
    1103         }, IllegalStateException.class, msg);
    1104
    1105         GeometryTestUtils.assertThrows(() -> {
    1106             builder.prepend(p);
    1107         }, IllegalStateException.class, msg);
    1108     }
    1109
    1110     @Test
    1111     public void testBuilder_prependAndAppend_addingToInfinitePath() {
    1112         // arrange
    1113         final Vector2D p = Vector2D.Unit.PLUS_X;
    1114         final Builder builder = LinePath.builder(TEST_PRECISION);
    1115
    1116         builder.append(Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION).span());
    1117
    1118         // act/assert
    1119         GeometryTestUtils.assertThrows(() -> {
    1120             builder.append(p);
    1121         }, IllegalStateException.class);
    1122
    1123         GeometryTestUtils.assertThrows(() -> {
    1124             builder.prepend(p);
    1125         }, IllegalStateException.class);
    1126     }
    1127
    1128     @Test
    1129     public void testBuilder_prependAndAppend_ignoresEquivalentVertices() {
    1130         // arrange
    1131         final Vector2D p = Vector2D.ZERO;
    1132
    1133         final Builder builder = LinePath.builder(TEST_PRECISION);
    1134         builder.append(p);
    1135
    1136         // act
    1137         builder.append(p)
    1138             .prepend(p)
    1139             .append(Vector2D.of(0, 1e-20))
    1140             .prepend(Vector2D.of(1e-20, 0));
    1141
    1142         builder.append(Vector2D.Unit.PLUS_X);
    1143
    1144         // assert
    1145         final LinePath path = builder.build();
    1146
    1147         final List<LineConvexSubset> segments = path.getElements();
    1148         Assert.assertEquals(1, segments.size());
    1149         assertFiniteSegment(segments.get(0), p, Vector2D.Unit.PLUS_X);
    1150     }
    1151
    1152     @Test
    1153     public void testBuilder_prependAndAppend_mixedVerticesAndSegments() {
    1154         // arrange
    1155         final Vector2D p1 = Vector2D.ZERO;
    1156         final Vector2D p2 = Vector2D.of(1, 0);
    1157         final Vector2D p3 = Vector2D.of(1, 1);
    1158         final Vector2D p4 = Vector2D.of(0, 1);
    1159
    1160         final Segment a = Lines.segmentFromPoints(p1, p2, TEST_PRECISION);
    1161         final Segment c = Lines.segmentFromPoints(p3, p4, TEST_PRECISION);
    1162
    1163         final Builder builder = LinePath.builder(TEST_PRECISION);
    1164
    1165         // act
    1166         builder.prepend(p2)
    1167             .append(p3)
    1168             .append(c)
    1169             .prepend(a)
    1170             .append(p1);
    1171
    1172         final LinePath path = builder.build();
    1173
    1174         // assert
    1175         final List<LineConvexSubset> segments = path.getElements();
    1176         Assert.assertEquals(4, segments.size());
    1177         assertFiniteSegment(segments.get(0), p1, p2);
    1178         assertFiniteSegment(segments.get(1), p2, p3);
    1179         assertFiniteSegment(segments.get(2), p3, p4);
    1180         assertFiniteSegment(segments.get(3), p4, p1);
    1181     }
    1182
    1183     @Test
    1184     public void testBuilder_appendVertices() {
    1185         // arrange
    1186         final Vector2D p1 = Vector2D.ZERO;
    1187         final Vector2D p2 = Vector2D.of(1, 0);
    1188         final Vector2D p3 = Vector2D.of(1, 1);
    1189         final Vector2D p4 = Vector2D.of(0, 1);
    1190
    1191         final Builder builder = LinePath.builder(TEST_PRECISION);
    1192
    1193         // act
    1194         builder.appendVertices(p1, p2)
    1195             .appendVertices(Arrays.asList(p3, p4, p1));
    1196
    1197         final LinePath path = builder.build();
    1198
    1199         // assert
    1200         final List<LineConvexSubset> segments = path.getElements();
    1201         Assert.assertEquals(4, segments.size());
    1202         assertFiniteSegment(segments.get(0), p1, p2);
    1203         assertFiniteSegment(segments.get(1), p2, p3);
    1204         assertFiniteSegment(segments.get(2), p3, p4);
    1205         assertFiniteSegment(segments.get(3), p4, p1);
    1206     }
    1207
    1208     @Test
    1209     public void testBuilder_prependVertices() {
    1210         // arrange
    1211         final Vector2D p1 = Vector2D.ZERO;
    1212         final Vector2D p2 = Vector2D.of(1, 0);
    1213         final Vector2D p3 = Vector2D.of(1, 1);
    1214         final Vector2D p4 = Vector2D.of(0, 1);
    1215
    1216         final Builder builder = LinePath.builder(TEST_PRECISION);
    1217
    1218         // act
    1219         builder.prependVertices(p3, p4, p1)
    1220             .prependVertices(Arrays.asList(p1, p2));
    1221
    1222         final LinePath path = builder.build();
    1223
    1224         // assert
    1225         final List<LineConvexSubset> segments = path.getElements();
    1226         Assert.assertEquals(4, segments.size());
    1227         assertFiniteSegment(segments.get(0), p1, p2);
    1228         assertFiniteSegment(segments.get(1), p2, p3);
    1229         assertFiniteSegment(segments.get(2), p3, p4);
    1230         assertFiniteSegment(segments.get(3), p4, p1);
    1231     }
    1232
    1233     @Test
    1234     public void testBuilder_close_notYetClosed() {
    1235         // arrange
    1236         final Vector2D p1 = Vector2D.ZERO;
    1237         final Vector2D p2 = Vector2D.of(1, 0);
    1238         final Vector2D p3 = Vector2D.of(1, 1);
    1239
    1240         final Builder builder = LinePath.builder(TEST_PRECISION);
    1241
    1242         // act
    1243         builder.append(p1)
    1244             .append(p2)
    1245             .append(p3);
    1246
    1247         final LinePath path = builder.close();
    1248
    1249         // assert
    1250         final List<LineConvexSubset> segments = path.getElements();
    1251         Assert.assertEquals(3, segments.size());
    1252         assertFiniteSegment(segments.get(0), p1, p2);
    1253         assertFiniteSegment(segments.get(1), p2, p3);
    1254         assertFiniteSegment(segments.get(2), p3, p1);
    1255     }
    1256
    1257     @Test
    1258     public void testBuilder_close_alreadyClosed() {
    1259         // arrange
    1260         final Vector2D p1 = Vector2D.ZERO;
    1261         final Vector2D p2 = Vector2D.of(1, 0);
    1262         final Vector2D p3 = Vector2D.of(1, 1);
    1263
    1264         final Builder builder = LinePath.builder(TEST_PRECISION);
    1265
    1266         // act
    1267         builder.append(p1)
    1268             .append(p2)
    1269             .append(p3)
    1270             .append(p1);
    1271
    1272         final LinePath path = builder.close();
    1273
    1274         // assert
    1275         final List<LineConvexSubset> segments = path.getElements();
    1276         Assert.assertEquals(3, segments.size());
    1277         assertFiniteSegment(segments.get(0), p1, p2);
    1278         assertFiniteSegment(segments.get(1), p2, p3);
    1279         assertFiniteSegment(segments.get(2), p3, p1);
    1280     }
    1281
    1282     @Test
    1283     public void testBuilder_close_infiniteSegmentAtStart() {
    1284         // arrange
    1285         final Builder builder = LinePath.builder(TEST_PRECISION);
    1286
    1287         builder.append(Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION)
    1288                 .reverseRayTo(1))
    1289             .append(Vector2D.of(1, 1));
    1290
    1291         // act/assert
    1292         GeometryTestUtils.assertThrows(builder::close, IllegalStateException.class,
    1293                 "Unable to close line path: line path is infinite");
    1294     }
    1295
    1296     @Test
    1297     public void testBuilder_close_infiniteSegmentAtEnd() {
    1298         // arrange
    1299         final Builder builder = LinePath.builder(TEST_PRECISION);
    1300
    1301         builder
    1302             .append(Vector2D.ZERO)
    1303             .append(Vector2D.Unit.PLUS_X)
    1304             .append(Lines.fromPointAndAngle(Vector2D.Unit.PLUS_X, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION)
    1305                 .rayFrom(0));
    1306
    1307         // act/assert
    1308         GeometryTestUtils.assertThrows(builder::close, IllegalStateException.class,
    1309                 "Unable to close line path: line path is infinite");
    1310     }
    1311
    1312     @Test
    1313     public void testBuilder_close_emptyPath() {
    1314         // arrange
    1315         final Builder builder = LinePath.builder(TEST_PRECISION);
    1316
    1317         // act
    1318         final LinePath path = builder.close();
    1319
    1320         // assert
    1321         Assert.assertEquals(0, path.getElements().size());
    1322     }
    1323
    1324     @Test
    1325     public void testBuilder_close_obtuseTriangle() {
    1326         // arrange
    1327         final Builder builder = LinePath.builder(TEST_PRECISION);
    1328         builder.appendVertices(Vector2D.ZERO, Vector2D.of(1, 0), Vector2D.of(2, 1));
    1329
    1330         // act
    1331         final LinePath path = builder.close();
    1332
    1333         // assert
    1334         Assert.assertEquals(3, path.getElements().size());
    1335         assertFiniteSegment(path.getElements().get(0), Vector2D.ZERO, Vector2D.of(1, 0));
    1336         assertFiniteSegment(path.getElements().get(1), Vector2D.of(1, 0), Vector2D.of(2, 1));
    1337         assertFiniteSegment(path.getElements().get(2), Vector2D.of(2, 1), Vector2D.ZERO);
    1338     }
    1339
    1340     private static void assertFiniteSegment(final LineConvexSubset segment, final Vector2D start, final Vector2D end) {
    1341         Assert.assertFalse(segment.isInfinite());
    1342         Assert.assertTrue(segment.isFinite());
    1343
    1344         EuclideanTestUtils.assertCoordinatesEqual(start, segment.getStartPoint(), TEST_EPS);
    1345         EuclideanTestUtils.assertCoordinatesEqual(end, segment.getEndPoint(), TEST_EPS);
    1346     }
    1347 }