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