1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean.oned;
18
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.apache.commons.geometry.core.GeometryTestUtils;
24 import org.apache.commons.geometry.core.RegionLocation;
25 import org.apache.commons.geometry.core.partitioning.Split;
26 import org.apache.commons.geometry.core.partitioning.SplitLocation;
27 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
28 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
29 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
30 import org.apache.commons.geometry.euclidean.oned.RegionBSPTree1D.RegionNode1D;
31 import org.junit.Assert;
32 import org.junit.Test;
33
34 public class RegionBSPTree1DTest {
35
36 private static final double TEST_EPS = 1e-10;
37
38 private static final DoublePrecisionContext TEST_PRECISION =
39 new EpsilonDoublePrecisionContext(TEST_EPS);
40
41 @Test
42 public void testCopy() {
43
44 final RegionBSPTree1D tree = new RegionBSPTree1D(true);
45 tree.getRoot().cut(OrientedPoints.createPositiveFacing(1.0, TEST_PRECISION));
46
47
48 final RegionBSPTree1D copy = tree.copy();
49
50
51 Assert.assertNotSame(tree, copy);
52 Assert.assertEquals(3, copy.count());
53 }
54
55 @Test
56 public void testClassify_fullRegion() {
57
58 final RegionBSPTree1D tree = new RegionBSPTree1D(true);
59
60
61 checkClassify(tree, RegionLocation.INSIDE,
62 Double.NEGATIVE_INFINITY, -1, 0, 1, Double.POSITIVE_INFINITY);
63
64 checkClassify(tree, RegionLocation.OUTSIDE, Double.NaN);
65 }
66
67 @Test
68 public void testClassify_emptyRegion() {
69
70 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
71
72
73 checkClassify(tree, RegionLocation.OUTSIDE,
74 Double.NEGATIVE_INFINITY, -1, 0, 1, Double.POSITIVE_INFINITY);
75
76 checkClassify(tree, RegionLocation.OUTSIDE, Double.NaN);
77 }
78
79 @Test
80 public void testClassify_singleClosedInterval() {
81
82 final RegionBSPTree1D tree = new RegionBSPTree1D();
83 tree.insert(Arrays.asList(
84 OrientedPoints.createNegativeFacing(Vector1D.of(-1), TEST_PRECISION).span(),
85 OrientedPoints.createPositiveFacing(Vector1D.of(9), TEST_PRECISION).span()
86 ));
87
88
89 checkClassify(tree, RegionLocation.OUTSIDE, Double.NEGATIVE_INFINITY);
90 checkClassify(tree, RegionLocation.OUTSIDE, -2.0);
91 checkClassify(tree, RegionLocation.INSIDE, 0.0);
92 checkClassify(tree, RegionLocation.BOUNDARY, 9.0 - 1e-16);
93 checkClassify(tree, RegionLocation.BOUNDARY, 9.0 + 1e-16);
94 checkClassify(tree, RegionLocation.OUTSIDE, 10.0);
95 checkClassify(tree, RegionLocation.OUTSIDE, Double.POSITIVE_INFINITY);
96 }
97
98 @Test
99 public void testContains_fullRegion() {
100
101 final RegionBSPTree1D tree = new RegionBSPTree1D(true);
102
103
104 checkContains(tree, true,
105 Double.NEGATIVE_INFINITY, -1, 0, 1, Double.POSITIVE_INFINITY);
106
107 checkContains(tree, false, Double.NaN);
108 }
109
110 @Test
111 public void testContains_emptyRegion() {
112
113 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
114
115
116 checkContains(tree, false,
117 Double.NEGATIVE_INFINITY, -1, 0, 1, Double.POSITIVE_INFINITY);
118
119 checkContains(tree, false, Double.NaN);
120 }
121
122 @Test
123 public void testContains_singleClosedInterval() {
124
125 final RegionBSPTree1D tree = new RegionBSPTree1D();
126 tree.insert(Arrays.asList(
127 OrientedPoints.createNegativeFacing(Vector1D.of(-1), TEST_PRECISION).span(),
128 OrientedPoints.createPositiveFacing(Vector1D.of(9), TEST_PRECISION).span()
129 ));
130
131
132 checkContains(tree, false, Double.NEGATIVE_INFINITY);
133 checkContains(tree, false, -2.0);
134 checkContains(tree, true, 0.0);
135 checkContains(tree, true, 9.0 - 1e-16);
136 checkContains(tree, true, 9.0 + 1e-16);
137 checkContains(tree, false, 10.0);
138 checkContains(tree, false, Double.POSITIVE_INFINITY);
139 }
140
141 @Test
142 public void testGetBoundarySize_alwaysReturnsZero() {
143
144 Assert.assertEquals(0.0, RegionBSPTree1D.full().getBoundarySize(), TEST_EPS);
145 Assert.assertEquals(0.0, RegionBSPTree1D.empty().getBoundarySize(), TEST_EPS);
146 Assert.assertEquals(0.0, RegionBSPTree1D.from(
147 Interval.of(1, 2, TEST_PRECISION),
148 Interval.of(4, 5, TEST_PRECISION)
149 ).getBoundarySize(), TEST_EPS);
150 }
151
152 @Test
153 public void testProject_full() {
154
155 final RegionBSPTree1D full = RegionBSPTree1D.full();
156
157
158 Assert.assertNull(full.project(Vector1D.of(Double.NEGATIVE_INFINITY)));
159 Assert.assertNull(full.project(Vector1D.of(0)));
160 Assert.assertNull(full.project(Vector1D.of(Double.POSITIVE_INFINITY)));
161 }
162
163 @Test
164 public void testProject_empty() {
165
166 final RegionBSPTree1D empty = RegionBSPTree1D.empty();
167
168
169 Assert.assertNull(empty.project(Vector1D.of(Double.NEGATIVE_INFINITY)));
170 Assert.assertNull(empty.project(Vector1D.of(0)));
171 Assert.assertNull(empty.project(Vector1D.of(Double.POSITIVE_INFINITY)));
172 }
173
174 @Test
175 public void testProject_singlePoint() {
176
177 final RegionBSPTree1D tree = RegionBSPTree1D.from(Interval.point(1, TEST_PRECISION));
178
179
180 checkBoundaryProjection(tree, -1, 1);
181 checkBoundaryProjection(tree, 0, 1);
182
183 checkBoundaryProjection(tree, 1, 1);
184
185 checkBoundaryProjection(tree, 2, 1);
186 checkBoundaryProjection(tree, 3, 1);
187
188 checkBoundaryProjection(tree, Double.NEGATIVE_INFINITY, 1);
189 checkBoundaryProjection(tree, Double.POSITIVE_INFINITY, 1);
190 }
191
192 @Test
193 public void testProject_noMinBoundary() {
194
195 final RegionBSPTree1D tree = RegionBSPTree1D.from(Interval.of(Double.NEGATIVE_INFINITY, 1, TEST_PRECISION));
196
197
198 checkBoundaryProjection(tree, -1, 1);
199 checkBoundaryProjection(tree, 0, 1);
200 checkBoundaryProjection(tree, 1, 1);
201 checkBoundaryProjection(tree, 2, 1);
202 checkBoundaryProjection(tree, 3, 1);
203
204 checkBoundaryProjection(tree, Double.NEGATIVE_INFINITY, 1);
205 checkBoundaryProjection(tree, Double.POSITIVE_INFINITY, 1);
206 }
207
208 @Test
209 public void testProject_noMaxBoundary() {
210
211 final RegionBSPTree1D tree = RegionBSPTree1D.from(Interval.of(1, Double.POSITIVE_INFINITY, TEST_PRECISION));
212
213
214 checkBoundaryProjection(tree, -1, 1);
215 checkBoundaryProjection(tree, 0, 1);
216 checkBoundaryProjection(tree, 1, 1);
217 checkBoundaryProjection(tree, 2, 1);
218 checkBoundaryProjection(tree, 3, 1);
219
220 checkBoundaryProjection(tree, Double.NEGATIVE_INFINITY, 1);
221 checkBoundaryProjection(tree, Double.POSITIVE_INFINITY, 1);
222 }
223
224 @Test
225 public void testProject_closedInterval() {
226
227 final RegionBSPTree1D tree = RegionBSPTree1D.from(Interval.of(1, 3, TEST_PRECISION));
228
229
230 checkBoundaryProjection(tree, -1, 1);
231 checkBoundaryProjection(tree, 0, 1);
232 checkBoundaryProjection(tree, 1, 1);
233
234 checkBoundaryProjection(tree, 1.9, 1);
235 checkBoundaryProjection(tree, 2, 1);
236 checkBoundaryProjection(tree, 2.1, 3);
237
238 checkBoundaryProjection(tree, 3, 3);
239 checkBoundaryProjection(tree, 4, 3);
240 checkBoundaryProjection(tree, 5, 3);
241
242 checkBoundaryProjection(tree, Double.NEGATIVE_INFINITY, 1);
243 checkBoundaryProjection(tree, Double.POSITIVE_INFINITY, 3);
244 }
245
246 @Test
247 public void testProject_multipleIntervals() {
248
249 final RegionBSPTree1D tree = RegionBSPTree1D.from(
250 Interval.max(-1, TEST_PRECISION),
251 Interval.point(1, TEST_PRECISION),
252 Interval.of(2, 3, TEST_PRECISION),
253 Interval.of(5, 6, TEST_PRECISION)
254 );
255
256
257 checkBoundaryProjection(tree, Double.NEGATIVE_INFINITY, -1);
258 checkBoundaryProjection(tree, -2, -1);
259 checkBoundaryProjection(tree, -1, -1);
260
261 checkBoundaryProjection(tree, -0.5, -1);
262 checkBoundaryProjection(tree, 0, -1);
263 checkBoundaryProjection(tree, 0.5, 1);
264
265 checkBoundaryProjection(tree, 0.9, 1);
266 checkBoundaryProjection(tree, 1, 1);
267 checkBoundaryProjection(tree, 1.1, 1);
268
269 checkBoundaryProjection(tree, 0.5, 1);
270
271 checkBoundaryProjection(tree, 1.9, 2);
272 checkBoundaryProjection(tree, 2, 2);
273 checkBoundaryProjection(tree, 2.1, 2);
274 checkBoundaryProjection(tree, 2.5, 2);
275 checkBoundaryProjection(tree, 2.9, 3);
276 checkBoundaryProjection(tree, 3, 3);
277 checkBoundaryProjection(tree, 3.1, 3);
278
279 checkBoundaryProjection(tree, 3.9, 3);
280 checkBoundaryProjection(tree, 4, 3);
281 checkBoundaryProjection(tree, 4.1, 5);
282
283 checkBoundaryProjection(tree, 4.9, 5);
284 checkBoundaryProjection(tree, 5, 5);
285 checkBoundaryProjection(tree, 5.1, 5);
286 checkBoundaryProjection(tree, 5.49, 5);
287 checkBoundaryProjection(tree, 5.5, 5);
288 checkBoundaryProjection(tree, 5.51, 6);
289 checkBoundaryProjection(tree, 5.9, 6);
290 checkBoundaryProjection(tree, 6, 6);
291 checkBoundaryProjection(tree, 6.1, 6);
292
293 checkBoundaryProjection(tree, 7, 6);
294
295 checkBoundaryProjection(tree, Double.POSITIVE_INFINITY, 6);
296 }
297
298 @Test
299 public void testAdd_interval() {
300
301 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
302
303
304 tree.add(Interval.of(Double.NEGATIVE_INFINITY, -10, TEST_PRECISION));
305 tree.add(Interval.of(-1, 1, TEST_PRECISION));
306 tree.add(Interval.of(10, Double.POSITIVE_INFINITY, TEST_PRECISION));
307
308
309 checkClassify(tree, RegionLocation.INSIDE,
310 Double.NEGATIVE_INFINITY, -11, 0, 11, Double.POSITIVE_INFINITY);
311
312 checkClassify(tree, RegionLocation.BOUNDARY, -10, -1, 1, 10);
313
314 checkClassify(tree, RegionLocation.OUTSIDE, -9, -2, 2, 9);
315 }
316
317 @Test
318 public void testAdd_adjacentIntervals() {
319
320 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
321
322
323 tree.add(Interval.of(1, 2, TEST_PRECISION));
324 tree.add(Interval.of(2, 3, TEST_PRECISION));
325
326
327 checkClassify(tree, RegionLocation.INSIDE, 1.1, 2, 2.9);
328
329 checkClassify(tree, RegionLocation.BOUNDARY, 1, 3);
330
331 checkClassify(tree, RegionLocation.OUTSIDE,
332 Double.NEGATIVE_INFINITY, 0, 0.9, 3.1, 4, Double.POSITIVE_INFINITY);
333 }
334
335 @Test
336 public void testAdd_addFullInterval() {
337
338 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
339
340
341 tree.add(Interval.of(-1, 1, TEST_PRECISION));
342 tree.add(Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION));
343
344
345 Assert.assertTrue(tree.isFull());
346 Assert.assertEquals(1, tree.count());
347 }
348
349 @Test
350 public void testAdd_interval_duplicateBoundaryPoint() {
351
352 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
353
354
355 tree.add(Interval.of(1, 2, TEST_PRECISION));
356 tree.add(Interval.of(2, 3, TEST_PRECISION));
357 tree.add(Interval.of(1, 2, TEST_PRECISION));
358 tree.add(Interval.of(0, 1, TEST_PRECISION));
359
360
361 checkClassify(tree, RegionLocation.INSIDE, 0.1, 1, 2, 2.9);
362
363 checkClassify(tree, RegionLocation.BOUNDARY, 0, 3);
364
365 checkClassify(tree, RegionLocation.OUTSIDE, -1, -0.1, 3.1, 4);
366 }
367
368 @Test
369 public void testToIntervals_fullRegion() {
370
371 final RegionBSPTree1D tree = new RegionBSPTree1D(true);
372
373
374 final List<Interval> intervals = tree.toIntervals();
375
376
377 Assert.assertEquals(1, intervals.size());
378 checkInterval(intervals.get(0), Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
379 }
380
381 @Test
382 public void testToIntervals_emptyRegion() {
383
384 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
385
386
387 final List<Interval> intervals = tree.toIntervals();
388
389
390 Assert.assertEquals(0, intervals.size());
391 }
392
393 @Test
394 public void testToIntervals_halfOpen_negative() {
395
396 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
397
398 final RegionBSPTree1D tree = new RegionBSPTree1D();
399 tree.getRoot().cut(OrientedPoints.fromLocationAndDirection(1.0, true, precision));
400
401
402 final List<Interval> intervals = tree.toIntervals();
403
404
405 Assert.assertEquals(1, intervals.size());
406 checkInterval(intervals.get(0), Double.NEGATIVE_INFINITY, 1);
407 }
408
409 @Test
410 public void testToIntervals_halfOpen_positive() {
411
412 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
413
414 final RegionBSPTree1D tree = new RegionBSPTree1D();
415 tree.getRoot().cut(OrientedPoints.fromLocationAndDirection(-1.0, false, precision));
416
417
418 final List<Interval> intervals = tree.toIntervals();
419
420
421 Assert.assertEquals(1, intervals.size());
422 checkInterval(intervals.get(0), -1, Double.POSITIVE_INFINITY);
423 }
424
425 @Test
426 public void testToIntervals_singleClosedInterval() {
427
428 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
429
430 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
431 tree.add(Interval.of(-1, 1, precision));
432
433
434 final List<Interval> intervals = tree.toIntervals();
435
436
437 Assert.assertEquals(1, intervals.size());
438 checkInterval(intervals.get(0), -1, 1);
439 }
440
441 @Test
442 public void testToIntervals_singleClosedInterval_complement() {
443
444 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
445
446 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
447 tree.add(Interval.of(-1, 1, precision));
448 tree.complement();
449
450
451 final List<Interval> intervals = tree.toIntervals();
452
453
454 Assert.assertEquals(2, intervals.size());
455 checkInterval(intervals.get(0), Double.NEGATIVE_INFINITY, -1);
456 checkInterval(intervals.get(1), 1, Double.POSITIVE_INFINITY);
457 }
458
459 @Test
460 public void testToIntervals_openAndClosedIntervals() {
461
462 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
463
464 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
465 tree.add(Interval.of(Double.NEGATIVE_INFINITY, -10, precision));
466 tree.add(Interval.of(-1, 1, precision));
467 tree.add(Interval.of(10, Double.POSITIVE_INFINITY, precision));
468
469
470 final List<Interval> intervals = tree.toIntervals();
471
472
473 Assert.assertEquals(3, intervals.size());
474 checkInterval(intervals.get(0), Double.NEGATIVE_INFINITY, -10);
475 checkInterval(intervals.get(1), -1, 1);
476 checkInterval(intervals.get(2), 10, Double.POSITIVE_INFINITY);
477 }
478
479 @Test
480 public void testToIntervals_singlePoint() {
481
482 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
483
484 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
485 tree.add(Interval.of(1, 1, precision));
486
487
488 final List<Interval> intervals = tree.toIntervals();
489
490
491 Assert.assertEquals(1, intervals.size());
492 checkInterval(intervals.get(0), 1, 1);
493 }
494
495 @Test
496 public void testToIntervals_singlePoint_complement() {
497
498 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
499
500 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
501 tree.add(Interval.of(1, 1, precision));
502 tree.complement();
503
504
505 final List<Interval> intervals = tree.toIntervals();
506
507
508 Assert.assertEquals(2, intervals.size());
509 checkInterval(intervals.get(0), Double.NEGATIVE_INFINITY, 1);
510 checkInterval(intervals.get(1), 1, Double.POSITIVE_INFINITY);
511 }
512
513 @Test
514 public void testToIntervals_multiplePoints() {
515
516 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
517
518 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
519 tree.add(Interval.of(1, 1, precision));
520 tree.add(Interval.of(2, 2, precision));
521
522
523 final List<Interval> intervals = tree.toIntervals();
524
525
526 Assert.assertEquals(2, intervals.size());
527 checkInterval(intervals.get(0), 1, 1);
528 checkInterval(intervals.get(1), 2, 2);
529 }
530
531 @Test
532 public void testToIntervals_multiplePoints_complement() {
533
534 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
535
536 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
537 tree.add(Interval.of(1, 1, precision));
538 tree.add(Interval.of(2, 2, precision));
539 tree.complement();
540
541
542 final List<Interval> intervals = tree.toIntervals();
543
544
545 Assert.assertEquals(3, intervals.size());
546 checkInterval(intervals.get(0), Double.NEGATIVE_INFINITY, 1);
547 checkInterval(intervals.get(1), 1, 2);
548 checkInterval(intervals.get(2), 2, Double.POSITIVE_INFINITY);
549 }
550
551 @Test
552 public void testToIntervals_adjacentIntervals() {
553
554 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
555
556 tree.add(Interval.of(1, 2, TEST_PRECISION));
557 tree.add(Interval.of(2, 3, TEST_PRECISION));
558
559
560 final List<Interval> intervals = tree.toIntervals();
561
562
563 Assert.assertEquals(1, intervals.size());
564 checkInterval(intervals.get(0), 1, 3);
565 }
566
567 @Test
568 public void testToIntervals_multipleAdjacentIntervals() {
569
570 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
571
572 tree.add(Interval.of(1, 2, TEST_PRECISION));
573 tree.add(Interval.of(2, 3, TEST_PRECISION));
574 tree.add(Interval.of(3, 4, TEST_PRECISION));
575
576 tree.add(Interval.of(-2, -1, TEST_PRECISION));
577 tree.add(Interval.of(5, 6, TEST_PRECISION));
578
579
580 final List<Interval> intervals = tree.toIntervals();
581
582
583 Assert.assertEquals(3, intervals.size());
584 checkInterval(intervals.get(0), -2, -1);
585 checkInterval(intervals.get(1), 1, 4);
586 checkInterval(intervals.get(2), 5, 6);
587 }
588
589 @Test
590 public void testToIntervals() {
591
592 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-2);
593
594 final RegionBSPTree1D tree = new RegionBSPTree1D(false);
595 tree.add(Interval.of(-1, 6, precision));
596
597
598 final List<Interval> intervals = tree.toIntervals();
599
600
601 Assert.assertEquals(1, intervals.size());
602 checkInterval(intervals.get(0), -1, 6);
603 }
604
605 @Test
606 public void testGetNodeRegion() {
607
608 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
609
610 final RegionNode1D root = tree.getRoot();
611 root.cut(OrientedPoints.createPositiveFacing(1.0, TEST_PRECISION));
612
613 final RegionNode1D minus = root.getMinus();
614 minus.cut(OrientedPoints.createNegativeFacing(0.0, TEST_PRECISION));
615
616
617 checkInterval(root.getNodeRegion(), Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
618
619 checkInterval(minus.getNodeRegion(), Double.NEGATIVE_INFINITY, 1.0);
620 checkInterval(root.getPlus().getNodeRegion(), 1.0, Double.POSITIVE_INFINITY);
621
622 checkInterval(minus.getPlus().getNodeRegion(), Double.NEGATIVE_INFINITY, 0.0);
623 checkInterval(minus.getMinus().getNodeRegion(), 0.0, 1.0);
624 }
625
626 @Test
627 public void testTransform_full() {
628
629 final RegionBSPTree1D tree = RegionBSPTree1D.full();
630
631 final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(2);
632
633
634 tree.transform(transform);
635
636
637 Assert.assertTrue(tree.isFull());
638 }
639
640 @Test
641 public void testTransform_noReflection() {
642
643 final RegionBSPTree1D tree = RegionBSPTree1D.from(
644 Interval.of(1, 2, TEST_PRECISION),
645 Interval.min(3, TEST_PRECISION)
646 );
647
648 final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(2)
649 .translate(3);
650
651
652 tree.transform(transform);
653
654
655 final List<Interval> intervals = tree.toIntervals();
656
657 Assert.assertEquals(2, intervals.size());
658 checkInterval(intervals.get(0), 5, 7);
659 checkInterval(intervals.get(1), 9, Double.POSITIVE_INFINITY);
660 }
661
662 @Test
663 public void testTransform_withReflection() {
664
665 final RegionBSPTree1D tree = RegionBSPTree1D.from(
666 Interval.of(1, 2, TEST_PRECISION),
667 Interval.min(3, TEST_PRECISION)
668 );
669
670 final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(-2)
671 .translate(3);
672
673
674 tree.transform(transform);
675
676
677 final List<Interval> intervals = tree.toIntervals();
678
679 Assert.assertEquals(2, intervals.size());
680 checkInterval(intervals.get(0), Double.NEGATIVE_INFINITY, -3);
681 checkInterval(intervals.get(1), -1, 1);
682 }
683
684 @Test
685 public void testTransform_withReflection_functionBasedTransform() {
686
687 final RegionBSPTree1D tree = RegionBSPTree1D.from(
688 Interval.of(1, 2, TEST_PRECISION),
689 Interval.min(3, TEST_PRECISION)
690 );
691
692 final AffineTransformMatrix1D transform = AffineTransformMatrix1D.from(Vector1D::negate);
693
694
695 tree.transform(transform);
696
697
698 final List<Interval> intervals = tree.toIntervals();
699
700 Assert.assertEquals(2, intervals.size());
701 checkInterval(intervals.get(0), Double.NEGATIVE_INFINITY, -3);
702 checkInterval(intervals.get(1), -2, -1);
703 }
704
705 @Test
706 public void testSplit_full() {
707
708 final RegionBSPTree1D tree = RegionBSPTree1D.full();
709 final OrientedPoint splitter = OrientedPoints.fromLocationAndDirection(2, true, TEST_PRECISION);
710
711
712 final Split<RegionBSPTree1D> split = tree.split(splitter);
713
714
715 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
716
717 final List<Interval> minusIntervals = split.getMinus().toIntervals();
718 Assert.assertEquals(1, minusIntervals.size());
719 checkInterval(minusIntervals.get(0), Double.NEGATIVE_INFINITY, 2);
720
721 final List<Interval> plusIntervals = split.getPlus().toIntervals();
722 Assert.assertEquals(1, plusIntervals.size());
723 checkInterval(plusIntervals.get(0), 2, Double.POSITIVE_INFINITY);
724 }
725
726 @Test
727 public void testSplit_empty() {
728
729 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
730 final OrientedPoint splitter = OrientedPoints.fromLocationAndDirection(2, true, TEST_PRECISION);
731
732
733 final Split<RegionBSPTree1D> split = tree.split(splitter);
734
735
736 Assert.assertEquals(SplitLocation.NEITHER, split.getLocation());
737
738 Assert.assertNull(split.getMinus());
739 Assert.assertNull(split.getPlus());
740 }
741
742 @Test
743 public void testSplit_bothSides() {
744
745 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
746 tree.add(Interval.max(-2, TEST_PRECISION));
747 tree.add(Interval.of(1, 4, TEST_PRECISION));
748
749 final OrientedPoint splitter = OrientedPoints.fromLocationAndDirection(2, false, TEST_PRECISION);
750
751
752 final Split<RegionBSPTree1D> split = tree.split(splitter);
753
754
755 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
756
757 final List<Interval> minusIntervals = split.getMinus().toIntervals();
758 Assert.assertEquals(1, minusIntervals.size());
759 checkInterval(minusIntervals.get(0), 2, 4);
760
761 final List<Interval> plusIntervals = split.getPlus().toIntervals();
762 Assert.assertEquals(2, plusIntervals.size());
763 checkInterval(plusIntervals.get(0), Double.NEGATIVE_INFINITY, -2);
764 checkInterval(plusIntervals.get(1), 1, 2);
765 }
766
767 @Test
768 public void testSplit_splitterOnBoundary_minus() {
769
770 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
771 tree.add(Interval.of(1, 4, TEST_PRECISION));
772
773 final OrientedPoint splitter = OrientedPoints.fromLocationAndDirection(1, false, TEST_PRECISION);
774
775
776 final Split<RegionBSPTree1D> split = tree.split(splitter);
777
778
779 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
780
781 final List<Interval> minusIntervals = split.getMinus().toIntervals();
782 Assert.assertEquals(1, minusIntervals.size());
783 checkInterval(minusIntervals.get(0), 1, 4);
784
785 Assert.assertNull(split.getPlus());
786 }
787
788 @Test
789 public void testSplit_splitterOnBoundary_plus() {
790
791 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
792 tree.add(Interval.of(1, 4, TEST_PRECISION));
793
794 final OrientedPoint splitter = OrientedPoints.fromLocationAndDirection(4, false, TEST_PRECISION);
795
796
797 final Split<RegionBSPTree1D> split = tree.split(splitter);
798
799
800 Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
801
802 Assert.assertNull(split.getMinus());
803
804 final List<Interval> plusIntervals = split.getPlus().toIntervals();
805 Assert.assertEquals(1, plusIntervals.size());
806 checkInterval(plusIntervals.get(0), 1, 4);
807 }
808
809 @Test
810 public void testSplit_point() {
811
812 final RegionBSPTree1D tree = RegionBSPTree1D.from(Interval.point(1.0, TEST_PRECISION));
813
814 final OrientedPoint splitter = OrientedPoints.fromLocationAndDirection(2, false, TEST_PRECISION);
815
816
817 final Split<RegionBSPTree1D> split = tree.split(splitter);
818
819
820 Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
821
822 Assert.assertNull(split.getMinus());
823
824 final List<Interval> plusIntervals = split.getPlus().toIntervals();
825 Assert.assertEquals(1, plusIntervals.size());
826 checkInterval(plusIntervals.get(0), 1, 1);
827 }
828
829 @Test
830 public void testSplit_point_splitOnPoint_positiveFacingSplitter() {
831
832 final RegionBSPTree1D tree = RegionBSPTree1D.from(Interval.point(1, TEST_PRECISION));
833
834 final OrientedPoint splitter = OrientedPoints.fromLocationAndDirection(1, true, TEST_PRECISION);
835
836
837 final Split<RegionBSPTree1D> split = tree.split(splitter);
838
839
840 Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
841
842 Assert.assertNull(split.getMinus());
843
844 final List<Interval> plusIntervals = split.getPlus().toIntervals();
845 Assert.assertEquals(1, plusIntervals.size());
846 checkInterval(plusIntervals.get(0), 1, 1);
847 }
848
849 @Test
850 public void testSplit_point_splitOnPoint_negativeFacingSplitter() {
851
852 final RegionBSPTree1D tree = RegionBSPTree1D.from(Interval.point(1, TEST_PRECISION));
853
854 final OrientedPoint splitter = OrientedPoints.fromLocationAndDirection(1, false, TEST_PRECISION);
855
856
857 final Split<RegionBSPTree1D> split = tree.split(splitter);
858
859
860 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
861
862 final List<Interval> minusIntervals = split.getMinus().toIntervals();
863 Assert.assertEquals(1, minusIntervals.size());
864 checkInterval(minusIntervals.get(0), 1, 1);
865
866 Assert.assertNull(split.getPlus());
867 }
868
869 @Test
870 public void testGetSize_infinite() {
871
872 final RegionBSPTree1D full = RegionBSPTree1D.full();
873
874 final RegionBSPTree1D posHalfSpace = RegionBSPTree1D.empty();
875 posHalfSpace.getRoot().cut(OrientedPoints.createNegativeFacing(-2.0, TEST_PRECISION));
876
877 final RegionBSPTree1D negHalfSpace = RegionBSPTree1D.empty();
878 negHalfSpace.getRoot().cut(OrientedPoints.createPositiveFacing(3.0, TEST_PRECISION));
879
880
881 Assert.assertEquals(Double.POSITIVE_INFINITY, full.getSize(), TEST_EPS);
882 Assert.assertEquals(Double.POSITIVE_INFINITY, posHalfSpace.getSize(), TEST_EPS);
883 Assert.assertEquals(Double.POSITIVE_INFINITY, negHalfSpace.getSize(), TEST_EPS);
884 }
885
886 @Test
887 public void testGetSize_empty() {
888
889 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
890
891
892 Assert.assertEquals(0, tree.getSize(), TEST_EPS);
893 }
894
895 @Test
896 public void testGetSize_exactPoints() {
897
898 final RegionBSPTree1D singlePoint = RegionBSPTree1D.empty();
899 singlePoint.add(Interval.of(1, 1, TEST_PRECISION));
900
901 final RegionBSPTree1D multiplePoints = RegionBSPTree1D.empty();
902 multiplePoints.add(Interval.of(1, 1, TEST_PRECISION));
903 multiplePoints.add(Interval.of(-1, -1, TEST_PRECISION));
904 multiplePoints.add(Interval.of(2, 2, TEST_PRECISION));
905
906
907 Assert.assertEquals(0, singlePoint.getSize(), TEST_EPS);
908 Assert.assertEquals(0, multiplePoints.getSize(), TEST_EPS);
909 }
910
911 @Test
912 public void testGetSize_pointsWithinPrecision() {
913
914 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-1);
915
916 final RegionBSPTree1D singlePoint = RegionBSPTree1D.empty();
917 singlePoint.add(Interval.of(1, 1.02, precision));
918
919 final RegionBSPTree1D multiplePoints = RegionBSPTree1D.empty();
920 multiplePoints.add(Interval.of(1, 1.02, precision));
921 multiplePoints.add(Interval.of(-1.02, -1, precision));
922 multiplePoints.add(Interval.of(2, 2.02, precision));
923
924
925 Assert.assertEquals(0.02, singlePoint.getSize(), TEST_EPS);
926 Assert.assertEquals(0.06, multiplePoints.getSize(), TEST_EPS);
927 }
928
929 @Test
930 public void testGetSize_nonEmptyIntervals() {
931
932 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
933 tree.add(Interval.of(1, 2, TEST_PRECISION));
934 tree.add(Interval.of(3, 5, TEST_PRECISION));
935
936
937 Assert.assertEquals(3, tree.getSize(), TEST_EPS);
938 }
939
940 @Test
941 public void testGetSize_intervalWithPoints() {
942
943 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
944 tree.add(Interval.of(1, 2, TEST_PRECISION));
945 tree.add(Interval.of(3, 3, TEST_PRECISION));
946 tree.add(Interval.of(5, 5, TEST_PRECISION));
947
948
949 Assert.assertEquals(1, tree.getSize(), TEST_EPS);
950 }
951
952 @Test
953 public void testGetSize_complementedRegion() {
954
955 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
956 tree.add(Interval.of(Double.NEGATIVE_INFINITY, 2, TEST_PRECISION));
957 tree.add(Interval.of(4, Double.POSITIVE_INFINITY, TEST_PRECISION));
958
959 tree.complement();
960
961
962 Assert.assertEquals(2, tree.getSize(), TEST_EPS);
963 }
964
965 @Test
966 public void testGetCentroid_infinite() {
967
968 final RegionBSPTree1D full = RegionBSPTree1D.full();
969
970 final RegionBSPTree1D posHalfSpace = RegionBSPTree1D.empty();
971 posHalfSpace.getRoot().cut(OrientedPoints.createNegativeFacing(-2.0, TEST_PRECISION));
972
973 final RegionBSPTree1D negHalfSpace = RegionBSPTree1D.empty();
974 negHalfSpace.getRoot().cut(OrientedPoints.createPositiveFacing(3.0, TEST_PRECISION));
975
976
977 Assert.assertNull(full.getCentroid());
978 Assert.assertNull(posHalfSpace.getCentroid());
979 Assert.assertNull(negHalfSpace.getCentroid());
980 }
981
982 @Test
983 public void testGetCentroid_empty() {
984
985 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
986
987
988 Assert.assertNull(tree.getCentroid());
989 }
990
991 @Test
992 public void testGetCentroid_exactPoints() {
993
994 final RegionBSPTree1D singlePoint = RegionBSPTree1D.empty();
995 singlePoint.add(Interval.of(1, 1, TEST_PRECISION));
996
997 final RegionBSPTree1D multiplePoints = RegionBSPTree1D.empty();
998 multiplePoints.add(Interval.of(1, 1, TEST_PRECISION));
999 multiplePoints.add(Interval.of(-1, -1, TEST_PRECISION));
1000 multiplePoints.add(Interval.of(6, 6, TEST_PRECISION));
1001
1002
1003 EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(1), singlePoint.getCentroid(), TEST_EPS);
1004 EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(2), multiplePoints.getCentroid(), TEST_EPS);
1005 }
1006
1007 @Test
1008 public void testGetCentroid_pointsWithinPrecision() {
1009
1010 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-1);
1011
1012 final RegionBSPTree1D singlePoint = RegionBSPTree1D.empty();
1013 singlePoint.add(Interval.of(1, 1.02, precision));
1014
1015 final RegionBSPTree1D multiplePoints = RegionBSPTree1D.empty();
1016 multiplePoints.add(Interval.of(1, 1.02, precision));
1017 multiplePoints.add(Interval.of(-1.02, -1, precision));
1018 multiplePoints.add(Interval.of(6, 6.02, precision));
1019
1020
1021 EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(1.01), singlePoint.getCentroid(), TEST_EPS);
1022 EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(6.01 / 3), multiplePoints.getCentroid(), TEST_EPS);
1023 }
1024
1025 @Test
1026 public void testGetCentroid_nonEmptyIntervals() {
1027
1028 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
1029 tree.add(Interval.of(1, 2, TEST_PRECISION));
1030 tree.add(Interval.of(3, 5, TEST_PRECISION));
1031
1032
1033 EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(9.5 / 3), tree.getCentroid(), TEST_EPS);
1034 }
1035
1036 @Test
1037 public void testGetCentroid_complementedRegion() {
1038
1039 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
1040 tree.add(Interval.of(Double.NEGATIVE_INFINITY, 2, TEST_PRECISION));
1041 tree.add(Interval.of(4, Double.POSITIVE_INFINITY, TEST_PRECISION));
1042
1043 tree.complement();
1044
1045
1046 EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(3), tree.getCentroid(), TEST_EPS);
1047 }
1048
1049 @Test
1050 public void testGetCentroid_intervalWithPoints() {
1051
1052 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
1053 tree.add(Interval.of(1, 2, TEST_PRECISION));
1054 tree.add(Interval.of(3, 3, TEST_PRECISION));
1055 tree.add(Interval.of(5, 5, TEST_PRECISION));
1056
1057
1058 EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(1.5), tree.getCentroid(), TEST_EPS);
1059 }
1060
1061 @Test
1062 public void testGetMinMax_full() {
1063
1064 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
1065
1066
1067 GeometryTestUtils.assertPositiveInfinity(tree.getMin());
1068 GeometryTestUtils.assertNegativeInfinity(tree.getMax());
1069 }
1070
1071 @Test
1072 public void testGetMinMax_empty() {
1073
1074 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
1075
1076
1077 GeometryTestUtils.assertPositiveInfinity(tree.getMin());
1078 GeometryTestUtils.assertNegativeInfinity(tree.getMax());
1079 }
1080
1081 @Test
1082 public void testGetMinMax_halfSpaces() {
1083
1084 final RegionBSPTree1D posHalfSpace = RegionBSPTree1D.empty();
1085 posHalfSpace.getRoot().cut(OrientedPoints.createNegativeFacing(-2.0, TEST_PRECISION));
1086
1087 final RegionBSPTree1D negHalfSpace = RegionBSPTree1D.empty();
1088 negHalfSpace.getRoot().cut(OrientedPoints.createPositiveFacing(3.0, TEST_PRECISION));
1089
1090
1091 Assert.assertEquals(-2, posHalfSpace.getMin(), TEST_EPS);
1092 GeometryTestUtils.assertPositiveInfinity(posHalfSpace.getMax());
1093
1094 GeometryTestUtils.assertNegativeInfinity(negHalfSpace.getMin());
1095 Assert.assertEquals(3, negHalfSpace.getMax(), TEST_EPS);
1096 }
1097
1098 @Test
1099 public void testGetMinMax_multipleIntervals() {
1100
1101 final RegionBSPTree1D tree = RegionBSPTree1D.from(Arrays.asList(
1102 Interval.of(3, 5, TEST_PRECISION),
1103 Interval.of(-4, -2, TEST_PRECISION),
1104 Interval.of(0, 0, TEST_PRECISION)
1105 ));
1106
1107
1108 Assert.assertEquals(-4, tree.getMin(), TEST_EPS);
1109 Assert.assertEquals(5, tree.getMax(), TEST_EPS);
1110 }
1111
1112 @Test
1113 public void testGetMinMax_pointsAtMinAndMax() {
1114
1115 final RegionBSPTree1D tree = RegionBSPTree1D.from(Arrays.asList(
1116 Interval.of(5, 5, TEST_PRECISION),
1117 Interval.of(-4, -4, TEST_PRECISION),
1118 Interval.of(0, 0, TEST_PRECISION)
1119 ));
1120
1121
1122 Assert.assertEquals(-4, tree.getMin(), TEST_EPS);
1123 Assert.assertEquals(5, tree.getMax(), TEST_EPS);
1124 }
1125
1126 @Test
1127 public void testFull_factoryMethod() {
1128
1129 final RegionBSPTree1D tree = RegionBSPTree1D.full();
1130
1131
1132 Assert.assertTrue(tree.isFull());
1133 Assert.assertFalse(tree.isEmpty());
1134 Assert.assertNotSame(tree, RegionBSPTree1D.full());
1135 }
1136
1137 @Test
1138 public void testEmpty_factoryMethod() {
1139
1140 final RegionBSPTree1D tree = RegionBSPTree1D.empty();
1141
1142
1143 Assert.assertFalse(tree.isFull());
1144 Assert.assertTrue(tree.isEmpty());
1145 Assert.assertNotSame(tree, RegionBSPTree1D.full());
1146 }
1147
1148 @Test
1149 public void testFromIntervals_iterable() {
1150
1151 final RegionBSPTree1D tree = RegionBSPTree1D.from(Arrays.asList(
1152 Interval.of(1, 2, TEST_PRECISION),
1153 Interval.of(3, 4, TEST_PRECISION)
1154 ));
1155
1156
1157 Assert.assertFalse(tree.isFull());
1158 Assert.assertFalse(tree.isEmpty());
1159
1160 checkClassify(tree, RegionLocation.INSIDE, 1.5, 3.5);
1161 checkClassify(tree, RegionLocation.BOUNDARY, 1, 2, 3, 4);
1162 checkClassify(tree, RegionLocation.OUTSIDE, 0, 2.5, 5);
1163
1164 Assert.assertEquals(2, tree.toIntervals().size());
1165 }
1166
1167 @Test
1168 public void testFromIntervals_iterable_noItervals() {
1169
1170 final RegionBSPTree1D tree = RegionBSPTree1D.from(Collections.emptyList());
1171
1172
1173 Assert.assertFalse(tree.isFull());
1174 Assert.assertTrue(tree.isEmpty());
1175
1176 Assert.assertEquals(0, tree.toIntervals().size());
1177 }
1178
1179 @Test
1180 public void testFromIntervals_varargs() {
1181
1182 final RegionBSPTree1D tree = RegionBSPTree1D.from(
1183 Interval.of(1, 2, TEST_PRECISION),
1184 Interval.of(3, 4, TEST_PRECISION)
1185 );
1186
1187
1188 Assert.assertFalse(tree.isFull());
1189 Assert.assertFalse(tree.isEmpty());
1190
1191 checkClassify(tree, RegionLocation.INSIDE, 1.5, 3.5);
1192 checkClassify(tree, RegionLocation.BOUNDARY, 1, 2, 3, 4);
1193 checkClassify(tree, RegionLocation.OUTSIDE, 0, 2.5, 5);
1194
1195 Assert.assertEquals(2, tree.toIntervals().size());
1196 }
1197
1198 private static void checkClassify(final RegionBSPTree1D tree, final RegionLocation loc, final double... points) {
1199 for (final double x : points) {
1200 final String msg = "Unexpected location for point " + x;
1201
1202 Assert.assertEquals(msg, loc, tree.classify(x));
1203 Assert.assertEquals(msg, loc, tree.classify(Vector1D.of(x)));
1204 }
1205 }
1206
1207 private static void checkContains(final RegionBSPTree1D tree, final boolean contains, final double... points) {
1208 for (final double x : points) {
1209 final String msg = "Unexpected contains status for point " + x;
1210
1211 Assert.assertEquals(msg, contains, tree.contains(x));
1212 Assert.assertEquals(msg, contains, tree.contains(Vector1D.of(x)));
1213 }
1214 }
1215
1216 private static void checkBoundaryProjection(final RegionBSPTree1D tree, final double location, final double projectedLocation) {
1217 final Vector1D pt = Vector1D.of(location);
1218
1219 final Vector1D proj = tree.project(pt);
1220
1221 Assert.assertEquals(projectedLocation, proj.getX(), TEST_EPS);
1222 }
1223
1224 private static void checkInterval(final Interval interval, final double min, final double max) {
1225 checkInterval(interval, min, max, TEST_PRECISION);
1226 }
1227
1228 private static void checkInterval(final Interval interval, final double min, final double max, final DoublePrecisionContext precision) {
1229 Assert.assertEquals(min, interval.getMin(), TEST_EPS);
1230 Assert.assertEquals(max, interval.getMax(), TEST_EPS);
1231 }
1232 }