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 }