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.List;
20
21 import org.apache.commons.geometry.core.GeometryTestUtils;
22 import org.apache.commons.geometry.core.partitioning.Split;
23 import org.apache.commons.geometry.core.partitioning.SplitLocation;
24 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
25 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
26 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
27 import org.apache.commons.geometry.euclidean.oned.Interval;
28 import org.apache.commons.geometry.euclidean.oned.RegionBSPTree1D;
29 import org.apache.commons.numbers.angle.PlaneAngleRadians;
30 import org.junit.Assert;
31 import org.junit.Test;
32
33 public class EmbeddedTreeLineSubsetTest {
34
35 private static final double TEST_EPS = 1e-10;
36
37 private static final DoublePrecisionContext TEST_PRECISION =
38 new EpsilonDoublePrecisionContext(TEST_EPS);
39
40 private static final Line DEFAULT_TEST_LINE =
41 Lines.fromPointAndDirection(Vector2D.of(0, 1), Vector2D.Unit.PLUS_X, TEST_PRECISION);
42
43 @Test
44 public void testCtor_lineOnly() {
45
46 final EmbeddedTreeLineSubset sub = new EmbeddedTreeLineSubset(DEFAULT_TEST_LINE);
47
48
49 Assert.assertSame(DEFAULT_TEST_LINE, sub.getLine());
50 Assert.assertSame(TEST_PRECISION, sub.getPrecision());
51
52 Assert.assertFalse(sub.isFull());
53 Assert.assertTrue(sub.isEmpty());
54 Assert.assertFalse(sub.isInfinite());
55 Assert.assertTrue(sub.isFinite());
56
57 Assert.assertEquals(0, sub.getSize(), TEST_EPS);
58 Assert.assertNull(sub.getCentroid());
59 }
60
61 @Test
62 public void testCtor_lineAndBoolean() {
63
64 final EmbeddedTreeLineSubset sub = new EmbeddedTreeLineSubset(DEFAULT_TEST_LINE, true);
65
66
67 Assert.assertSame(DEFAULT_TEST_LINE, sub.getLine());
68 Assert.assertSame(TEST_PRECISION, sub.getPrecision());
69
70 Assert.assertTrue(sub.isFull());
71 Assert.assertFalse(sub.isEmpty());
72 Assert.assertTrue(sub.isInfinite());
73 Assert.assertFalse(sub.isFinite());
74
75 GeometryTestUtils.assertPositiveInfinity(sub.getSize());
76 Assert.assertNull(sub.getCentroid());
77 }
78
79 @Test
80 public void testCtor_lineAndRegion() {
81
82 final RegionBSPTree1D tree = RegionBSPTree1D.full();
83
84
85 final EmbeddedTreeLineSubset sub = new EmbeddedTreeLineSubset(DEFAULT_TEST_LINE, tree);
86
87
88 Assert.assertSame(DEFAULT_TEST_LINE, sub.getLine());
89 Assert.assertSame(tree, sub.getSubspaceRegion());
90 Assert.assertSame(TEST_PRECISION, sub.getPrecision());
91
92 Assert.assertTrue(sub.isFull());
93 Assert.assertFalse(sub.isEmpty());
94 Assert.assertTrue(sub.isInfinite());
95 Assert.assertFalse(sub.isFinite());
96
97 GeometryTestUtils.assertPositiveInfinity(sub.getSize());
98 Assert.assertNull(sub.getCentroid());
99 }
100
101 @Test
102 public void testToConvex_full() {
103
104 final EmbeddedTreeLineSubset sub = new EmbeddedTreeLineSubset(DEFAULT_TEST_LINE, true);
105
106
107 final List<LineConvexSubset> segments = sub.toConvex();
108
109
110 Assert.assertEquals(1, segments.size());
111
112 final LineConvexSubset seg = segments.get(0);
113 Assert.assertTrue(seg.isFull());
114 }
115
116 @Test
117 public void testToConvex_empty() {
118
119 final EmbeddedTreeLineSubset sub = new EmbeddedTreeLineSubset(DEFAULT_TEST_LINE, false);
120
121
122 final List<LineConvexSubset> segments = sub.toConvex();
123
124
125 Assert.assertEquals(0, segments.size());
126 }
127
128 @Test
129 public void testToConvex_finiteAndInfiniteSegments() {
130
131 final EmbeddedTreeLineSubset sub = new EmbeddedTreeLineSubset(DEFAULT_TEST_LINE, false);
132 final RegionBSPTree1D tree = sub.getSubspaceRegion();
133 tree.add(Interval.max(-2.0, TEST_PRECISION));
134 tree.add(Interval.of(-1, 2, TEST_PRECISION));
135
136
137 final List<LineConvexSubset> segments = sub.toConvex();
138
139
140 Assert.assertEquals(2, segments.size());
141
142 Assert.assertNull(segments.get(0).getStartPoint());
143 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 1), segments.get(0).getEndPoint(), TEST_EPS);
144
145 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 1), segments.get(1).getStartPoint(), TEST_EPS);
146 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 1), segments.get(1).getEndPoint(), TEST_EPS);
147 }
148
149 @Test
150 public void testAdd_lineSegment() {
151
152 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), 0.0, TEST_PRECISION);
153 final Line otherLine = Lines.fromPointAndAngle(Vector2D.of(0, 1), 1e-11, TEST_PRECISION);
154
155 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line);
156
157
158 subset.add(Lines.subsetFromInterval(line, 2, 4));
159 subset.add(Lines.subsetFromInterval(otherLine, 1, 3));
160 subset.add(Lines.segmentFromPoints(Vector2D.of(-3, 1), Vector2D.of(-1, 1), TEST_PRECISION));
161
162
163 Assert.assertFalse(subset.isFull());
164 Assert.assertFalse(subset.isEmpty());
165
166 final List<LineConvexSubset> segments = subset.toConvex();
167
168 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 1), segments.get(0).getStartPoint(), TEST_EPS);
169 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 1), segments.get(0).getEndPoint(), TEST_EPS);
170
171 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), segments.get(1).getStartPoint(), TEST_EPS);
172 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(4, 1), segments.get(1).getEndPoint(), TEST_EPS);
173
174 Assert.assertEquals(5, subset.getSize(), TEST_EPS);
175 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0.7, 1), subset.getCentroid(), TEST_EPS);
176 }
177
178 @Test
179 public void testAdd_subset() {
180
181 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), 0.0, TEST_PRECISION);
182
183 final EmbeddedTreeLineSubset a = new EmbeddedTreeLineSubset(line);
184 final RegionBSPTree1D aTree = a.getSubspaceRegion();
185 aTree.add(Interval.max(-3, TEST_PRECISION));
186 aTree.add(Interval.of(1, 2, TEST_PRECISION));
187
188 final EmbeddedTreeLineSubset b = new EmbeddedTreeLineSubset(line);
189 final RegionBSPTree1D bTree = b.getSubspaceRegion();
190 bTree.add(Interval.of(2, 4, TEST_PRECISION));
191 bTree.add(Interval.of(-4, -2, TEST_PRECISION));
192
193 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line);
194
195 final int aTreeCount = aTree.count();
196 final int bTreeCount = bTree.count();
197
198
199 subset.add(a);
200 subset.add(b);
201
202
203 Assert.assertFalse(subset.isFull());
204 Assert.assertFalse(subset.isEmpty());
205
206 final List<LineConvexSubset> segments = subset.toConvex();
207
208 Assert.assertEquals(2, segments.size());
209
210 Assert.assertNull(segments.get(0).getStartPoint());
211 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 1), segments.get(0).getEndPoint(), TEST_EPS);
212
213 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), segments.get(1).getStartPoint(), TEST_EPS);
214 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(4, 1), segments.get(1).getEndPoint(), TEST_EPS);
215
216 Assert.assertEquals(aTreeCount, aTree.count());
217 Assert.assertEquals(bTreeCount, bTree.count());
218
219 GeometryTestUtils.assertPositiveInfinity(subset.getSize());
220 Assert.assertNull(subset.getCentroid());
221 }
222
223 @Test
224 public void testAdd_argumentsFromDifferentLine() {
225
226 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), 0.0, TEST_PRECISION);
227 final Line otherLine = Lines.fromPointAndAngle(Vector2D.of(0, 1), 1e-2, TEST_PRECISION);
228
229 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line);
230
231
232 GeometryTestUtils.assertThrows(() -> {
233 subset.add(Lines.subsetFromInterval(otherLine, 0, 1));
234 }, IllegalArgumentException.class);
235
236 GeometryTestUtils.assertThrows(() -> {
237 subset.add(new EmbeddedTreeLineSubset(otherLine));
238 }, IllegalArgumentException.class);
239 }
240
241 @Test
242 public void testGetBounds_noBounds() {
243
244 final Line line = Lines.fromPointAndAngle(Vector2D.of(1, 0), 0.25 * Math.PI, TEST_PRECISION);
245
246 final EmbeddedTreeLineSubset full = new EmbeddedTreeLineSubset(line, RegionBSPTree1D.full());
247 final EmbeddedTreeLineSubset empty = new EmbeddedTreeLineSubset(line, RegionBSPTree1D.empty());
248 final EmbeddedTreeLineSubset halfFull = new EmbeddedTreeLineSubset(line, Interval.min(1.0, TEST_PRECISION).toTree());
249
250
251 Assert.assertNull(full.getBounds());
252 Assert.assertNull(empty.getBounds());
253 Assert.assertNull(halfFull.getBounds());
254 }
255
256 @Test
257 public void testGetBounds_hasBounds() {
258
259 final Line line = Lines.fromPoints(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
260
261 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, false);
262
263 final double sqrt2 = Math.sqrt(2);
264 subset.getSubspaceRegion().add(Interval.of(-2 * sqrt2, -sqrt2, TEST_PRECISION));
265 subset.getSubspaceRegion().add(Interval.of(0, sqrt2, TEST_PRECISION));
266
267
268 final Bounds2D bounds = subset.getBounds();
269
270
271 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, -2), bounds.getMin(), TEST_EPS);
272 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), bounds.getMax(), TEST_EPS);
273 }
274
275 @Test
276 public void testSplit_both_anglePositive() {
277
278 final RegionBSPTree1D subRegion = RegionBSPTree1D.empty();
279 subRegion.add(Interval.of(0, 2, TEST_PRECISION));
280 subRegion.add(Interval.of(3, 4, TEST_PRECISION));
281
282 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
283 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, subRegion);
284
285 final Line splitter = Lines.fromPointAndAngle(Vector2D.of(1, 0), 0.1 * PlaneAngleRadians.PI, TEST_PRECISION);
286
287
288 final Split<EmbeddedTreeLineSubset> split = subset.split(splitter);
289
290
291 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
292
293 final List<LineConvexSubset> minusSegments = split.getMinus().toConvex();
294 Assert.assertEquals(1, minusSegments.size());
295 checkFiniteSegment(minusSegments.get(0), Vector2D.ZERO, Vector2D.of(1, 0));
296
297 final List<LineConvexSubset> plusSegments = split.getPlus().toConvex();
298 Assert.assertEquals(2, plusSegments.size());
299 checkFiniteSegment(plusSegments.get(0), Vector2D.of(1, 0), Vector2D.of(2, 0));
300 checkFiniteSegment(plusSegments.get(1), Vector2D.of(3, 0), Vector2D.of(4, 0));
301 }
302
303 @Test
304 public void testSplit_both_angleNegative() {
305
306 final RegionBSPTree1D subRegion = RegionBSPTree1D.empty();
307 subRegion.add(Interval.of(0, 2, TEST_PRECISION));
308 subRegion.add(Interval.of(3, 4, TEST_PRECISION));
309
310 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
311 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, subRegion);
312
313 final Line splitter = Lines.fromPointAndAngle(Vector2D.of(1, 0), -0.9 * PlaneAngleRadians.PI, TEST_PRECISION);
314
315
316 final Split<EmbeddedTreeLineSubset> split = subset.split(splitter);
317
318
319 Assert.assertEquals(SplitLocation.BOTH, split.getLocation());
320
321 final List<LineConvexSubset> minusSegments = split.getMinus().toConvex();
322 Assert.assertEquals(2, minusSegments.size());
323 checkFiniteSegment(minusSegments.get(0), Vector2D.of(1, 0), Vector2D.of(2, 0));
324 checkFiniteSegment(minusSegments.get(1), Vector2D.of(3, 0), Vector2D.of(4, 0));
325
326 final List<LineConvexSubset> plusSegments = split.getPlus().toConvex();
327 Assert.assertEquals(1, plusSegments.size());
328 checkFiniteSegment(plusSegments.get(0), Vector2D.ZERO, Vector2D.of(1, 0));
329 }
330
331 @Test
332 public void testSplit_intersection_plusOnly() {
333
334 final RegionBSPTree1D subRegion = RegionBSPTree1D.empty();
335 subRegion.add(Interval.of(0, 2, TEST_PRECISION));
336 subRegion.add(Interval.of(3, 4, TEST_PRECISION));
337
338 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
339 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, subRegion);
340
341 final Line splitter = Lines.fromPointAndAngle(Vector2D.of(-1, 0), 0.1 * PlaneAngleRadians.PI, TEST_PRECISION);
342
343
344 final Split<EmbeddedTreeLineSubset> split = subset.split(splitter);
345
346
347 Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
348
349 Assert.assertNull(split.getMinus());
350 Assert.assertSame(subset, split.getPlus());
351 }
352
353 @Test
354 public void testSplit_intersection_minusOnly() {
355
356 final RegionBSPTree1D subRegion = RegionBSPTree1D.empty();
357 subRegion.add(Interval.of(0, 2, TEST_PRECISION));
358 subRegion.add(Interval.of(3, 4, TEST_PRECISION));
359
360 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
361 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, subRegion);
362
363 final Line splitter = Lines.fromPointAndAngle(Vector2D.of(10, 0), 0.1 * PlaneAngleRadians.PI, TEST_PRECISION);
364
365
366 final Split<EmbeddedTreeLineSubset> split = subset.split(splitter);
367
368
369 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
370
371 Assert.assertSame(subset, split.getMinus());
372 Assert.assertNull(split.getPlus());
373 }
374
375 @Test
376 public void testSplit_parallel_plus() {
377
378 final RegionBSPTree1D subRegion = RegionBSPTree1D.empty();
379 subRegion.add(Interval.of(0, 2, TEST_PRECISION));
380 subRegion.add(Interval.of(3, 4, TEST_PRECISION));
381
382 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
383 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, subRegion);
384
385 final Line splitter = Lines.fromPointAndAngle(Vector2D.of(0, 1), 0.0, TEST_PRECISION);
386
387
388 final Split<EmbeddedTreeLineSubset> split = subset.split(splitter);
389
390
391 Assert.assertEquals(SplitLocation.PLUS, split.getLocation());
392
393 Assert.assertNull(split.getMinus());
394 Assert.assertSame(subset, split.getPlus());
395 }
396
397 @Test
398 public void testSplit_parallel_minus() {
399
400 final RegionBSPTree1D subRegion = RegionBSPTree1D.empty();
401 subRegion.add(Interval.of(0, 2, TEST_PRECISION));
402 subRegion.add(Interval.of(3, 4, TEST_PRECISION));
403
404 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
405 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, subRegion);
406
407 final Line splitter = Lines.fromPointAndAngle(Vector2D.of(0, -1), 0.0, TEST_PRECISION);
408
409
410 final Split<EmbeddedTreeLineSubset> split = subset.split(splitter);
411
412
413 Assert.assertEquals(SplitLocation.MINUS, split.getLocation());
414
415 Assert.assertSame(subset, split.getMinus());
416 Assert.assertNull(split.getPlus());
417 }
418
419 @Test
420 public void testSplit_coincident_sameDirection() {
421
422 final RegionBSPTree1D subRegion = RegionBSPTree1D.empty();
423 subRegion.add(Interval.of(0, 2, TEST_PRECISION));
424 subRegion.add(Interval.of(3, 4, TEST_PRECISION));
425
426 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
427 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, subRegion);
428
429 final Line splitter = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
430
431
432 final Split<EmbeddedTreeLineSubset> split = subset.split(splitter);
433
434
435 Assert.assertEquals(SplitLocation.NEITHER, split.getLocation());
436
437 Assert.assertNull(split.getMinus());
438 Assert.assertNull(split.getPlus());
439 }
440
441 @Test
442 public void testSplit_coincident_oppositeDirection() {
443
444 final RegionBSPTree1D subRegion = RegionBSPTree1D.empty();
445 subRegion.add(Interval.of(0, 2, TEST_PRECISION));
446 subRegion.add(Interval.of(3, 4, TEST_PRECISION));
447
448 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
449 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(line, subRegion);
450
451 final Line splitter = Lines.fromPointAndAngle(Vector2D.ZERO, PlaneAngleRadians.PI, TEST_PRECISION);
452
453
454 final Split<EmbeddedTreeLineSubset> split = subset.split(splitter);
455
456
457 Assert.assertEquals(SplitLocation.NEITHER, split.getLocation());
458
459 Assert.assertNull(split.getMinus());
460 Assert.assertNull(split.getPlus());
461 }
462
463 @Test
464 public void testTransform() {
465
466 final AffineTransformMatrix2D mat = AffineTransformMatrix2D
467 .createRotation(Vector2D.of(0, 1), PlaneAngleRadians.PI_OVER_TWO)
468 .scale(Vector2D.of(3, 2));
469
470 final EmbeddedTreeLineSubset subset = new EmbeddedTreeLineSubset(Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION));
471 subset.getSubspaceRegion().add(Interval.of(0, 1, TEST_PRECISION));
472 subset.getSubspaceRegion().add(Interval.min(3, TEST_PRECISION));
473
474
475 final EmbeddedTreeLineSubset transformed = subset.transform(mat);
476
477
478 Assert.assertNotSame(subset, transformed);
479
480 final List<LineConvexSubset> originalSegments = subset.toConvex();
481 Assert.assertEquals(2, originalSegments.size());
482 checkFiniteSegment(originalSegments.get(0), Vector2D.ZERO, Vector2D.Unit.PLUS_X);
483 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 0), originalSegments.get(1).getStartPoint(), TEST_EPS);
484 Assert.assertNull(originalSegments.get(1).getEndPoint());
485
486 final List<LineConvexSubset> transformedSegments = transformed.toConvex();
487 Assert.assertEquals(2, transformedSegments.size());
488 checkFiniteSegment(transformedSegments.get(0), Vector2D.of(3, 2), Vector2D.of(3, 4));
489 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 8), transformedSegments.get(1).getStartPoint(), TEST_EPS);
490 Assert.assertNull(transformedSegments.get(1).getEndPoint());
491 }
492
493 @Test
494 public void testTransform_reflection() {
495
496 final AffineTransformMatrix2D mat = AffineTransformMatrix2D.createScale(Vector2D.of(-1, 2));
497
498 final EmbeddedTreeLineSubset subset =
499 new EmbeddedTreeLineSubset(Lines.fromPointAndAngle(Vector2D.of(0, 1), 0.0, TEST_PRECISION));
500 subset.getSubspaceRegion().add(Interval.of(0, 1, TEST_PRECISION));
501
502
503 final EmbeddedTreeLineSubset transformed = subset.transform(mat);
504
505
506 Assert.assertNotSame(subset, transformed);
507
508 final List<LineConvexSubset> originalSegments = subset.toConvex();
509 Assert.assertEquals(1, originalSegments.size());
510 checkFiniteSegment(originalSegments.get(0), Vector2D.of(0, 1), Vector2D.of(1, 1));
511
512 final List<LineConvexSubset> transformedSegments = transformed.toConvex();
513 Assert.assertEquals(1, transformedSegments.size());
514 checkFiniteSegment(transformedSegments.get(0), Vector2D.of(0, 2), Vector2D.of(-1, 2));
515 }
516
517 @Test
518 public void testToString() {
519
520 final EmbeddedTreeLineSubset sub = new EmbeddedTreeLineSubset(DEFAULT_TEST_LINE);
521
522
523 final String str = sub.toString();
524
525
526 Assert.assertTrue(str.contains("EmbeddedTreeLineSubset[lineOrigin= "));
527 Assert.assertTrue(str.contains(", lineDirection= "));
528 Assert.assertTrue(str.contains(", region= "));
529 }
530
531 private static void checkFiniteSegment(final LineConvexSubset segment, final Vector2D start, final Vector2D end) {
532 Assert.assertFalse(segment.isInfinite());
533
534 EuclideanTestUtils.assertCoordinatesEqual(start, segment.getStartPoint(), TEST_EPS);
535 EuclideanTestUtils.assertCoordinatesEqual(end, segment.getEndPoint(), TEST_EPS);
536 }
537 }