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.core.partitioning;
    18
    19  import java.util.ArrayList;
    20  import java.util.Arrays;
    21  import java.util.Collections;
    22  import java.util.List;
    23
    24  import org.apache.commons.geometry.core.GeometryTestUtils;
    25  import org.apache.commons.geometry.core.Region;
    26  import org.apache.commons.geometry.core.RegionLocation;
    27  import org.apache.commons.geometry.core.Transform;
    28  import org.apache.commons.geometry.core.partitioning.test.PartitionTestUtils;
    29  import org.apache.commons.geometry.core.partitioning.test.TestLine;
    30  import org.apache.commons.geometry.core.partitioning.test.TestLineSegment;
    31  import org.apache.commons.geometry.core.partitioning.test.TestPoint2D;
    32  import org.apache.commons.geometry.core.partitioning.test.TestTransform2D;
    33  import org.junit.Assert;
    34  import org.junit.Test;
    35
    36  public class AbstractConvexHyperplaneBoundedRegionTest {
    37
    38      @Test
    39      public void testBoundaries_areUnmodifiable() {
    40          // arrange
    41          final StubRegion region = new StubRegion(new ArrayList<>());
    42
    43          // act/assert
    44          GeometryTestUtils.assertThrows(() -> {
    45              region.getBoundaries().add(TestLine.X_AXIS.span());
    46          }, UnsupportedOperationException.class);
    47      }
    48
    49      @Test
    50      public void testFull() {
    51          // act
    52          final StubRegion region = new StubRegion(Collections.emptyList());
    53
    54          // assert
    55          Assert.assertTrue(region.isFull());
    56          Assert.assertFalse(region.isEmpty());
    57      }
    58
    59      @Test
    60      public void testGetBoundarySize() {
    61          // arrange
    62          final TestPoint2D p1 = new TestPoint2D(1, 0);
    63          final TestPoint2D p2 = new TestPoint2D(2, 0);
    64          final TestPoint2D p3 = new TestPoint2D(1, 1);
    65
    66          // act/assert
    67          Assert.assertEquals(0, new StubRegion(Collections.emptyList()).getBoundarySize(), PartitionTestUtils.EPS);
    68          GeometryTestUtils.assertPositiveInfinity(new StubRegion(Collections.singletonList(TestLine.X_AXIS.span())).getBoundarySize());
    69          Assert.assertEquals(2 + Math.sqrt(2), new StubRegion(Arrays.asList(
    70                      new TestLineSegment(p1, p2),
    71                      new TestLineSegment(p2, p3),
    72                      new TestLineSegment(p3, p1)
    73                  )).getBoundarySize(), PartitionTestUtils.EPS);
    74      }
    75
    76      @Test
    77      public void testClassify() {
    78          // arrange
    79          final TestPoint2D p1 = new TestPoint2D(1, 0);
    80          final TestPoint2D p2 = new TestPoint2D(2, 0);
    81          final TestPoint2D p3 = new TestPoint2D(1, 1);
    82
    83          final StubRegion full = new StubRegion(Collections.emptyList());
    84          final StubRegion halfSpace = new StubRegion(Collections.singletonList(TestLine.X_AXIS.span()));
    85          final StubRegion triangle = new StubRegion(Arrays.asList(
    86                  new TestLineSegment(p1, p2),
    87                  new TestLineSegment(p2, p3),
    88                  new TestLineSegment(p3, p1)
    89              ));
    90
    91          // act/assert
    92          checkClassify(full, RegionLocation.INSIDE, TestPoint2D.ZERO, p1, p2, p3);
    93
    94          checkClassify(halfSpace, RegionLocation.INSIDE, new TestPoint2D(0, 1));
    95          checkClassify(halfSpace, RegionLocation.OUTSIDE, new TestPoint2D(0, -1));
    96          checkClassify(halfSpace, RegionLocation.BOUNDARY,
    97                  new TestPoint2D(-1, 0), new TestPoint2D(0, 0), new TestPoint2D(1, 0));
    98
    99          checkClassify(triangle, RegionLocation.INSIDE, new TestPoint2D(1.25, 0.25));
    100         checkClassify(triangle, RegionLocation.OUTSIDE, new TestPoint2D(-1, 0), new TestPoint2D(0, 0), new TestPoint2D(3, 0));
    101         checkClassify(triangle, RegionLocation.BOUNDARY, p1, p2, p3);
    102     }
    103
    104     @Test
    105     public void testProject() {
    106         // arrange
    107         final TestPoint2D p1 = new TestPoint2D(1, 0);
    108         final TestPoint2D p2 = new TestPoint2D(2, 0);
    109         final TestPoint2D p3 = new TestPoint2D(1, 1);
    110
    111         final StubRegion full = new StubRegion(Collections.emptyList());
    112         final StubRegion halfSpace = new StubRegion(Collections.singletonList(TestLine.X_AXIS.span()));
    113         final StubRegion triangle = new StubRegion(Arrays.asList(
    114                 new TestLineSegment(p1, p2),
    115                 new TestLineSegment(p2, p3),
    116                 new TestLineSegment(p3, p1)
    117             ));
    118
    119         // act/assert
    120         Assert.assertNull(full.project(TestPoint2D.ZERO));
    121         Assert.assertNull(full.project(new TestPoint2D(1, 1)));
    122
    123         PartitionTestUtils.assertPointsEqual(TestPoint2D.ZERO, halfSpace.project(new TestPoint2D(0, 1)));
    124         PartitionTestUtils.assertPointsEqual(TestPoint2D.ZERO, halfSpace.project(new TestPoint2D(0, 0)));
    125         PartitionTestUtils.assertPointsEqual(TestPoint2D.ZERO, halfSpace.project(new TestPoint2D(0, -1)));
    126
    127         PartitionTestUtils.assertPointsEqual(new TestPoint2D(1.25, 0), triangle.project(new TestPoint2D(1.25, 0.1)));
    128         PartitionTestUtils.assertPointsEqual(p1, triangle.project(TestPoint2D.ZERO));
    129         PartitionTestUtils.assertPointsEqual(p3, triangle.project(new TestPoint2D(0, 10)));
    130     }
    131
    132     @Test
    133     public void testTrim() {
    134         // arrange
    135         final TestPoint2D p1 = new TestPoint2D(1, 0);
    136         final TestPoint2D p2 = new TestPoint2D(2, 0);
    137         final TestPoint2D p3 = new TestPoint2D(2, 1);
    138         final TestPoint2D p4 = new TestPoint2D(1, 1);
    139
    140         final StubRegion full = new StubRegion(Collections.emptyList());
    141         final StubRegion halfSpace = new StubRegion(Collections.singletonList(TestLine.Y_AXIS.span()));
    142         final StubRegion square = new StubRegion(Arrays.asList(
    143                 new TestLineSegment(p1, p2),
    144                 new TestLineSegment(p2, p3),
    145                 new TestLineSegment(p3, p4),
    146                 new TestLineSegment(p4, p1)
    147             ));
    148
    149         final TestLineSegment segment = new TestLineSegment(new TestPoint2D(-1, 0.5), new TestPoint2D(4, 0.5));
    150
    151         // act/assert
    152         Assert.assertSame(segment, full.trim(segment));
    153
    154         final TestLineSegment trimmedA = halfSpace.trim(segment);
    155         PartitionTestUtils.assertPointsEqual(new TestPoint2D(-1, 0.5), trimmedA.getStartPoint());
    156         PartitionTestUtils.assertPointsEqual(new TestPoint2D(0, 0.5), trimmedA.getEndPoint());
    157
    158         final TestLineSegment trimmedB = square.trim(segment);
    159         PartitionTestUtils.assertPointsEqual(new TestPoint2D(1, 0.5), trimmedB.getStartPoint());
    160         PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 0.5), trimmedB.getEndPoint());
    161     }
    162
    163     @Test
    164     public void testSplit_full() {
    165         // arrange
    166         final StubRegion region = new StubRegion(Collections.emptyList());
    167
    168         final TestLine splitter = TestLine.X_AXIS;
    169
    170         // act
    171         final Split<StubRegion> split = region.split(splitter);
    172
    173         // assert
    174         Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
    175
    176         final StubRegion minus = split.getMinus();
    177         Assert.assertEquals(1, minus.getBoundaries().size());
    178         checkClassify(minus, RegionLocation.INSIDE, new TestPoint2D(0, 1));
    179         checkClassify(minus, RegionLocation.BOUNDARY, new TestPoint2D(0, 0));
    180         checkClassify(minus, RegionLocation.OUTSIDE, new TestPoint2D(0, -1));
    181
    182         final StubRegion plus = split.getPlus();
    183         Assert.assertEquals(1, plus.getBoundaries().size());
    184         checkClassify(plus, RegionLocation.OUTSIDE, new TestPoint2D(0, 1));
    185         checkClassify(plus, RegionLocation.BOUNDARY, new TestPoint2D(0, 0));
    186         checkClassify(plus, RegionLocation.INSIDE, new TestPoint2D(0, -1));
    187     }
    188
    189     @Test
    190     public void testSplit_parallel_splitterIsOutside_plusOnly() {
    191      // arrange
    192         final StubRegion region = new StubRegion(
    193                 Collections.singletonList(new TestLineSegment(new TestPoint2D(0, 1), new TestPoint2D(1, 1))));
    194
    195         final TestLine splitter = TestLine.X_AXIS.reverse();
    196
    197         // act
    198         final Split<StubRegion> split = region.split(splitter);
    199
    200         // assert
    201         Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
    202
    203         Assert.assertNull(split.getMinus());
    204         Assert.assertSame(region, split.getPlus());
    205     }
    206
    207     @Test
    208     public void testSplit_parallel_splitterIsOutside_minusOnly() {
    209      // arrange
    210         final StubRegion region = new StubRegion(
    211                 Collections.singletonList(new TestLineSegment(new TestPoint2D(0, 1), new TestPoint2D(1, 1))));
    212
    213         final TestLine splitter = TestLine.X_AXIS;
    214
    215         // act
    216         final Split<StubRegion> split = region.split(splitter);
    217
    218         // assert
    219         Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
    220
    221         Assert.assertSame(region, split.getMinus());
    222         Assert.assertNull(split.getPlus());
    223     }
    224
    225     @Test
    226     public void testSplit_parallel_splitterIsInside() {
    227      // arrange
    228         final StubRegion region = new StubRegion(
    229                 Collections.singletonList(new TestLineSegment(new TestPoint2D(1, 1), new TestPoint2D(0, 1))));
    230
    231         final TestLine splitter = TestLine.X_AXIS;
    232
    233         // act
    234         final Split<StubRegion> split = region.split(splitter);
    235
    236         // assert
    237         Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
    238
    239         final TestPoint2D p1 = new TestPoint2D(0, 1.5);
    240         final TestPoint2D p2 = new TestPoint2D(0, 0.5);
    241         final TestPoint2D p3 = new TestPoint2D(0, -0.5);
    242
    243         final StubRegion minus = split.getMinus();
    244         Assert.assertEquals(2, minus.getBoundaries().size());
    245         checkClassify(minus, RegionLocation.INSIDE, p2);
    246         checkClassify(minus, RegionLocation.OUTSIDE, p1, p3);
    247
    248         final StubRegion plus = split.getPlus();
    249         Assert.assertEquals(1, plus.getBoundaries().size());
    250         checkClassify(plus, RegionLocation.INSIDE, p3);
    251         checkClassify(plus, RegionLocation.OUTSIDE, p1, p2);
    252     }
    253
    254     @Test
    255     public void testSplit_coincident_sameOrientation() {
    256      // arrange
    257         final StubRegion region = new StubRegion(Collections.singletonList(TestLine.X_AXIS.span()));
    258
    259         final TestLine splitter = TestLine.X_AXIS;
    260
    261         // act
    262         final Split<StubRegion> split = region.split(splitter);
    263
    264         // assert
    265         Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
    266
    267         Assert.assertSame(region, split.getMinus());
    268         Assert.assertNull(split.getPlus());
    269     }
    270
    271     @Test
    272     public void testSplit_coincident_oppositeOrientation() {
    273      // arrange
    274         final StubRegion region = new StubRegion(Collections.singletonList(TestLine.X_AXIS.span()));
    275
    276         final TestLine splitter = TestLine.X_AXIS.reverse();
    277
    278         // act
    279         final Split<StubRegion> split = region.split(splitter);
    280
    281         // assert
    282         Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
    283
    284         Assert.assertNull(split.getMinus());
    285         Assert.assertSame(region, split.getPlus());
    286     }
    287
    288     @Test
    289     public void testSplit_finite_both() {
    290         // arrange
    291         final TestPoint2D p1 = new TestPoint2D(1, -0.5);
    292         final TestPoint2D p2 = new TestPoint2D(2, -0.5);
    293         final TestPoint2D p3 = new TestPoint2D(2, 0.5);
    294         final TestPoint2D p4 = new TestPoint2D(1, 0.5);
    295
    296         final StubRegion region = new StubRegion(Arrays.asList(
    297                     new TestLineSegment(p1, p2),
    298                     new TestLineSegment(p2, p3),
    299                     new TestLineSegment(p3, p4),
    300                     new TestLineSegment(p4, p1)
    301                 ));
    302
    303         final TestLine splitter = TestLine.X_AXIS;
    304
    305         // act
    306         final Split<StubRegion> split = region.split(splitter);
    307
    308         // assert
    309         Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
    310
    311         final StubRegion minus = split.getMinus();
    312         Assert.assertEquals(4, minus.getBoundaries().size());
    313         checkClassify(minus, RegionLocation.INSIDE, new TestPoint2D(1.5, 0.25));
    314         checkClassify(minus, RegionLocation.BOUNDARY, new TestPoint2D(1.5, 0));
    315         checkClassify(minus, RegionLocation.OUTSIDE, new TestPoint2D(1.5, -0.25));
    316
    317         final StubRegion plus = split.getPlus();
    318         Assert.assertEquals(4, plus.getBoundaries().size());
    319         checkClassify(plus, RegionLocation.OUTSIDE, new TestPoint2D(1.5, 0.25));
    320         checkClassify(plus, RegionLocation.BOUNDARY, new TestPoint2D(1.5, 0));
    321         checkClassify(plus, RegionLocation.INSIDE, new TestPoint2D(1.5, -0.25));
    322     }
    323
    324     // The following tests are designed to check the situation where there are
    325     // inconsistencies between how a splitter splits a set of boundaries and how
    326     // the boundaries split the splitter. For example, no portion of the splitter
    327     // may lie inside the region (on the minus sides of all boundaries), but some
    328     // of the boundaries may be determined to lie on both sides of the splitter.
    329     // One potential cause of this situation is accumulated floating point errors.
    330
    331     @Test
    332     public void testSplit_inconsistentBoundarySplitLocations_minus() {
    333         // arrange
    334         final TestLine a = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(1, 1));
    335         final TestLine b = new TestLine(new TestPoint2D(-1, 1), new TestPoint2D(0, 0));
    336
    337         final StubRegion region = new StubRegion(Arrays.asList(
    338                     new TestLineSegment(-1e-8, Double.POSITIVE_INFINITY, a),
    339                     new TestLineSegment(Double.NEGATIVE_INFINITY, 1e-8, b)
    340                 ));
    341
    342         final List<TestLineSegment> segments = region.getBoundaries();
    343         PartitionTestUtils.assertPointsEqual(segments.get(0).getStartPoint(), segments.get(1).getEndPoint());
    344
    345         final TestLine splitter = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(1, 0));
    346
    347         // act
    348         final Split<StubRegion> split = region.split(splitter);
    349
    350         // assert
    351         Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
    352         Assert.assertSame(region, split.getMinus());
    353         Assert.assertNull(split.getPlus());
    354     }
    355
    356     @Test
    357     public void testSplit_inconsistentBoundarySplitLocations_plus() {
    358         // arrange
    359         final TestLine a = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(1, 1));
    360         final TestLine b = new TestLine(new TestPoint2D(-1, 1), new TestPoint2D(0, 0));
    361
    362         final StubRegion region = new StubRegion(Arrays.asList(
    363                     new TestLineSegment(-1e-8, Double.POSITIVE_INFINITY, a),
    364                     new TestLineSegment(Double.NEGATIVE_INFINITY, 1e-8, b)
    365                 ));
    366
    367         final List<TestLineSegment> segments = region.getBoundaries();
    368         PartitionTestUtils.assertPointsEqual(segments.get(0).getStartPoint(), segments.get(1).getEndPoint());
    369
    370         final TestLine splitter = new TestLine(new TestPoint2D(1, 0), new TestPoint2D(0, 0));
    371
    372         // act
    373         final Split<StubRegion> split = region.split(splitter);
    374
    375         // assert
    376         Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
    377         Assert.assertNull(split.getMinus());
    378         Assert.assertSame(region, split.getPlus());
    379     }
    380
    381     @Test
    382     public void testSplit_inconsistentBoundarySplitLocations_trimmedNotNull_minus() {
    383         // arrange
    384         final TestLine a = new TestLine(new TestPoint2D(1e-8, 0), new TestPoint2D(1, 1));
    385         final TestLine b = new TestLine(new TestPoint2D(-1, 1), new TestPoint2D(-1e-8, 0));
    386
    387         final StubRegion region = new StubRegion(Arrays.asList(
    388                     new TestLineSegment(1e-8, Double.POSITIVE_INFINITY, a),
    389                     new TestLineSegment(Double.NEGATIVE_INFINITY, -1e-8, b)
    390                 ));
    391
    392         final List<TestLineSegment> segments = region.getBoundaries();
    393         PartitionTestUtils.assertPointsEqual(segments.get(0).getStartPoint(), segments.get(1).getEndPoint());
    394
    395         final TestLine splitter = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(1, 0));
    396
    397         // act
    398         final Split<StubRegion> split = region.split(splitter);
    399
    400         // assert
    401         Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
    402         Assert.assertSame(region, split.getMinus());
    403         Assert.assertNull(split.getPlus());
    404     }
    405
    406     @Test
    407     public void testSplit_inconsistentBoundarySplitLocations_trimmedNotNull_plus() {
    408         // arrange
    409         final TestLine a = new TestLine(new TestPoint2D(1e-8, 0), new TestPoint2D(1, 1));
    410         final TestLine b = new TestLine(new TestPoint2D(-1, 1), new TestPoint2D(-1e-8, 0));
    411
    412         final StubRegion region = new StubRegion(Arrays.asList(
    413                     new TestLineSegment(1e-8, Double.POSITIVE_INFINITY, a),
    414                     new TestLineSegment(Double.NEGATIVE_INFINITY, -1e-8, b)
    415                 ));
    416
    417         final List<TestLineSegment> segments = region.getBoundaries();
    418         PartitionTestUtils.assertPointsEqual(segments.get(0).getStartPoint(), segments.get(1).getEndPoint());
    419
    420         final TestLine splitter = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(-1, 0));
    421
    422         // act
    423         final Split<StubRegion> split = region.split(splitter);
    424
    425         // assert
    426         Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
    427         Assert.assertNull(split.getMinus());
    428         Assert.assertSame(region, split.getPlus());
    429     }
    430
    431     @Test
    432     public void testSplit_inconsistentBoundarySplitLocations_trimmedNotNull_neither() {
    433         // arrange
    434         final TestLine a = new TestLine(new TestPoint2D(1e-8, 0), new TestPoint2D(1, 1));
    435         final TestLine b = new TestLine(new TestPoint2D(-1, 1), new TestPoint2D(-1e-8, 0));
    436
    437         final StubRegion region = new StubRegion(Arrays.asList(
    438                     new TestLineSegment(0, 0, a),
    439                     new TestLineSegment(0, 0, b)
    440                 ));
    441
    442         final List<TestLineSegment> segments = region.getBoundaries();
    443         PartitionTestUtils.assertPointsEqual(segments.get(0).getStartPoint(), segments.get(1).getEndPoint());
    444
    445         final TestLine splitter = new TestLine(new TestPoint2D(0, 0), new TestPoint2D(1, 0));
    446
    447         // act
    448         final Split<StubRegion> split = region.split(splitter);
    449
    450         // assert
    451         Assert.assertEquals(SplitLocation.NEITHER, split.getLocation());
    452         Assert.assertNull(split.getMinus());
    453         Assert.assertNull(split.getPlus());
    454     }
    455
    456     @Test
    457     public void testTransform_full() {
    458         // arrange
    459         final StubRegion region = new StubRegion(Collections.emptyList());
    460
    461         final Transform<TestPoint2D> transform = new TestTransform2D(p -> new TestPoint2D(p.getX() + 1, p.getY() + 2));
    462
    463         // act
    464         final StubRegion transformed = region.transform(transform);
    465
    466         // assert
    467         Assert.assertTrue(transformed.isFull());
    468         Assert.assertFalse(transformed.isEmpty());
    469     }
    470
    471     @Test
    472     public void testTransform_infinite() {
    473         // arrange
    474         final TestLine line = TestLine.Y_AXIS;
    475
    476         final StubRegion region = new StubRegion(Collections.singletonList(line.span()));
    477
    478         final Transform<TestPoint2D> transform = new TestTransform2D(p -> new TestPoint2D(p.getX() + 1, p.getY() + 2));
    479
    480         // act
    481         final StubRegion transformed = region.transform(transform);
    482
    483         // assert
    484         final List<TestLineSegment> boundaries = transformed.getBoundaries();
    485
    486         Assert.assertEquals(1, boundaries.size());
    487
    488         final TestLineSegment a = boundaries.get(0);
    489         final TestLine aLine = a.getHyperplane();
    490         PartitionTestUtils.assertPointsEqual(aLine.getOrigin(), new TestPoint2D(1, 0));
    491         Assert.assertEquals(0.0, aLine.getDirectionX(), PartitionTestUtils.EPS);
    492         Assert.assertEquals(1.0, aLine.getDirectionY(), PartitionTestUtils.EPS);
    493
    494         GeometryTestUtils.assertNegativeInfinity(a.getStart());
    495         GeometryTestUtils.assertPositiveInfinity(a.getEnd());
    496     }
    497
    498     @Test
    499     public void testTransform_finite() {
    500         // arrange
    501         final TestPoint2D p1 = new TestPoint2D(1, 0);
    502         final TestPoint2D p2 = new TestPoint2D(2, 0);
    503         final TestPoint2D p3 = new TestPoint2D(1, 1);
    504
    505         final StubRegion region = new StubRegion(Arrays.asList(
    506                 new TestLineSegment(p1, p2),
    507                 new TestLineSegment(p2, p3),
    508                 new TestLineSegment(p3, p1)
    509             ));
    510
    511         final Transform<TestPoint2D> transform = new TestTransform2D(p -> new TestPoint2D(p.getX() + 1, p.getY() + 2));
    512
    513         // act
    514         final StubRegion transformed = region.transform(transform);
    515
    516         // assert
    517         final List<TestLineSegment> boundaries = transformed.getBoundaries();
    518
    519         Assert.assertEquals(3, boundaries.size());
    520
    521         final TestLineSegment a = boundaries.get(0);
    522         PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 2), a.getStartPoint());
    523         PartitionTestUtils.assertPointsEqual(new TestPoint2D(3, 2), a.getEndPoint());
    524
    525         final TestLineSegment b = boundaries.get(1);
    526         PartitionTestUtils.assertPointsEqual(new TestPoint2D(3, 2), b.getStartPoint());
    527         PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 3), b.getEndPoint());
    528
    529         final TestLineSegment c = boundaries.get(2);
    530         PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 3), c.getStartPoint());
    531         PartitionTestUtils.assertPointsEqual(new TestPoint2D(2, 2), c.getEndPoint());
    532     }
    533
    534     @Test
    535     public void testTransform_reflection() {
    536         // arrange
    537         final TestPoint2D p1 = new TestPoint2D(1, 0);
    538         final TestPoint2D p2 = new TestPoint2D(2, 0);
    539         final TestPoint2D p3 = new TestPoint2D(1, 1);
    540
    541         final StubRegion region = new StubRegion(Arrays.asList(
    542                 new TestLineSegment(p1, p2),
    543                 new TestLineSegment(p2, p3),
    544                 new TestLineSegment(p3, p1)
    545             ));
    546
    547         final Transform<TestPoint2D> transform = new TestTransform2D(p -> new TestPoint2D(-p.getX(), p.getY()));
    548
    549         // act
    550         final StubRegion transformed = region.transform(transform);
    551
    552         // assert
    553         final List<TestLineSegment> boundaries = transformed.getBoundaries();
    554
    555         Assert.assertEquals(3, boundaries.size());
    556
    557         final TestLineSegment a = boundaries.get(0);
    558         PartitionTestUtils.assertPointsEqual(new TestPoint2D(-2, 0), a.getStartPoint());
    559         PartitionTestUtils.assertPointsEqual(new TestPoint2D(-1, 0), a.getEndPoint());
    560
    561         final TestLineSegment b = boundaries.get(1);
    562         PartitionTestUtils.assertPointsEqual(new TestPoint2D(-1, 1), b.getStartPoint());
    563         PartitionTestUtils.assertPointsEqual(new TestPoint2D(-2, 0), b.getEndPoint());
    564
    565         final TestLineSegment c = boundaries.get(2);
    566         PartitionTestUtils.assertPointsEqual(new TestPoint2D(-1, 0), c.getStartPoint());
    567         PartitionTestUtils.assertPointsEqual(new TestPoint2D(-1, 1), c.getEndPoint());
    568     }
    569
    570     @Test
    571     public void testConvexRegionBoundaryBuilder_full() {
    572         // act
    573         final StubRegion region = StubRegion.fromBounds(Collections.emptyList());
    574
    575         // assert
    576         Assert.assertSame(StubRegion.FULL, region);
    577     }
    578
    579     @Test
    580     public void testConvexRegionBoundaryBuilder_singleLine() {
    581         // act
    582         final StubRegion region = StubRegion.fromBounds(Collections.singletonList(TestLine.Y_AXIS));
    583
    584         // assert
    585         Assert.assertEquals(1, region.getBoundaries().size());
    586
    587         checkClassify(region, RegionLocation.INSIDE, new TestPoint2D(-1, 0));
    588         checkClassify(region, RegionLocation.BOUNDARY, new TestPoint2D(0, 0));
    589         checkClassify(region, RegionLocation.OUTSIDE, new TestPoint2D(1, 0));
    590     }
    591
    592     @Test
    593     public void testConvexRegionBoundaryBuilder_multipleLines() {
    594         // act
    595         final StubRegion region = StubRegion.fromBounds(Arrays.asList(
    596                     TestLine.X_AXIS,
    597                     new TestLine(new TestPoint2D(1, 0), new TestPoint2D(0, 1)),
    598                     TestLine.Y_AXIS.reverse()
    599                 ));
    600
    601         // assert
    602         Assert.assertEquals(3, region.getBoundaries().size());
    603
    604         checkClassify(region, RegionLocation.INSIDE, new TestPoint2D(0.25, 0.25));
    605
    606         checkClassify(region, RegionLocation.BOUNDARY,
    607                 TestPoint2D.ZERO, new TestPoint2D(1, 0), new TestPoint2D(1, 0), new TestPoint2D(0.5, 0.5));
    608
    609         checkClassify(region, RegionLocation.OUTSIDE,
    610                 new TestPoint2D(-1, 0.5), new TestPoint2D(1, 0.5),
    611                 new TestPoint2D(0.5, 1), new TestPoint2D(0.5, -1));
    612     }
    613
    614     @Test
    615     public void testConvexRegionBoundaryBuilder_duplicateLines() {
    616         // act
    617         final StubRegion region = StubRegion.fromBounds(Arrays.asList(
    618                 TestLine.Y_AXIS,
    619                 TestLine.Y_AXIS,
    620                 new TestLine(new TestPoint2D(0, 0), new TestPoint2D(0, 1)),
    621                 TestLine.Y_AXIS));
    622
    623         // assert
    624         Assert.assertEquals(1, region.getBoundaries().size());
    625
    626         checkClassify(region, RegionLocation.INSIDE, new TestPoint2D(-1, 0));
    627         checkClassify(region, RegionLocation.BOUNDARY, new TestPoint2D(0, 0));
    628         checkClassify(region, RegionLocation.OUTSIDE, new TestPoint2D(1, 0));
    629     }
    630
    631     @Test
    632     public void testConvexRegionBoundaryBuilder() {
    633         // act/assert
    634         GeometryTestUtils.assertThrows(() -> {
    635             StubRegion.fromBounds(Arrays.asList(TestLine.X_AXIS, TestLine.X_AXIS.reverse()));
    636         }, IllegalArgumentException.class);
    637
    638         GeometryTestUtils.assertThrows(() -> {
    639             StubRegion.fromBounds(Arrays.asList(
    640                     TestLine.X_AXIS,
    641                     TestLine.Y_AXIS,
    642                     new TestLine(new TestPoint2D(1, 0), new TestPoint2D(0, -1)),
    643                     new TestLine(new TestPoint2D(1, 0), new TestPoint2D(0, -2))));
    644         }, IllegalArgumentException.class);
    645     }
    646
    647     @Test
    648     public void testToString() {
    649         // arrange
    650         final StubRegion region = new StubRegion(Collections.emptyList());
    651
    652         // act
    653         final String str = region.toString();
    654
    655         // assert
    656         Assert.assertTrue(str.contains("StubRegion"));
    657         Assert.assertTrue(str.contains("boundaries= "));
    658     }
    659
    660     private static void checkClassify(final Region<TestPoint2D> region, final RegionLocation loc, final TestPoint2D... pts) {
    661         for (final TestPoint2D pt : pts) {
    662             Assert.assertEquals("Unexpected location for point " + pt, loc, region.classify(pt));
    663         }
    664     }
    665
    666     private static final class StubRegion extends AbstractConvexHyperplaneBoundedRegion<TestPoint2D, TestLineSegment> {
    667
    668         private static final StubRegion FULL = new StubRegion(Collections.emptyList());
    669
    670         StubRegion(final List<TestLineSegment> boundaries) {
    671             super(boundaries);
    672         }
    673
    674         public StubRegion transform(final Transform<TestPoint2D> transform) {
    675             return transformInternal(transform, this, TestLineSegment.class, StubRegion::new);
    676         }
    677
    678         @Override
    679         public Split<StubRegion> split(final Hyperplane<TestPoint2D> splitter) {
    680             return splitInternal(splitter, this, TestLineSegment.class, StubRegion::new);
    681         }
    682
    683         @Override
    684         public TestLineSegment trim(final HyperplaneConvexSubset<TestPoint2D> subset) {
    685             return (TestLineSegment) super.trim(subset);
    686         }
    687
    688         @Override
    689         public double getSize() {
    690             throw new UnsupportedOperationException();
    691         }
    692
    693         @Override
    694         public TestPoint2D getCentroid() {
    695             throw new UnsupportedOperationException();
    696         }
    697
    698         public static StubRegion fromBounds(final Iterable<TestLine> boundingLines) {
    699             final List<TestLineSegment> segments = new ConvexRegionBoundaryBuilder<>(TestLineSegment.class)
    700                     .build(boundingLines);
    701             return segments.isEmpty() ? FULL : new StubRegion(segments);
    702         }
    703     }
    704 }