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 org.apache.commons.geometry.core.GeometryTestUtils;
20 import org.apache.commons.geometry.core.RegionLocation;
21 import org.apache.commons.geometry.core.partitioning.Split;
22 import org.apache.commons.geometry.core.partitioning.SplitLocation;
23 import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
24 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
25 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
26 import org.apache.commons.geometry.euclidean.oned.Interval;
27 import org.junit.Assert;
28 import org.junit.Test;
29
30 public class SegmentTest {
31
32 private static final double TEST_EPS = 1e-10;
33
34 private static final DoublePrecisionContext TEST_PRECISION =
35 new EpsilonDoublePrecisionContext(TEST_EPS);
36
37 @Test
38 public void testFromPoints() {
39
40 final Vector2D p1 = Vector2D.of(1, 2);
41 final Vector2D p2 = Vector2D.of(3, 2);
42
43
44 final Segment seg = Lines.segmentFromPoints(p1, p2, TEST_PRECISION);
45
46
47 Assert.assertFalse(seg.isFull());
48 Assert.assertFalse(seg.isEmpty());
49 Assert.assertFalse(seg.isInfinite());
50 Assert.assertTrue(seg.isFinite());
51
52 EuclideanTestUtils.assertCoordinatesEqual(p1, seg.getStartPoint(), TEST_EPS);
53 EuclideanTestUtils.assertCoordinatesEqual(p2, seg.getEndPoint(), TEST_EPS);
54 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 2), seg.getCentroid(), TEST_EPS);
55
56 Assert.assertEquals(1, seg.getSubspaceStart(), TEST_EPS);
57 Assert.assertEquals(3, seg.getSubspaceEnd(), TEST_EPS);
58
59 Assert.assertEquals(2, seg.getSize(), TEST_EPS);
60 }
61
62 @Test
63 public void testFromPoints_invalidArgs() {
64
65 final Vector2D p1 = Vector2D.of(0, 2);
66 final Vector2D p2 = Vector2D.of(1e-17, 2);
67
68
69 GeometryTestUtils.assertThrows(() -> {
70 Lines.segmentFromPoints(p1, p1, TEST_PRECISION);
71 }, IllegalArgumentException.class, "Line direction cannot be zero");
72
73 GeometryTestUtils.assertThrows(() -> {
74 Lines.segmentFromPoints(p1, p2, TEST_PRECISION);
75 }, IllegalArgumentException.class, "Line direction cannot be zero");
76 }
77
78 @Test
79 public void testFromPoints_givenLine() {
80
81 final Vector2D p1 = Vector2D.of(-1, 2);
82 final Vector2D p2 = Vector2D.of(3, 3);
83
84 final Line line = Lines.fromPointAndDirection(Vector2D.of(1, 0), Vector2D.Unit.PLUS_Y, TEST_PRECISION);
85
86
87 final Segment seg = Lines.segmentFromPoints(line, p2, p1);
88
89
90 Assert.assertFalse(seg.isFull());
91 Assert.assertFalse(seg.isEmpty());
92 Assert.assertFalse(seg.isInfinite());
93 Assert.assertTrue(seg.isFinite());
94
95 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2), seg.getStartPoint(), TEST_EPS);
96 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 3), seg.getEndPoint(), TEST_EPS);
97 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2.5), seg.getCentroid(), TEST_EPS);
98
99 Assert.assertEquals(2, seg.getSubspaceStart(), TEST_EPS);
100 Assert.assertEquals(3, seg.getSubspaceEnd(), TEST_EPS);
101
102 Assert.assertEquals(1, seg.getSize(), TEST_EPS);
103 }
104
105 @Test
106 public void testFromPoints_givenLine_singlePoint() {
107
108 final Vector2D p1 = Vector2D.of(-1, 2);
109
110 final Line line = Lines.fromPointAndDirection(Vector2D.of(1, 0), Vector2D.Unit.PLUS_Y, TEST_PRECISION);
111
112
113 final Segment seg = Lines.segmentFromPoints(line, p1, p1);
114
115
116 Assert.assertFalse(seg.isFull());
117 Assert.assertFalse(seg.isEmpty());
118 Assert.assertFalse(seg.isInfinite());
119 Assert.assertTrue(seg.isFinite());
120
121 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2), seg.getStartPoint(), TEST_EPS);
122 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2), seg.getEndPoint(), TEST_EPS);
123 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 2), seg.getCentroid(), TEST_EPS);
124
125 Assert.assertEquals(2, seg.getSubspaceStart(), TEST_EPS);
126 Assert.assertEquals(2, seg.getSubspaceEnd(), TEST_EPS);
127
128 Assert.assertEquals(0, seg.getSize(), TEST_EPS);
129 }
130
131 @Test
132 public void testFromPoints_givenLine_invalidArgs() {
133
134 final Vector2D p0 = Vector2D.of(1, 0);
135 final Vector2D p1 = Vector2D.of(2, 0);
136
137 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION);
138
139
140 GeometryTestUtils.assertThrows(() -> {
141 Lines.segmentFromPoints(line, Vector2D.NaN, p1);
142 }, IllegalArgumentException.class, "Invalid line segment locations: NaN, 2.0");
143
144 GeometryTestUtils.assertThrows(() -> {
145 Lines.segmentFromPoints(line, p0, Vector2D.NaN);
146 }, IllegalArgumentException.class, "Invalid line segment locations: 1.0, NaN");
147
148 GeometryTestUtils.assertThrows(() -> {
149 Lines.segmentFromPoints(line, Vector2D.NEGATIVE_INFINITY, p1);
150 }, IllegalArgumentException.class, "Invalid line segment locations: NaN, 2.0");
151
152 GeometryTestUtils.assertThrows(() -> {
153 Lines.segmentFromPoints(line, p0, Vector2D.POSITIVE_INFINITY);
154 }, IllegalArgumentException.class, "Invalid line segment locations: 1.0, NaN");
155 }
156
157 @Test
158 public void testFromLocations() {
159
160 final Line line = Lines.fromPointAndDirection(Vector2D.of(-1, 0), Vector2D.Unit.PLUS_Y, TEST_PRECISION);
161
162
163 final Segment seg = Lines.segmentFromLocations(line, -1, 2);
164
165
166 Assert.assertFalse(seg.isFull());
167 Assert.assertFalse(seg.isEmpty());
168 Assert.assertFalse(seg.isInfinite());
169 Assert.assertTrue(seg.isFinite());
170
171 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, -1), seg.getStartPoint(), TEST_EPS);
172 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 2), seg.getEndPoint(), TEST_EPS);
173 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 0.5), seg.getCentroid(), TEST_EPS);
174
175 Assert.assertEquals(-1, seg.getSubspaceStart(), TEST_EPS);
176 Assert.assertEquals(2, seg.getSubspaceEnd(), TEST_EPS);
177
178 Assert.assertEquals(3, seg.getSize(), TEST_EPS);
179 }
180
181 @Test
182 public void testFromLocations_reversedLocationOrder() {
183
184 final Line line = Lines.fromPointAndDirection(Vector2D.of(-1, 0), Vector2D.Unit.PLUS_Y, TEST_PRECISION);
185
186
187 final Segment seg = Lines.segmentFromLocations(line, 2, -1);
188
189
190 Assert.assertFalse(seg.isFull());
191 Assert.assertFalse(seg.isEmpty());
192 Assert.assertFalse(seg.isInfinite());
193 Assert.assertTrue(seg.isFinite());
194
195 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, -1), seg.getStartPoint(), TEST_EPS);
196 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 2), seg.getEndPoint(), TEST_EPS);
197 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 0.5), seg.getCentroid(), TEST_EPS);
198
199 Assert.assertEquals(-1, seg.getSubspaceStart(), TEST_EPS);
200 Assert.assertEquals(2, seg.getSubspaceEnd(), TEST_EPS);
201
202 Assert.assertEquals(3, seg.getSize(), TEST_EPS);
203 }
204
205 @Test
206 public void testFromLocations_singlePoint() {
207
208 final Line line = Lines.fromPointAndDirection(Vector2D.of(-1, 0), Vector2D.Unit.PLUS_Y, TEST_PRECISION);
209
210
211 final Segment seg = Lines.segmentFromLocations(line, 1, 1);
212
213
214 Assert.assertFalse(seg.isFull());
215 Assert.assertFalse(seg.isEmpty());
216 Assert.assertFalse(seg.isInfinite());
217 Assert.assertTrue(seg.isFinite());
218
219 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 1), seg.getStartPoint(), TEST_EPS);
220 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 1), seg.getEndPoint(), TEST_EPS);
221 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 1), seg.getCentroid(), TEST_EPS);
222
223 Assert.assertEquals(1, seg.getSubspaceStart(), TEST_EPS);
224 Assert.assertEquals(1, seg.getSubspaceEnd(), TEST_EPS);
225
226 Assert.assertEquals(0, seg.getSize(), TEST_EPS);
227 }
228
229 @Test
230 public void testFromLocations_invalidArgs() {
231
232 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION);
233
234
235 GeometryTestUtils.assertThrows(() -> {
236 Lines.segmentFromLocations(line, Double.NaN, 2);
237 }, IllegalArgumentException.class, "Invalid line segment locations: NaN, 2.0");
238
239 GeometryTestUtils.assertThrows(() -> {
240 Lines.segmentFromLocations(line, 1, Double.NaN);
241 }, IllegalArgumentException.class, "Invalid line segment locations: 1.0, NaN");
242
243 GeometryTestUtils.assertThrows(() -> {
244 Lines.segmentFromLocations(line, Double.NEGATIVE_INFINITY, 2);
245 }, IllegalArgumentException.class, "Invalid line segment locations: -Infinity, 2.0");
246
247 GeometryTestUtils.assertThrows(() -> {
248 Lines.segmentFromLocations(line, 1, Double.POSITIVE_INFINITY);
249 }, IllegalArgumentException.class, "Invalid line segment locations: 1.0, Infinity");
250 }
251
252 @Test
253 public void testGetBounds() {
254
255 final Segment seg = Lines.segmentFromPoints(Vector2D.of(-1, 4), Vector2D.of(2, -2), TEST_PRECISION);
256
257
258 final Bounds2D bounds = seg.getBounds();
259
260
261 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, -2), bounds.getMin(), TEST_EPS);
262 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 4), bounds.getMax(), TEST_EPS);
263 }
264
265 @Test
266 public void testTransform() {
267
268 final AffineTransformMatrix2D t = AffineTransformMatrix2D.createRotation(0.5 * Math.PI)
269 .translate(Vector2D.Unit.PLUS_X);
270
271 final Segment seg = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
272
273
274 final Segment result = seg.transform(t);
275
276
277 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 0), result.getStartPoint(), TEST_EPS);
278 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), result.getEndPoint(), TEST_EPS);
279 }
280
281 @Test
282 public void testTransform_reflection() {
283
284 final AffineTransformMatrix2D t = AffineTransformMatrix2D.createRotation(0.5 * Math.PI)
285 .translate(Vector2D.Unit.PLUS_X)
286 .scale(1, -1);
287
288 final Segment seg = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
289
290
291 final Segment result = seg.transform(t);
292
293
294 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 0), result.getStartPoint(), TEST_EPS);
295 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, -1), result.getEndPoint(), TEST_EPS);
296 }
297
298 @Test
299 public void testReverse() {
300
301 final Vector2D start = Vector2D.of(1, 2);
302
303 EuclideanTestUtils.permuteSkipZero(-4, 4, 1, (x, y) -> {
304 final Vector2D end = Vector2D.of(x, y).add(start);
305
306 final Segment seg = Lines.segmentFromPoints(start, end, TEST_PRECISION);
307
308
309 final Segment rev = seg.reverse();
310
311
312 Assert.assertEquals(seg.getSize(), rev.getSize(), TEST_EPS);
313
314 EuclideanTestUtils.assertCoordinatesEqual(seg.getLine().getOrigin(), rev.getLine().getOrigin(), TEST_EPS);
315 Assert.assertEquals(-1, seg.getLine().getDirection().dot(rev.getLine().getDirection()), TEST_EPS);
316
317 EuclideanTestUtils.assertCoordinatesEqual(seg.getEndPoint(), rev.getStartPoint(), TEST_EPS);
318 EuclideanTestUtils.assertCoordinatesEqual(seg.getStartPoint(), rev.getEndPoint(), TEST_EPS);
319 });
320 }
321
322 @Test
323 public void testClosest() {
324
325 final Vector2D p1 = Vector2D.of(0, -1);
326 final Vector2D p2 = Vector2D.of(0, 1);
327 final Segment seg = Lines.segmentFromPoints(p1, p2, TEST_PRECISION);
328
329
330 EuclideanTestUtils.assertCoordinatesEqual(p1, seg.closest(p1), TEST_EPS);
331 EuclideanTestUtils.assertCoordinatesEqual(p1, seg.closest(Vector2D.of(0, -2)), TEST_EPS);
332 EuclideanTestUtils.assertCoordinatesEqual(p1, seg.closest(Vector2D.of(2, -2)), TEST_EPS);
333 EuclideanTestUtils.assertCoordinatesEqual(p1, seg.closest(Vector2D.of(-1, -1)), TEST_EPS);
334
335 EuclideanTestUtils.assertCoordinatesEqual(p2, seg.closest(p2), TEST_EPS);
336 EuclideanTestUtils.assertCoordinatesEqual(p2, seg.closest(Vector2D.of(0, 2)), TEST_EPS);
337 EuclideanTestUtils.assertCoordinatesEqual(p2, seg.closest(Vector2D.of(-2, 2)), TEST_EPS);
338 EuclideanTestUtils.assertCoordinatesEqual(p2, seg.closest(Vector2D.of(-1, 1)), TEST_EPS);
339
340 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, seg.closest(Vector2D.ZERO), TEST_EPS);
341 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 0.5), seg.closest(Vector2D.of(1, 0.5)), TEST_EPS);
342 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -0.5), seg.closest(Vector2D.of(-2, -0.5)), TEST_EPS);
343 }
344
345 @Test
346 public void testClassify() {
347
348 final Segment seg = Lines.segmentFromPoints(Vector2D.of(1, 1), Vector2D.of(3, 1), TEST_PRECISION);
349
350
351 EuclideanTestUtils.assertRegionLocation(seg, RegionLocation.OUTSIDE,
352 Vector2D.of(2, 2), Vector2D.of(2, 0),
353 Vector2D.of(0, 1), Vector2D.of(4, 1));
354
355 EuclideanTestUtils.assertRegionLocation(seg, RegionLocation.BOUNDARY,
356 Vector2D.of(1, 1), Vector2D.of(3, 1),
357 Vector2D.of(1 + 1e-16, 1), Vector2D.of(3, 1 - 1e-12));
358
359 EuclideanTestUtils.assertRegionLocation(seg, RegionLocation.INSIDE, Vector2D.of(2, 1));
360 }
361
362 @Test
363 public void testSplit() {
364
365 final Vector2D p0 = Vector2D.of(1, 1);
366 final Vector2D p1 = Vector2D.of(3, 1);
367 final Vector2D mid = p0.lerp(p1, 0.5);
368 final Vector2D low = Vector2D.of(0, 1);
369 final Vector2D high = Vector2D.of(3, 1);
370
371 final Vector2D delta = Vector2D.of(1e-11, 1e-11);
372
373 final Segment seg = Lines.segmentFromPoints(Vector2D.of(1, 1), Vector2D.of(3, 1), TEST_PRECISION);
374
375
376
377
378 checkSplit(seg.split(Lines.fromPointAndAngle(Vector2D.of(2, 2), 0, TEST_PRECISION)),
379 null, null,
380 p0, p1);
381 checkSplit(seg.split(Lines.fromPointAndAngle(Vector2D.of(2, 2), Math.PI, TEST_PRECISION)),
382 p0, p1,
383 null, null);
384
385
386 checkSplit(seg.split(Lines.fromPointAndAngle(p0.add(delta), 1e-20, TEST_PRECISION)),
387 null, null,
388 null, null);
389
390
391 checkSplit(seg.split(Lines.fromPointAndAngle(mid, 1, TEST_PRECISION)),
392 p0, mid,
393 mid, p1);
394 checkSplit(seg.split(Lines.fromPointAndAngle(mid, -1, TEST_PRECISION)),
395 mid, p1,
396 p0, mid);
397
398
399 checkSplit(seg.split(Lines.fromPointAndAngle(p0.subtract(delta), 1, TEST_PRECISION)),
400 null, null,
401 p0, p1);
402 checkSplit(seg.split(Lines.fromPointAndAngle(p0.add(delta), -1, TEST_PRECISION)),
403 p0, p1,
404 null, null);
405
406
407 checkSplit(seg.split(Lines.fromPointAndAngle(p1.subtract(delta), 1, TEST_PRECISION)),
408 p0, p1,
409 null, null);
410 checkSplit(seg.split(Lines.fromPointAndAngle(p1.add(delta), -1, TEST_PRECISION)),
411 null, null,
412 p0, p1);
413
414
415 checkSplit(seg.split(Lines.fromPointAndAngle(low, 1, TEST_PRECISION)),
416 null, null,
417 p0, p1);
418 checkSplit(seg.split(Lines.fromPointAndAngle(low, -1, TEST_PRECISION)),
419 p0, p1,
420 null, null);
421
422
423 checkSplit(seg.split(Lines.fromPointAndAngle(high, 1, TEST_PRECISION)),
424 p0, p1,
425 null, null);
426 checkSplit(seg.split(Lines.fromPointAndAngle(high, -1, TEST_PRECISION)),
427 null, null,
428 p0, p1);
429 }
430
431 @Test
432 public void testSplit_pointsOnSplitterWithLineIntersection() {
433
434
435
436
437
438 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-5);
439
440 final Segment seg = Lines.segmentFromPoints(Vector2D.of(1, 1e-8), Vector2D.of(1.01, 1e-6), precision);
441
442 final Line splitter = Lines.fromPointAndAngle(Vector2D.ZERO, 0, precision);
443
444
445 final Split<LineConvexSubset> split = seg.split(splitter);
446
447
448 Assert.assertEquals(SplitLocation.NEITHER, split.getLocation());
449
450 Assert.assertNull(split.getMinus());
451 Assert.assertNull(split.getPlus());
452 }
453
454 @Test
455 public void testGetInterval() {
456
457 final Segment seg = Lines.segmentFromPoints(Vector2D.of(2, -1), Vector2D.of(2, 2), TEST_PRECISION);
458
459
460 final Interval interval = seg.getInterval();
461
462
463 Assert.assertEquals(-1, interval.getMin(), TEST_EPS);
464 Assert.assertEquals(2, interval.getMax(), TEST_EPS);
465
466 Assert.assertSame(seg.getLine().getPrecision(), interval.getMinBoundary().getPrecision());
467 }
468
469 @Test
470 public void testGetInterval_singlePoint() {
471
472 final Line line = Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION);
473 final Segment seg = Lines.segmentFromLocations(line, 1, 1);
474
475
476 final Interval interval = seg.getInterval();
477
478
479 Assert.assertEquals(1, interval.getMin(), TEST_EPS);
480 Assert.assertEquals(1, interval.getMax(), TEST_EPS);
481 Assert.assertEquals(0, interval.getSize(), TEST_EPS);
482
483 Assert.assertSame(seg.getLine().getPrecision(), interval.getMinBoundary().getPrecision());
484 }
485
486 @Test
487 public void testToString() {
488
489 final Segment seg = Lines.segmentFromPoints(Vector2D.ZERO, Vector2D.of(1, 0), TEST_PRECISION);
490
491
492 final String str = seg.toString();
493
494
495 GeometryTestUtils.assertContains("Segment[startPoint= (0", str);
496 GeometryTestUtils.assertContains(", endPoint= (1", str);
497 }
498
499 private static void checkSplit(final Split<LineConvexSubset> split, final Vector2D minusStart, final Vector2D minusEnd,
500 final Vector2D plusStart, final Vector2D plusEnd) {
501
502 final Segment minus = (Segment) split.getMinus();
503 if (minusStart != null) {
504 EuclideanTestUtils.assertCoordinatesEqual(minusStart, minus.getStartPoint(), TEST_EPS);
505 EuclideanTestUtils.assertCoordinatesEqual(minusEnd, minus.getEndPoint(), TEST_EPS);
506 } else {
507 Assert.assertNull(minus);
508 }
509
510 final Segment plus = (Segment) split.getPlus();
511 if (plusStart != null) {
512 EuclideanTestUtils.assertCoordinatesEqual(plusStart, plus.getStartPoint(), TEST_EPS);
513 EuclideanTestUtils.assertCoordinatesEqual(plusEnd, plus.getEndPoint(), TEST_EPS);
514 } else {
515 Assert.assertNull(plus);
516 }
517 }
518 }