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.HyperplaneLocation;
23 import org.apache.commons.geometry.core.partitioning.Split;
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.numbers.angle.PlaneAngleRadians;
29 import org.junit.Assert;
30 import org.junit.Test;
31
32 public class LineConvexSubsetTest {
33
34 private static final double TEST_EPS = 1e-10;
35
36 private static final DoublePrecisionContext TEST_PRECISION =
37 new EpsilonDoublePrecisionContext(TEST_EPS);
38
39 @Test
40 public void testFromInterval_intervalArg_finite() {
41
42 final DoublePrecisionContext intervalPrecision = new EpsilonDoublePrecisionContext(1e-2);
43 final Interval interval = Interval.of(-1, 2, intervalPrecision);
44
45 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
46
47
48 final Segment segment = (Segment) Lines.subsetFromInterval(line, interval);
49
50
51 final double side = 1.0 / Math.sqrt(2);
52 checkFinite(segment, Vector2D.of(-side, -side), Vector2D.of(2 * side, 2 * side));
53
54 Assert.assertSame(TEST_PRECISION, segment.getPrecision());
55 }
56
57 @Test
58 public void testFromInterval_intervalArg_full() {
59
60 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
61
62
63 final LineConvexSubset span = Lines.subsetFromInterval(line, Interval.full());
64
65
66 GeometryTestUtils.assertNegativeInfinity(span.getSubspaceStart());
67 GeometryTestUtils.assertPositiveInfinity(span.getSubspaceEnd());
68
69 Assert.assertNull(span.getStartPoint());
70 Assert.assertNull(span.getEndPoint());
71
72 Assert.assertSame(Interval.full(), span.getInterval());
73 Assert.assertSame(TEST_PRECISION, span.getPrecision());
74 }
75
76 @Test
77 public void testFromInterval_intervalArg_positiveHalfSpace() {
78
79 final DoublePrecisionContext intervalPrecision = new EpsilonDoublePrecisionContext(1e-2);
80 final Interval interval = Interval.min(-1, intervalPrecision);
81
82 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
83
84
85 final Ray ray = (Ray) Lines.subsetFromInterval(line, interval);
86
87
88 Assert.assertEquals(-1.0, ray.getSubspaceStart(), TEST_EPS);
89 GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
90
91 final double side = 1.0 / Math.sqrt(2);
92
93 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-side, -side), ray.getStartPoint(), TEST_EPS);
94 Assert.assertNull(ray.getEndPoint());
95
96 checkInterval(interval, ray.getInterval());
97 Assert.assertSame(TEST_PRECISION, ray.getPrecision());
98 }
99
100 @Test
101 public void testFromInterval_intervalArg_negativeHalfSpace() {
102
103 final DoublePrecisionContext intervalPrecision = new EpsilonDoublePrecisionContext(1e-2);
104 final Interval interval = Interval.max(2, intervalPrecision);
105
106 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
107
108
109 final ReverseRay halfLine = (ReverseRay) Lines.subsetFromInterval(line, interval);
110
111
112 GeometryTestUtils.assertNegativeInfinity(halfLine.getSubspaceStart());
113 Assert.assertEquals(2, halfLine.getSubspaceEnd(), TEST_EPS);
114
115 final double side = 1.0 / Math.sqrt(2);
116
117 Assert.assertNull(halfLine.getStartPoint());
118 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2 * side, 2 * side), halfLine.getEndPoint(), TEST_EPS);
119
120 checkInterval(interval, halfLine.getInterval());
121 Assert.assertSame(TEST_PRECISION, halfLine.getPrecision());
122 }
123
124 @Test
125 public void testFromInterval_doubleArgs_finite() {
126
127 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
128
129
130 final Segment segment = (Segment) Lines.subsetFromInterval(line, -1, 2);
131
132
133 final double side = 1.0 / Math.sqrt(2);
134 checkFinite(segment, Vector2D.of(-side, -side), Vector2D.of(2 * side, 2 * side));
135
136 Assert.assertSame(TEST_PRECISION, segment.getPrecision());
137 }
138
139 @Test
140 public void testFromInterval_doubleArgs_full() {
141
142 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
143
144
145 final LineConvexSubset span = Lines.subsetFromInterval(line, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
146
147
148 GeometryTestUtils.assertNegativeInfinity(span.getSubspaceStart());
149 GeometryTestUtils.assertPositiveInfinity(span.getSubspaceEnd());
150
151 Assert.assertNull(span.getStartPoint());
152 Assert.assertNull(span.getEndPoint());
153
154 Assert.assertSame(TEST_PRECISION, span.getPrecision());
155 }
156
157 @Test
158 public void testFromInterval_doubleArgs_positiveHalfSpace() {
159
160 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
161
162
163 final Ray ray = (Ray) Lines.subsetFromInterval(line, -1, Double.POSITIVE_INFINITY);
164
165
166 Assert.assertEquals(-1.0, ray.getSubspaceStart(), TEST_EPS);
167 GeometryTestUtils.assertPositiveInfinity(ray.getSubspaceEnd());
168
169 final double side = 1.0 / Math.sqrt(2);
170
171 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-side, -side), ray.getStartPoint(), TEST_EPS);
172 Assert.assertNull(ray.getEndPoint());
173
174 Assert.assertSame(TEST_PRECISION, ray.getPrecision());
175 }
176
177 @Test
178 public void testFromInterval_doubleArgs_negativeHalfSpace() {
179
180 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
181
182
183 final ReverseRay halfLine = (ReverseRay) Lines.subsetFromInterval(line, 2, Double.NEGATIVE_INFINITY);
184
185
186 GeometryTestUtils.assertNegativeInfinity(halfLine.getSubspaceStart());
187 Assert.assertEquals(2, halfLine.getSubspaceEnd(), TEST_EPS);
188
189 final double side = 1.0 / Math.sqrt(2);
190
191 Assert.assertNull(halfLine.getStartPoint());
192 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2 * side, 2 * side), halfLine.getEndPoint(), TEST_EPS);
193
194 Assert.assertSame(TEST_PRECISION, halfLine.getPrecision());
195 }
196
197 @Test
198 public void testFromInterval_doubleArgs_invalid() {
199
200 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION);
201
202
203 GeometryTestUtils.assertThrows(() -> {
204 Lines.subsetFromInterval(line, 0, Double.NaN);
205 }, IllegalArgumentException.class, "Invalid line subset interval: 0.0, NaN");
206
207 GeometryTestUtils.assertThrows(() -> {
208 Lines.subsetFromInterval(line, Double.NaN, 0.0);
209 }, IllegalArgumentException.class, "Invalid line subset interval: NaN, 0.0");
210
211 GeometryTestUtils.assertThrows(() -> {
212 Lines.subsetFromInterval(line, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
213 }, IllegalArgumentException.class, "Invalid line subset interval: Infinity, Infinity");
214
215 GeometryTestUtils.assertThrows(() -> {
216 Lines.subsetFromInterval(line, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
217 }, IllegalArgumentException.class, "Invalid line subset interval: -Infinity, -Infinity");
218
219 GeometryTestUtils.assertThrows(() -> {
220 Lines.subsetFromInterval(line, Double.POSITIVE_INFINITY, Double.NaN);
221 }, IllegalArgumentException.class, "Invalid line subset interval: Infinity, NaN");
222
223 GeometryTestUtils.assertThrows(() -> {
224 Lines.subsetFromInterval(line, Double.NaN, Double.NEGATIVE_INFINITY);
225 }, IllegalArgumentException.class, "Invalid line subset interval: NaN, -Infinity");
226 }
227
228 @Test
229 public void testToConvex() {
230
231 final Line line = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(4, 5), TEST_PRECISION);
232 final LineConvexSubset sub = Lines.subsetFromInterval(line, 1, 2);
233
234
235 final List<LineConvexSubset> segments = sub.toConvex();
236
237
238 Assert.assertEquals(1, segments.size());
239 Assert.assertSame(sub, segments.get(0));
240 }
241
242 @Test
243 public void testIntersection_line() {
244
245 final Segment aSeg = Lines.segmentFromPoints(Vector2D.of(1, 0), Vector2D.of(2, 0), TEST_PRECISION);
246 final Segment bSeg = Lines.segmentFromPoints(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION);
247
248 final Line xAxis = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
249 final Line yAxis = Lines.fromPointAndAngle(Vector2D.ZERO, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION);
250 final Line angledLine = Lines.fromPoints(Vector2D.of(1, 1), Vector2D.of(2, 0), TEST_PRECISION);
251
252
253 Assert.assertNull(aSeg.intersection(xAxis));
254 Assert.assertNull(aSeg.intersection(yAxis));
255
256 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, bSeg.intersection(xAxis), TEST_EPS);
257 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, bSeg.intersection(yAxis), TEST_EPS);
258
259 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), bSeg.intersection(angledLine), TEST_EPS);
260 }
261
262 @Test
263 public void testIntersection_lineSegment() {
264
265 final Segment a = Lines.segmentFromPoints(Vector2D.of(1, 0), Vector2D.of(2, 0), TEST_PRECISION);
266 final Segment b = Lines.segmentFromPoints(Vector2D.of(-1, -1), Vector2D.of(1, 1), TEST_PRECISION);
267 final Segment c = Lines.segmentFromPoints(Vector2D.of(-1, 0), Vector2D.ZERO, TEST_PRECISION);
268 final Segment d = Lines.segmentFromPoints(Vector2D.of(0, 3), Vector2D.of(3, 0), TEST_PRECISION);
269
270
271 Assert.assertNull(a.intersection(a));
272 Assert.assertNull(a.intersection(c));
273 Assert.assertNull(a.intersection(b));
274
275 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, b.intersection(c), TEST_EPS);
276
277 Assert.assertNull(b.intersection(d));
278 Assert.assertNull(d.intersection(b));
279 }
280
281 @Test
282 public void testSplit_finite() {
283
284 final Vector2D start = Vector2D.of(1, 1);
285 final Vector2D end = Vector2D.of(3, 2);
286 final Vector2D middle = start.lerp(end, 0.5);
287
288 final Segment sub = Lines.segmentFromPoints(start, end, TEST_PRECISION);
289
290
291 final Split<LineConvexSubset> both = sub.split(Lines.fromPointAndDirection(middle, Vector2D.of(1, -2), TEST_PRECISION));
292 checkFinite(both.getMinus(), middle, end);
293 checkFinite(both.getPlus(), start, middle);
294
295 final Split<LineConvexSubset> bothReversed = sub.split(Lines.fromPointAndDirection(middle, Vector2D.of(-1, 2), TEST_PRECISION));
296 checkFinite(bothReversed.getMinus(), start, middle);
297 checkFinite(bothReversed.getPlus(), middle, end);
298
299 final Split<LineConvexSubset> minusOnlyOrthogonal = sub.split(Lines.fromPointAndDirection(start, Vector2D.of(1, -2), TEST_PRECISION));
300 Assert.assertSame(sub, minusOnlyOrthogonal.getMinus());
301 Assert.assertNull(minusOnlyOrthogonal.getPlus());
302
303 final Split<LineConvexSubset> minusOnlyParallel = sub.split(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION));
304 Assert.assertSame(sub, minusOnlyParallel.getMinus());
305 Assert.assertNull(minusOnlyParallel.getPlus());
306
307 final Split<LineConvexSubset> plusOnlyOrthogonal = sub.split(Lines.fromPointAndDirection(end, Vector2D.of(1, -2), TEST_PRECISION));
308 Assert.assertNull(plusOnlyOrthogonal.getMinus());
309 Assert.assertSame(sub, plusOnlyOrthogonal.getPlus());
310
311 final Split<LineConvexSubset> plusOnlyParallel = sub.split(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(-2, -1), TEST_PRECISION));
312 Assert.assertNull(plusOnlyParallel.getMinus());
313 Assert.assertSame(sub, plusOnlyParallel.getPlus());
314
315 final Split<LineConvexSubset> hyper = sub.split(Lines.fromPointAndDirection(start, Vector2D.of(2, 1), TEST_PRECISION));
316 Assert.assertNull(hyper.getMinus());
317 Assert.assertNull(hyper.getPlus());
318 }
319
320 @Test
321 public void testSplit_full() {
322
323 final Vector2D p1 = Vector2D.of(1, 1);
324 final Vector2D p2 = Vector2D.of(3, 2);
325 final Vector2D middle = p1.lerp(p2, 0.5);
326
327 final Line line = Lines.fromPoints(p1, p2, TEST_PRECISION);
328
329 final LineConvexSubset seg = Lines.subsetFromInterval(line, Interval.full());
330
331
332 final Split<LineConvexSubset> both = seg.split(Lines.fromPointAndDirection(middle, Vector2D.of(1, -2), TEST_PRECISION));
333 checkInfinite(both.getMinus(), line, middle, null);
334 checkInfinite(both.getPlus(), line, null, middle);
335
336 final Split<LineConvexSubset> bothReversed = seg.split(Lines.fromPointAndDirection(middle, Vector2D.of(-1, 2), TEST_PRECISION));
337 checkInfinite(bothReversed.getMinus(), line, null, middle);
338 checkInfinite(bothReversed.getPlus(), line, middle, null);
339
340 final Split<LineConvexSubset> minusOnlyParallel = seg.split(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION));
341 Assert.assertSame(seg, minusOnlyParallel.getMinus());
342 Assert.assertNull(minusOnlyParallel.getPlus());
343
344 final Split<LineConvexSubset> plusOnlyParallel = seg.split(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(-2, -1), TEST_PRECISION));
345 Assert.assertNull(plusOnlyParallel.getMinus());
346 Assert.assertSame(seg, plusOnlyParallel.getPlus());
347
348 final Split<LineConvexSubset> hyper = seg.split(Lines.fromPointAndDirection(p1, Vector2D.of(2, 1), TEST_PRECISION));
349 Assert.assertNull(hyper.getMinus());
350 Assert.assertNull(hyper.getPlus());
351 }
352
353 @Test
354 public void testSplit_positiveHalfSpace() {
355
356 final Vector2D p1 = Vector2D.of(1, 1);
357 final Vector2D p2 = Vector2D.of(3, 2);
358 final Vector2D middle = p1.lerp(p2, 0.5);
359
360 final Line line = Lines.fromPoints(p1, p2, TEST_PRECISION);
361
362 final LineConvexSubset sub = Lines.subsetFromInterval(line, Interval.min(line.toSubspace(p1).getX(), TEST_PRECISION));
363
364
365 final Split<LineConvexSubset> both = sub.split(Lines.fromPointAndDirection(middle, Vector2D.of(1, -2), TEST_PRECISION));
366 checkInfinite(both.getMinus(), line, middle, null);
367 checkFinite(both.getPlus(), p1, middle);
368
369 final Split<LineConvexSubset> bothReversed = sub.split(Lines.fromPointAndDirection(middle, Vector2D.of(-1, 2), TEST_PRECISION));
370 checkFinite(bothReversed.getMinus(), p1, middle);
371 checkInfinite(bothReversed.getPlus(), line, middle, null);
372
373 final Split<LineConvexSubset> minusOnlyParallel = sub.split(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION));
374 Assert.assertSame(sub, minusOnlyParallel.getMinus());
375 Assert.assertNull(minusOnlyParallel.getPlus());
376
377 final Split<LineConvexSubset> minusOnlyOrthogonal = sub.split(Lines.fromPointAndDirection(p1, Vector2D.of(1, -2), TEST_PRECISION));
378 Assert.assertSame(sub, minusOnlyOrthogonal.getMinus());
379 Assert.assertNull(minusOnlyOrthogonal.getPlus());
380
381 final Split<LineConvexSubset> plusOnlyParallel = sub.split(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(-2, -1), TEST_PRECISION));
382 Assert.assertNull(plusOnlyParallel.getMinus());
383 Assert.assertSame(sub, plusOnlyParallel.getPlus());
384
385 final Split<LineConvexSubset> hyper = sub.split(Lines.fromPointAndDirection(p1, Vector2D.of(2, 1), TEST_PRECISION));
386 Assert.assertNull(hyper.getMinus());
387 Assert.assertNull(hyper.getPlus());
388 }
389
390 @Test
391 public void testSplit_negativeHalfSpace() {
392
393 final Vector2D p1 = Vector2D.of(1, 1);
394 final Vector2D p2 = Vector2D.of(3, 2);
395 final Vector2D middle = p1.lerp(p2, 0.5);
396
397 final Line line = Lines.fromPoints(p1, p2, TEST_PRECISION);
398
399 final LineConvexSubset seg = Lines.subsetFromInterval(line, Interval.max(line.toSubspace(p2).getX(), TEST_PRECISION));
400
401
402 final Split<LineConvexSubset> both = seg.split(Lines.fromPointAndDirection(middle, Vector2D.of(1, -2), TEST_PRECISION));
403 checkFinite(both.getMinus(), middle, p2);
404 checkInfinite(both.getPlus(), line, null, middle);
405
406 final Split<LineConvexSubset> bothReversed = seg.split(Lines.fromPointAndDirection(middle, Vector2D.of(-1, 2), TEST_PRECISION));
407 checkInfinite(bothReversed.getMinus(), line, null, middle);
408 checkFinite(bothReversed.getPlus(), middle, p2);
409
410 final Split<LineConvexSubset> minusOnlyParallel = seg.split(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(2, 1), TEST_PRECISION));
411 Assert.assertSame(seg, minusOnlyParallel.getMinus());
412 Assert.assertNull(minusOnlyParallel.getPlus());
413
414 final Split<LineConvexSubset> plusOnlyParallel = seg.split(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(-2, -1), TEST_PRECISION));
415 Assert.assertNull(plusOnlyParallel.getMinus());
416 Assert.assertSame(seg, plusOnlyParallel.getPlus());
417
418 final Split<LineConvexSubset> plusOnlyOrthogonal = seg.split(Lines.fromPointAndDirection(p2, Vector2D.of(1, -2), TEST_PRECISION));
419 Assert.assertNull(plusOnlyOrthogonal.getMinus());
420 Assert.assertSame(seg, plusOnlyOrthogonal.getPlus());
421
422 final Split<LineConvexSubset> hyper = seg.split(Lines.fromPointAndDirection(p1, Vector2D.of(2, 1), TEST_PRECISION));
423 Assert.assertNull(hyper.getMinus());
424 Assert.assertNull(hyper.getPlus());
425 }
426
427 private static void checkInterval(final Interval expected, final Interval actual) {
428 Assert.assertEquals(expected.getMin(), actual.getMin(), TEST_EPS);
429 Assert.assertEquals(expected.getMax(), actual.getMax(), TEST_EPS);
430 }
431
432 private static void checkFinite(final LineConvexSubset segment, final Vector2D start, final Vector2D end) {
433 checkFinite(segment, start, end, TEST_PRECISION);
434 }
435
436 private static void checkFinite(final LineConvexSubset segment, final Vector2D start, final Vector2D end, final DoublePrecisionContext precision) {
437 Assert.assertFalse(segment.isInfinite());
438
439 EuclideanTestUtils.assertCoordinatesEqual(start, segment.getStartPoint(), TEST_EPS);
440 EuclideanTestUtils.assertCoordinatesEqual(end, segment.getEndPoint(), TEST_EPS);
441
442 final Line line = segment.getLine();
443 Assert.assertEquals(HyperplaneLocation.ON, line.classify(segment.getStartPoint()));
444 Assert.assertEquals(HyperplaneLocation.ON, line.classify(segment.getEndPoint()));
445
446 Assert.assertEquals(line.toSubspace(segment.getStartPoint()).getX(), segment.getSubspaceStart(), TEST_EPS);
447 Assert.assertEquals(line.toSubspace(segment.getEndPoint()).getX(), segment.getSubspaceEnd(), TEST_EPS);
448
449 Assert.assertSame(precision, segment.getPrecision());
450 Assert.assertSame(precision, line.getPrecision());
451 }
452
453 private static void checkInfinite(final LineConvexSubset segment, final Line line, final Vector2D start, final Vector2D end) {
454 checkInfinite(segment, line, start, end, TEST_PRECISION);
455 }
456
457 private static void checkInfinite(final LineConvexSubset segment, final Line line, final Vector2D start, final Vector2D end,
458 final DoublePrecisionContext precision) {
459
460 Assert.assertTrue(segment.isInfinite());
461
462 Assert.assertEquals(line, segment.getLine());
463
464 if (start == null) {
465 Assert.assertNull(segment.getStartPoint());
466 } else {
467 EuclideanTestUtils.assertCoordinatesEqual(start, segment.getStartPoint(), TEST_EPS);
468 Assert.assertEquals(line.toSubspace(segment.getStartPoint()).getX(), segment.getSubspaceStart(), TEST_EPS);
469 }
470
471 if (end == null) {
472 Assert.assertNull(segment.getEndPoint());
473 } else {
474 EuclideanTestUtils.assertCoordinatesEqual(end, segment.getEndPoint(), TEST_EPS);
475 Assert.assertEquals(line.toSubspace(segment.getEndPoint()).getX(), segment.getSubspaceEnd(), TEST_EPS);
476 }
477
478 Assert.assertSame(precision, segment.getPrecision());
479 Assert.assertSame(precision, line.getPrecision());
480 }
481 }