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.precision.DoublePrecisionContext;
21 import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
22 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
23 import org.apache.commons.geometry.euclidean.oned.AffineTransformMatrix1D;
24 import org.apache.commons.geometry.euclidean.oned.Vector1D;
25 import org.apache.commons.geometry.euclidean.twod.Line.SubspaceTransform;
26 import org.apache.commons.numbers.angle.PlaneAngleRadians;
27 import org.junit.Assert;
28 import org.junit.Test;
29
30 public class LineTest {
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 checkLine(Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION),
41 Vector2D.ZERO, Vector2D.Unit.PLUS_X);
42 checkLine(Lines.fromPoints(Vector2D.ZERO, Vector2D.of(100, 0), TEST_PRECISION),
43 Vector2D.ZERO, Vector2D.Unit.PLUS_X);
44 checkLine(Lines.fromPoints(Vector2D.of(100, 0), Vector2D.ZERO, TEST_PRECISION),
45 Vector2D.ZERO, Vector2D.Unit.MINUS_X);
46 checkLine(Lines.fromPoints(Vector2D.of(-100, 0), Vector2D.of(100, 0), TEST_PRECISION),
47 Vector2D.ZERO, Vector2D.Unit.PLUS_X);
48
49 checkLine(Lines.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 2), TEST_PRECISION),
50 Vector2D.of(-1, 1), Vector2D.of(1, 1).normalize());
51 checkLine(Lines.fromPoints(Vector2D.of(0, 2), Vector2D.of(-2, 0), TEST_PRECISION),
52 Vector2D.of(-1, 1), Vector2D.of(-1, -1).normalize());
53 }
54
55 @Test
56 public void testFromPoints_pointsTooClose() {
57
58 GeometryTestUtils.assertThrows(() -> Lines.fromPoints(Vector2D.Unit.PLUS_X, Vector2D.Unit.PLUS_X, TEST_PRECISION),
59 IllegalArgumentException.class, "Line direction cannot be zero");
60 GeometryTestUtils.assertThrows(() -> Lines.fromPoints(Vector2D.Unit.PLUS_X, Vector2D.of(1 + 1e-11, 1e-11), TEST_PRECISION),
61 IllegalArgumentException.class, "Line direction cannot be zero");
62 }
63
64 @Test
65 public void testFromPointAndDirection() {
66
67 checkLine(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION),
68 Vector2D.ZERO, Vector2D.Unit.PLUS_X);
69 checkLine(Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(100, 0), TEST_PRECISION),
70 Vector2D.ZERO, Vector2D.Unit.PLUS_X);
71 checkLine(Lines.fromPointAndDirection(Vector2D.of(-100, 0), Vector2D.of(100, 0), TEST_PRECISION),
72 Vector2D.ZERO, Vector2D.Unit.PLUS_X);
73
74 checkLine(Lines.fromPointAndDirection(Vector2D.of(-2, 0), Vector2D.of(1, 1), TEST_PRECISION),
75 Vector2D.of(-1, 1), Vector2D.of(1, 1).normalize());
76 checkLine(Lines.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(-1, -1), TEST_PRECISION),
77 Vector2D.of(-1, 1), Vector2D.of(-1, -1).normalize());
78 }
79
80 @Test
81 public void testFromPointAndDirection_directionIsZero() {
82
83 GeometryTestUtils.assertThrows(() -> Lines.fromPointAndDirection(Vector2D.Unit.PLUS_X, Vector2D.ZERO, TEST_PRECISION),
84 IllegalArgumentException.class, "Line direction cannot be zero");
85 GeometryTestUtils.assertThrows(() -> Lines.fromPointAndDirection(Vector2D.Unit.PLUS_X, Vector2D.of(1e-11, -1e-12), TEST_PRECISION),
86 IllegalArgumentException.class, "Line direction cannot be zero");
87 }
88
89 @Test
90 public void testFromPointAndAngle() {
91
92 checkLine(Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION),
93 Vector2D.ZERO, Vector2D.Unit.PLUS_X);
94 checkLine(Lines.fromPointAndAngle(Vector2D.of(1, 1), PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION),
95 Vector2D.of(1, 0), Vector2D.Unit.PLUS_Y);
96 checkLine(Lines.fromPointAndAngle(Vector2D.of(-1, -1), PlaneAngleRadians.PI, TEST_PRECISION),
97 Vector2D.of(0, -1), Vector2D.Unit.MINUS_X);
98 checkLine(Lines.fromPointAndAngle(Vector2D.of(1, -1), -PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION),
99 Vector2D.of(1, 0), Vector2D.Unit.MINUS_Y);
100 checkLine(Lines.fromPointAndAngle(Vector2D.of(-1, 1), PlaneAngleRadians.TWO_PI, TEST_PRECISION),
101 Vector2D.of(0, 1), Vector2D.Unit.PLUS_X);
102 }
103
104 @Test
105 public void testGetAngle() {
106
107 final Vector2D vec = Vector2D.of(1, 2);
108
109 for (double theta = -4 * PlaneAngleRadians.PI; theta < 2 * PlaneAngleRadians.PI; theta += 0.1) {
110 final Line line = Lines.fromPointAndAngle(vec, theta, TEST_PRECISION);
111
112
113 Assert.assertEquals(PlaneAngleRadians.normalizeBetweenZeroAndTwoPi(theta),
114 line.getAngle(), TEST_EPS);
115 }
116 }
117
118 @Test
119 public void testGetAngle_multiplesOfPi() {
120
121 final Vector2D vec = Vector2D.of(-1, -2);
122
123
124 Assert.assertEquals(0, Lines.fromPointAndAngle(vec, 0.0, TEST_PRECISION).getAngle(), TEST_EPS);
125 Assert.assertEquals(PlaneAngleRadians.PI, Lines.fromPointAndAngle(vec, PlaneAngleRadians.PI, TEST_PRECISION).getAngle(), TEST_EPS);
126 Assert.assertEquals(0, Lines.fromPointAndAngle(vec, PlaneAngleRadians.TWO_PI, TEST_PRECISION).getAngle(), TEST_EPS);
127
128 Assert.assertEquals(0, Lines.fromPointAndAngle(vec, -2 * PlaneAngleRadians.PI, TEST_PRECISION).getAngle(), TEST_EPS);
129 Assert.assertEquals(PlaneAngleRadians.PI, Lines.fromPointAndAngle(vec, -3 * PlaneAngleRadians.PI, TEST_PRECISION).getAngle(), TEST_EPS);
130 Assert.assertEquals(0, Lines.fromPointAndAngle(vec, -4 * PlaneAngleRadians.TWO_PI, TEST_PRECISION).getAngle(), TEST_EPS);
131 }
132
133 @Test
134 public void testGetDirection() {
135
136 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.PLUS_X,
137 Lines.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 0), TEST_PRECISION).getDirection(), TEST_EPS);
138 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_Y,
139 Lines.fromPoints(Vector2D.of(0, 1), Vector2D.of(0, -1), TEST_PRECISION).getDirection(), TEST_EPS);
140
141 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_X,
142 Lines.fromPoints(Vector2D.of(2, 2), Vector2D.of(1, 2), TEST_PRECISION).getDirection(), TEST_EPS);
143 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.PLUS_X,
144 Lines.fromPoints(Vector2D.of(10, -2), Vector2D.of(10.1, -2), TEST_PRECISION).getDirection(), TEST_EPS);
145
146 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_Y,
147 Lines.fromPoints(Vector2D.of(3, 2), Vector2D.of(3, 1), TEST_PRECISION).getDirection(), TEST_EPS);
148 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.PLUS_Y,
149 Lines.fromPoints(Vector2D.of(-3, 10), Vector2D.of(-3, 10.1), TEST_PRECISION).getDirection(), TEST_EPS);
150
151 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, -1).normalize(),
152 Lines.fromPoints(Vector2D.of(0, 2), Vector2D.of(2, 0), TEST_PRECISION).getDirection(), TEST_EPS);
153 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, 1).normalize(),
154 Lines.fromPoints(Vector2D.of(2, 0), Vector2D.of(0, 2), TEST_PRECISION).getDirection(), TEST_EPS);
155 }
156
157 @Test
158 public void testGetOffsetDirection() {
159
160 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_Y,
161 Lines.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 0), TEST_PRECISION).getOffsetDirection(), TEST_EPS);
162 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_X,
163 Lines.fromPoints(Vector2D.of(0, 1), Vector2D.of(0, -1), TEST_PRECISION).getOffsetDirection(), TEST_EPS);
164
165 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.PLUS_Y,
166 Lines.fromPoints(Vector2D.of(2, 2), Vector2D.of(1, 2), TEST_PRECISION).getOffsetDirection(), TEST_EPS);
167 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_Y,
168 Lines.fromPoints(Vector2D.of(10, -2), Vector2D.of(10.1, -2), TEST_PRECISION).getOffsetDirection(), TEST_EPS);
169
170 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.MINUS_X,
171 Lines.fromPoints(Vector2D.of(3, 2), Vector2D.of(3, 1), TEST_PRECISION).getOffsetDirection(), TEST_EPS);
172 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.Unit.PLUS_X,
173 Lines.fromPoints(Vector2D.of(-3, 10), Vector2D.of(-3, 10.1), TEST_PRECISION).getOffsetDirection(), TEST_EPS);
174
175 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-1, -1).normalize(),
176 Lines.fromPoints(Vector2D.of(0, 2), Vector2D.of(2, 0), TEST_PRECISION).getOffsetDirection(), TEST_EPS);
177 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1).normalize(),
178 Lines.fromPoints(Vector2D.of(2, 0), Vector2D.of(0, 2), TEST_PRECISION).getOffsetDirection(), TEST_EPS);
179 }
180
181 @Test
182 public void testGetOrigin() {
183
184 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO,
185 Lines.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 0), TEST_PRECISION).getOrigin(), TEST_EPS);
186 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO,
187 Lines.fromPoints(Vector2D.of(0, 1), Vector2D.of(0, -1), TEST_PRECISION).getOrigin(), TEST_EPS);
188
189 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2),
190 Lines.fromPoints(Vector2D.of(2, 2), Vector2D.of(3, 2), TEST_PRECISION).getOrigin(), TEST_EPS);
191 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -2),
192 Lines.fromPoints(Vector2D.of(10, -2), Vector2D.of(10.1, -2), TEST_PRECISION).getOrigin(), TEST_EPS);
193
194 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 0),
195 Lines.fromPoints(Vector2D.of(3, 2), Vector2D.of(3, 1), TEST_PRECISION).getOrigin(), TEST_EPS);
196 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 0),
197 Lines.fromPoints(Vector2D.of(-3, 10), Vector2D.of(-3, 10.1), TEST_PRECISION).getOrigin(), TEST_EPS);
198
199 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1),
200 Lines.fromPoints(Vector2D.of(0, 2), Vector2D.of(2, 0), TEST_PRECISION).getOrigin(), TEST_EPS);
201 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1),
202 Lines.fromPoints(Vector2D.of(2, 0), Vector2D.of(0, 2), TEST_PRECISION).getOrigin(), TEST_EPS);
203 }
204
205 @Test
206 public void testGetOriginOffset() {
207
208 final double sqrt2 = Math.sqrt(2);
209
210
211 Assert.assertEquals(0.0,
212 Lines.fromPoints(Vector2D.of(0, 0), Vector2D.of(1, 1), TEST_PRECISION).getOriginOffset(), TEST_EPS);
213 Assert.assertEquals(0.0,
214 Lines.fromPoints(Vector2D.of(0, 0), Vector2D.of(-1, -1), TEST_PRECISION).getOriginOffset(), TEST_EPS);
215
216 Assert.assertEquals(sqrt2,
217 Lines.fromPoints(Vector2D.of(-1, 1), Vector2D.of(0, 2), TEST_PRECISION).getOriginOffset(), TEST_EPS);
218 Assert.assertEquals(-sqrt2,
219 Lines.fromPoints(Vector2D.of(0, -2), Vector2D.of(1, -1), TEST_PRECISION).getOriginOffset(), TEST_EPS);
220
221 Assert.assertEquals(-sqrt2,
222 Lines.fromPoints(Vector2D.of(0, 2), Vector2D.of(-1, 1), TEST_PRECISION).getOriginOffset(), TEST_EPS);
223 Assert.assertEquals(sqrt2,
224 Lines.fromPoints(Vector2D.of(1, -1), Vector2D.of(0, -2), TEST_PRECISION).getOriginOffset(), TEST_EPS);
225 }
226
227 @Test
228 public void testGetPrecision() {
229
230 Assert.assertSame(TEST_PRECISION, Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION).getPrecision());
231 Assert.assertSame(TEST_PRECISION, Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION).getPrecision());
232 Assert.assertSame(TEST_PRECISION, Lines.fromPointAndAngle(Vector2D.ZERO, 0, TEST_PRECISION).getPrecision());
233 }
234
235 @Test
236 public void testReverse() {
237
238 final Vector2D pt = Vector2D.of(0, 1);
239 final Vector2D dir = Vector2D.Unit.PLUS_X;
240 final Line line = Lines.fromPointAndDirection(pt, dir, TEST_PRECISION);
241
242
243 final Line reversed = line.reverse();
244 final Line doubleReversed = reversed.reverse();
245
246
247 checkLine(reversed, pt, dir.negate());
248 Assert.assertEquals(-1, reversed.getOriginOffset(), TEST_EPS);
249
250 checkLine(doubleReversed, pt, dir);
251 Assert.assertEquals(1, doubleReversed.getOriginOffset(), TEST_EPS);
252 }
253
254 @Test
255 public void testAbscissa() {
256
257 final Line line = Lines.fromPoints(Vector2D.of(-2, -2), Vector2D.of(2, 1), TEST_PRECISION);
258
259
260 Assert.assertEquals(0.0, line.abscissa(Vector2D.of(-3, 4)), TEST_EPS);
261 Assert.assertEquals(0.0, line.abscissa(Vector2D.of(3, -4)), TEST_EPS);
262 Assert.assertEquals(5.0, line.abscissa(Vector2D.of(7, -1)), TEST_EPS);
263 Assert.assertEquals(-5.0, line.abscissa(Vector2D.of(-1, -7)), TEST_EPS);
264 }
265
266 @Test
267 public void testToSubspace() {
268
269 final Line line = Lines.fromPoints(Vector2D.of(2, 1), Vector2D.of(-2, -2), TEST_PRECISION);
270
271
272 Assert.assertEquals(0.0, line.toSubspace(Vector2D.of(-3, 4)).getX(), TEST_EPS);
273 Assert.assertEquals(0.0, line.toSubspace(Vector2D.of(3, -4)).getX(), TEST_EPS);
274 Assert.assertEquals(-5.0, line.toSubspace(Vector2D.of(7, -1)).getX(), TEST_EPS);
275 Assert.assertEquals(5.0, line.toSubspace(Vector2D.of(-1, -7)).getX(), TEST_EPS);
276 }
277
278 @Test
279 public void testToSpace_throughOrigin() {
280
281 final double invSqrt2 = 1 / Math.sqrt(2);
282 final Vector2D dir = Vector2D.of(invSqrt2, invSqrt2);
283
284 final Line line = Lines.fromPoints(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
285
286
287 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, line.toSpace(Vector1D.of(0)), TEST_EPS);
288
289 for (int i = 0; i < 100; ++i) {
290 EuclideanTestUtils.assertCoordinatesEqual(dir.multiply(i), line.toSpace(Vector1D.of(i)), TEST_EPS);
291 EuclideanTestUtils.assertCoordinatesEqual(dir.multiply(-i), line.toSpace(Vector1D.of(-i)), TEST_EPS);
292 }
293 }
294
295 @Test
296 public void testToSpace_offsetFromOrigin() {
297
298 final double angle = PlaneAngleRadians.PI / 6;
299 final double cos = Math.cos(angle);
300 final double sin = Math.sin(angle);
301 final Vector2D pt = Vector2D.of(-5, 0);
302
303 final double h = Math.abs(pt.getX()) * cos;
304 final double d = h * cos;
305 final Vector2D origin = Vector2D.of(
306 pt.getX() + d,
307 h * sin
308 );
309 final Vector2D dir = Vector2D.of(cos, sin);
310
311 final Line line = Lines.fromPointAndAngle(pt, angle, TEST_PRECISION);
312
313
314 EuclideanTestUtils.assertCoordinatesEqual(origin, line.toSpace(Vector1D.of(0)), TEST_EPS);
315
316 for (int i = 0; i < 100; ++i) {
317 EuclideanTestUtils.assertCoordinatesEqual(origin.add(dir.multiply(i)), line.toSpace(Vector1D.of(i)), TEST_EPS);
318 EuclideanTestUtils.assertCoordinatesEqual(origin.add(dir.multiply(-i)), line.toSpace(Vector1D.of(-i)), TEST_EPS);
319 }
320 }
321
322 @Test
323 public void testIntersection() {
324
325 final Line a = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
326 final Line b = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_Y, TEST_PRECISION);
327 final Line c = Lines.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(2, 1), TEST_PRECISION);
328 final Line d = Lines.fromPointAndDirection(Vector2D.of(0, -1), Vector2D.of(2, -1), TEST_PRECISION);
329
330
331 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, a.intersection(b), TEST_EPS);
332 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, b.intersection(a), TEST_EPS);
333
334 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-4, 0), a.intersection(c), TEST_EPS);
335 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-4, 0), c.intersection(a), TEST_EPS);
336
337 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 0), a.intersection(d), TEST_EPS);
338 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 0), d.intersection(a), TEST_EPS);
339
340 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2), b.intersection(c), TEST_EPS);
341 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2), c.intersection(b), TEST_EPS);
342
343 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -1), b.intersection(d), TEST_EPS);
344 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, -1), d.intersection(b), TEST_EPS);
345
346 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 0.5), c.intersection(d), TEST_EPS);
347 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 0.5), d.intersection(c), TEST_EPS);
348 }
349
350 @Test
351 public void testIntersection_parallel() {
352
353 final Line a = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
354 final Line b = Lines.fromPointAndDirection(Vector2D.of(0, 1), Vector2D.Unit.PLUS_X, TEST_PRECISION);
355
356 final Line c = Lines.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(2, 1), TEST_PRECISION);
357 final Line d = Lines.fromPointAndDirection(Vector2D.of(0, -1), Vector2D.of(2, 1), TEST_PRECISION);
358
359
360 Assert.assertNull(a.intersection(b));
361 Assert.assertNull(b.intersection(a));
362
363 Assert.assertNull(c.intersection(d));
364 Assert.assertNull(d.intersection(c));
365 }
366
367 @Test
368 public void testIntersection_coincident() {
369
370 final Line a = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
371 final Line b = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
372
373 final Line c = Lines.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(2, 1), TEST_PRECISION);
374 final Line d = Lines.fromPointAndDirection(Vector2D.of(0, 2), Vector2D.of(2, 1), TEST_PRECISION);
375
376
377 Assert.assertNull(a.intersection(b));
378 Assert.assertNull(b.intersection(a));
379
380 Assert.assertNull(c.intersection(d));
381 Assert.assertNull(d.intersection(c));
382 }
383
384 @Test
385 public void testAngle() {
386
387 final Line a = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
388 final Line b = Lines.fromPointAndAngle(Vector2D.of(1, 4), PlaneAngleRadians.PI, TEST_PRECISION);
389 final Line c = Lines.fromPointAndDirection(Vector2D.of(1, 1), Vector2D.of(2, 2), TEST_PRECISION);
390
391
392 Assert.assertEquals(0.0, a.angle(a), TEST_EPS);
393 Assert.assertEquals(-PlaneAngleRadians.PI, a.angle(b), TEST_EPS);
394 Assert.assertEquals(0.25 * PlaneAngleRadians.PI, a.angle(c), TEST_EPS);
395
396 Assert.assertEquals(0.0, b.angle(b), TEST_EPS);
397 Assert.assertEquals(-PlaneAngleRadians.PI, b.angle(a), TEST_EPS);
398 Assert.assertEquals(-0.75 * PlaneAngleRadians.PI, b.angle(c), TEST_EPS);
399
400 Assert.assertEquals(0.0, c.angle(c), TEST_EPS);
401 Assert.assertEquals(-0.25 * PlaneAngleRadians.PI, c.angle(a), TEST_EPS);
402 Assert.assertEquals(0.75 * PlaneAngleRadians.PI, c.angle(b), TEST_EPS);
403 }
404
405 @Test
406 public void testProject() {
407
408 final Line xAxis = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
409 final Line yAxis = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_Y, TEST_PRECISION);
410
411 final double diagonalYIntercept = 1;
412 final Vector2D diagonalDir = Vector2D.of(1, 2);
413 final Line diagonal = Lines.fromPointAndDirection(Vector2D.of(0, diagonalYIntercept), diagonalDir, TEST_PRECISION);
414
415 EuclideanTestUtils.permute(-5, 5, 0.5, (x, y) -> {
416 final Vector2D pt = Vector2D.of(x, y);
417
418
419 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(x, 0), xAxis.project(pt), TEST_EPS);
420 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, y), yAxis.project(pt), TEST_EPS);
421
422 final Vector2D diagonalPt = diagonal.project(pt);
423 Assert.assertTrue(diagonal.contains(diagonalPt));
424 Assert.assertEquals(diagonal.distance(pt), pt.distance(diagonalPt), TEST_EPS);
425
426
427 Assert.assertEquals(diagonalPt.getY(),
428 (diagonalDir.getY() * diagonalPt.getX() / diagonalDir.getX()) + diagonalYIntercept, TEST_EPS);
429 });
430 }
431
432 @Test
433 public void testSpan() {
434
435 final Line line = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
436
437
438 final LineConvexSubset result = line.span();
439
440
441 Assert.assertSame(line, result.getHyperplane());
442 Assert.assertSame(line, result.getLine());
443 }
444
445 @Test
446 public void testSegment_doubles() {
447
448 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), 0.0, TEST_PRECISION);
449
450
451 final Segment segment = line.segment(1, 2);
452
453
454 Assert.assertSame(line, segment.getLine());
455 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), segment.getStartPoint(), TEST_EPS);
456 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 1), segment.getEndPoint(), TEST_EPS);
457 }
458
459 @Test
460 public void testSegment_pointsOnLine() {
461
462 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), 0.0, TEST_PRECISION);
463
464
465 final Segment segment = line.segment(Vector2D.of(3, 1), Vector2D.of(2, 1));
466
467
468 Assert.assertSame(line, segment.getLine());
469 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 1), segment.getStartPoint(), TEST_EPS);
470 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(3, 1), segment.getEndPoint(), TEST_EPS);
471 }
472
473 @Test
474 public void testSegment_pointsProjectedOnLine() {
475
476 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), 0.0, TEST_PRECISION);
477
478
479 final Segment segment = line.segment(Vector2D.of(-3, 2), Vector2D.of(2, -1));
480
481
482 Assert.assertSame(line, segment.getLine());
483 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 1), segment.getStartPoint(), TEST_EPS);
484 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(2, 1), segment.getEndPoint(), TEST_EPS);
485 }
486
487 @Test
488 public void testLineTo_pointOnLine() {
489
490 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), PlaneAngleRadians.PI, TEST_PRECISION);
491
492
493 final ReverseRay halfLine = line.reverseRayTo(Vector2D.of(-3, 1));
494
495
496 Assert.assertSame(line, halfLine.getLine());
497 Assert.assertTrue(halfLine.isInfinite());
498 Assert.assertNull(halfLine.getStartPoint());
499 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 1), halfLine.getEndPoint(), TEST_EPS);
500
501 Assert.assertTrue(halfLine.contains(Vector2D.of(1, 1)));
502 Assert.assertFalse(halfLine.contains(Vector2D.of(-4, 1)));
503 }
504
505 @Test
506 public void testLineTo_pointProjectedOnLine() {
507
508 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), PlaneAngleRadians.PI, TEST_PRECISION);
509
510
511 final ReverseRay halfLine = line.reverseRayTo(Vector2D.of(-3, 5));
512
513
514 Assert.assertSame(line, halfLine.getLine());
515 Assert.assertTrue(halfLine.isInfinite());
516 Assert.assertNull(halfLine.getStartPoint());
517 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 1), halfLine.getEndPoint(), TEST_EPS);
518
519 Assert.assertTrue(halfLine.contains(Vector2D.of(1, 1)));
520 Assert.assertFalse(halfLine.contains(Vector2D.of(-4, 1)));
521 }
522
523 @Test
524 public void testLineTo_double() {
525
526 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), PlaneAngleRadians.PI, TEST_PRECISION);
527
528
529 final ReverseRay halfLine = line.reverseRayTo(-1);
530
531
532 Assert.assertSame(line, halfLine.getLine());
533 Assert.assertTrue(halfLine.isInfinite());
534 Assert.assertNull(halfLine.getStartPoint());
535 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), halfLine.getEndPoint(), TEST_EPS);
536
537 Assert.assertTrue(halfLine.contains(Vector2D.of(2, 1)));
538 Assert.assertFalse(halfLine.contains(Vector2D.of(-4, 1)));
539 }
540
541 @Test
542 public void testRayFrom_pointOnLine() {
543
544 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), PlaneAngleRadians.PI, TEST_PRECISION);
545
546
547 final Ray ray = line.rayFrom(Vector2D.of(-3, 1));
548
549
550 Assert.assertSame(line, ray.getLine());
551 Assert.assertTrue(ray.isInfinite());
552 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 1), ray.getStartPoint(), TEST_EPS);
553 Assert.assertNull(ray.getEndPoint());
554
555 Assert.assertFalse(ray.contains(Vector2D.of(1, 1)));
556 Assert.assertTrue(ray.contains(Vector2D.of(-4, 1)));
557 }
558
559 @Test
560 public void testRayFrom_pointProjectedOnLine() {
561
562 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), PlaneAngleRadians.PI, TEST_PRECISION);
563
564
565 final Ray ray = line.rayFrom(Vector2D.of(-3, 5));
566
567
568 Assert.assertSame(line, ray.getLine());
569 Assert.assertTrue(ray.isInfinite());
570 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 1), ray.getStartPoint(), TEST_EPS);
571 Assert.assertNull(ray.getEndPoint());
572
573 Assert.assertFalse(ray.contains(Vector2D.of(1, 1)));
574 Assert.assertTrue(ray.contains(Vector2D.of(-4, 1)));
575 }
576
577 @Test
578 public void testRayFrom_double() {
579
580 final Line line = Lines.fromPointAndAngle(Vector2D.of(0, 1), PlaneAngleRadians.PI, TEST_PRECISION);
581
582
583 final Ray ray = line.rayFrom(-1);
584
585
586 Assert.assertSame(line, ray.getLine());
587 Assert.assertTrue(ray.isInfinite());
588 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), ray.getStartPoint(), TEST_EPS);
589 Assert.assertNull(ray.getEndPoint());
590
591 Assert.assertFalse(ray.contains(Vector2D.of(2, 1)));
592 Assert.assertTrue(ray.contains(Vector2D.of(-4, 1)));
593 }
594
595 @Test
596 public void testOffset_parallelLines() {
597
598 final double dist = Math.sin(Math.atan2(2, 1));
599
600 final Line a = Lines.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4), TEST_PRECISION);
601 final Line b = Lines.fromPoints(Vector2D.of(-3, 0), Vector2D.of(0, 6), TEST_PRECISION);
602 final Line c = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2), TEST_PRECISION);
603 final Line d = Lines.fromPoints(Vector2D.of(1, 0), Vector2D.of(0, -2), TEST_PRECISION);
604
605
606 Assert.assertEquals(-dist, a.offset(b), TEST_EPS);
607 Assert.assertEquals(dist, b.offset(a), TEST_EPS);
608
609 Assert.assertEquals(dist, a.offset(c), TEST_EPS);
610 Assert.assertEquals(-dist, c.offset(a), TEST_EPS);
611
612 Assert.assertEquals(3 * dist, a.offset(d), TEST_EPS);
613 Assert.assertEquals(3 * dist, d.offset(a), TEST_EPS);
614 }
615
616 @Test
617 public void testOffset_coincidentLines() {
618
619 final Line a = Lines.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4), TEST_PRECISION);
620 final Line b = Lines.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4), TEST_PRECISION);
621 final Line c = b.reverse();
622
623
624 Assert.assertEquals(0, a.offset(a), TEST_EPS);
625
626 Assert.assertEquals(0, a.offset(b), TEST_EPS);
627 Assert.assertEquals(0, b.offset(a), TEST_EPS);
628
629 Assert.assertEquals(0, a.offset(c), TEST_EPS);
630 Assert.assertEquals(0, c.offset(a), TEST_EPS);
631 }
632
633 @Test
634 public void testOffset_nonParallelLines() {
635
636 final Line a = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
637 final Line b = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_Y, TEST_PRECISION);
638 final Line c = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2), TEST_PRECISION);
639 final Line d = Lines.fromPoints(Vector2D.of(1, 0), Vector2D.of(0, 4), TEST_PRECISION);
640
641
642 Assert.assertEquals(0, a.offset(b), TEST_EPS);
643 Assert.assertEquals(0, b.offset(a), TEST_EPS);
644
645 Assert.assertEquals(0, a.offset(c), TEST_EPS);
646 Assert.assertEquals(0, c.offset(a), TEST_EPS);
647
648 Assert.assertEquals(0, a.offset(d), TEST_EPS);
649 Assert.assertEquals(0, d.offset(a), TEST_EPS);
650 }
651
652 @Test
653 public void testOffset_point() {
654
655 final Line line = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2), TEST_PRECISION);
656 final Line reversed = line.reverse();
657
658
659 Assert.assertEquals(0.0, line.offset(Vector2D.of(-0.5, 1)), TEST_EPS);
660 Assert.assertEquals(0.0, line.offset(Vector2D.of(-1.5, -1)), TEST_EPS);
661 Assert.assertEquals(0.0, line.offset(Vector2D.of(0.5, 3)), TEST_EPS);
662
663 final double d = Math.sin(Math.atan2(2, 1));
664
665 Assert.assertEquals(d, line.offset(Vector2D.ZERO), TEST_EPS);
666 Assert.assertEquals(-d, line.offset(Vector2D.of(-1, 2)), TEST_EPS);
667
668 Assert.assertEquals(-d, reversed.offset(Vector2D.ZERO), TEST_EPS);
669 Assert.assertEquals(d, reversed.offset(Vector2D.of(-1, 2)), TEST_EPS);
670 }
671
672 @Test
673 public void testOffset_point_permute() {
674
675 final Line line = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2), TEST_PRECISION);
676 final Vector2D lineOrigin = line.getOrigin();
677
678 EuclideanTestUtils.permute(-5, 5, 0.5, (x, y) -> {
679 final Vector2D pt = Vector2D.of(x, y);
680
681
682 final double offset = line.offset(pt);
683
684
685 final Vector2D vec = lineOrigin.vectorTo(pt).reject(line.getDirection());
686 final double dot = vec.dot(line.getOffsetDirection());
687 final double expected = Math.signum(dot) * vec.norm();
688
689 Assert.assertEquals(expected, offset, TEST_EPS);
690 });
691 }
692
693 @Test
694 public void testSimilarOrientation() {
695
696 final Line a = Lines.fromPointAndAngle(Vector2D.ZERO, 0.0, TEST_PRECISION);
697 final Line b = Lines.fromPointAndAngle(Vector2D.of(4, 5), 0.0, TEST_PRECISION);
698 final Line c = Lines.fromPointAndAngle(Vector2D.of(-1, -3), 0.4 * PlaneAngleRadians.PI, TEST_PRECISION);
699 final Line d = Lines.fromPointAndAngle(Vector2D.of(1, 0), -0.4 * PlaneAngleRadians.PI, TEST_PRECISION);
700
701 final Line e = Lines.fromPointAndAngle(Vector2D.of(6, -3), PlaneAngleRadians.PI, TEST_PRECISION);
702 final Line f = Lines.fromPointAndAngle(Vector2D.of(8, 5), 0.8 * PlaneAngleRadians.PI, TEST_PRECISION);
703 final Line g = Lines.fromPointAndAngle(Vector2D.of(6, -3), -0.8 * PlaneAngleRadians.PI, TEST_PRECISION);
704
705
706 Assert.assertTrue(a.similarOrientation(a));
707 Assert.assertTrue(a.similarOrientation(b));
708 Assert.assertTrue(b.similarOrientation(a));
709 Assert.assertTrue(a.similarOrientation(c));
710 Assert.assertTrue(c.similarOrientation(a));
711 Assert.assertTrue(a.similarOrientation(d));
712 Assert.assertTrue(d.similarOrientation(a));
713
714 Assert.assertFalse(c.similarOrientation(d));
715 Assert.assertFalse(d.similarOrientation(c));
716
717 Assert.assertTrue(e.similarOrientation(f));
718 Assert.assertTrue(f.similarOrientation(e));
719 Assert.assertTrue(e.similarOrientation(g));
720 Assert.assertTrue(g.similarOrientation(e));
721
722 Assert.assertFalse(a.similarOrientation(e));
723 Assert.assertFalse(e.similarOrientation(a));
724 }
725
726 @Test
727 public void testSimilarOrientation_orthogonal() {
728
729 final Line a = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
730 final Line b = Lines.fromPointAndDirection(Vector2D.of(4, 5), Vector2D.Unit.PLUS_Y, TEST_PRECISION);
731 final Line c = Lines.fromPointAndDirection(Vector2D.of(-4, -5), Vector2D.Unit.MINUS_Y, TEST_PRECISION);
732
733
734 Assert.assertTrue(a.similarOrientation(b));
735 Assert.assertTrue(b.similarOrientation(a));
736 Assert.assertTrue(a.similarOrientation(c));
737 Assert.assertTrue(c.similarOrientation(a));
738 }
739
740 @Test
741 public void testDistance_parallelLines() {
742
743 final double dist = Math.sin(Math.atan2(2, 1));
744
745 final Line a = Lines.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4), TEST_PRECISION);
746 final Line b = Lines.fromPoints(Vector2D.of(-3, 0), Vector2D.of(0, 6), TEST_PRECISION);
747 final Line c = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2), TEST_PRECISION);
748 final Line d = Lines.fromPoints(Vector2D.of(1, 0), Vector2D.of(0, -2), TEST_PRECISION);
749
750
751 Assert.assertEquals(dist, a.distance(b), TEST_EPS);
752 Assert.assertEquals(dist, b.distance(a), TEST_EPS);
753
754 Assert.assertEquals(dist, a.distance(c), TEST_EPS);
755 Assert.assertEquals(dist, c.distance(a), TEST_EPS);
756
757 Assert.assertEquals(3 * dist, a.distance(d), TEST_EPS);
758 Assert.assertEquals(3 * dist, d.distance(a), TEST_EPS);
759 }
760
761 @Test
762 public void testDistance_coincidentLines() {
763
764 final Line a = Lines.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4), TEST_PRECISION);
765 final Line b = Lines.fromPoints(Vector2D.of(-2, 0), Vector2D.of(0, 4), TEST_PRECISION);
766 final Line c = b.reverse();
767
768
769 Assert.assertEquals(0, a.distance(a), TEST_EPS);
770
771 Assert.assertEquals(0, a.distance(b), TEST_EPS);
772 Assert.assertEquals(0, b.distance(a), TEST_EPS);
773
774 Assert.assertEquals(0, a.distance(c), TEST_EPS);
775 Assert.assertEquals(0, c.distance(a), TEST_EPS);
776 }
777
778 @Test
779 public void testDistance_nonParallelLines() {
780
781 final Line a = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
782 final Line b = Lines.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_Y, TEST_PRECISION);
783 final Line c = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2), TEST_PRECISION);
784 final Line d = Lines.fromPoints(Vector2D.of(1, 0), Vector2D.of(0, 4), TEST_PRECISION);
785
786
787 Assert.assertEquals(0, a.distance(b), TEST_EPS);
788 Assert.assertEquals(0, b.distance(a), TEST_EPS);
789
790 Assert.assertEquals(0, a.distance(c), TEST_EPS);
791 Assert.assertEquals(0, c.distance(a), TEST_EPS);
792
793 Assert.assertEquals(0, a.distance(d), TEST_EPS);
794 Assert.assertEquals(0, d.distance(a), TEST_EPS);
795 }
796
797 @Test
798 public void testDistance() {
799
800 final Line line = Lines.fromPoints(Vector2D.of(2, 1), Vector2D.of(-2, -2), TEST_PRECISION);
801
802
803 Assert.assertEquals(0, line.distance(line.getOrigin()), TEST_EPS);
804 Assert.assertEquals(+5.0, line.distance(Vector2D.of(5, -3)), TEST_EPS);
805 Assert.assertEquals(+5.0, line.distance(Vector2D.of(-5, 2)), TEST_EPS);
806 }
807
808 @Test
809 public void testPointAt() {
810
811 final Vector2D origin = Vector2D.of(-1, 1);
812 final double d = Math.sqrt(2);
813 final Line line = Lines.fromPointAndDirection(origin, Vector2D.of(1, 1), TEST_PRECISION);
814
815
816 EuclideanTestUtils.assertCoordinatesEqual(origin, line.pointAt(0, 0), TEST_EPS);
817 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.ZERO, line.pointAt(0, d), TEST_EPS);
818 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 2), line.pointAt(0, -d), TEST_EPS);
819
820 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-2, 0), line.pointAt(-d, 0), TEST_EPS);
821 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(0, 2), line.pointAt(d, 0), TEST_EPS);
822
823 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(1, 1), line.pointAt(d, d), TEST_EPS);
824 EuclideanTestUtils.assertCoordinatesEqual(Vector2D.of(-3, 1), line.pointAt(-d, -d), TEST_EPS);
825 }
826
827 @Test
828 public void testPointAt_abscissaOffsetRoundtrip() {
829
830 final Line line = Lines.fromPoints(Vector2D.of(2, 1), Vector2D.of(-2, -2), TEST_PRECISION);
831
832 for (double abscissa = -2.0; abscissa < 2.0; abscissa += 0.2) {
833 for (double offset = -2.0; offset < 2.0; offset += 0.2) {
834
835
836 final Vector2D point = line.pointAt(abscissa, offset);
837
838
839 Assert.assertEquals(abscissa, line.toSubspace(point).getX(), TEST_EPS);
840 Assert.assertEquals(offset, line.offset(point), TEST_EPS);
841 }
842 }
843 }
844
845 @Test
846 public void testContains_line() {
847
848 final Vector2D pt = Vector2D.of(1, 2);
849 final Vector2D dir = Vector2D.of(3, 7);
850 final Line a = Lines.fromPointAndDirection(pt, dir, TEST_PRECISION);
851 final Line b = Lines.fromPointAndDirection(Vector2D.of(0, -4), dir, TEST_PRECISION);
852 final Line c = Lines.fromPointAndDirection(Vector2D.of(-2, -2), dir.negate(), TEST_PRECISION);
853 final Line d = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
854
855 final Line e = Lines.fromPointAndDirection(pt, dir, TEST_PRECISION);
856 final Line f = Lines.fromPointAndDirection(pt, dir.negate(), TEST_PRECISION);
857
858
859 Assert.assertTrue(a.contains(a));
860
861 Assert.assertTrue(a.contains(e));
862 Assert.assertTrue(e.contains(a));
863
864 Assert.assertTrue(a.contains(f));
865 Assert.assertTrue(f.contains(a));
866
867 Assert.assertFalse(a.contains(b));
868 Assert.assertFalse(a.contains(c));
869 Assert.assertFalse(a.contains(d));
870 }
871
872 @Test
873 public void testIsParallel_closeToEpsilon() {
874
875 final double eps = 1e-3;
876 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(eps);
877
878 final Vector2D p = Vector2D.of(1, 2);
879
880 final Line line = Lines.fromPointAndAngle(p, 0.0, precision);
881
882
883 final Vector2D offset1 = Vector2D.of(0, 1e-4);
884 final Vector2D offset2 = Vector2D.of(0, 2e-3);
885
886 Assert.assertTrue(line.contains(Lines.fromPointAndAngle(p.add(offset1), 0.0, precision)));
887 Assert.assertTrue(line.contains(Lines.fromPointAndAngle(p.subtract(offset1), 0.0, precision)));
888
889 Assert.assertFalse(line.contains(Lines.fromPointAndAngle(p.add(offset2), 0.0, precision)));
890 Assert.assertFalse(line.contains(Lines.fromPointAndAngle(p.subtract(offset2), 0.0, precision)));
891
892 Assert.assertTrue(line.contains(Lines.fromPointAndAngle(p, 1e-4, precision)));
893 Assert.assertFalse(line.contains(Lines.fromPointAndAngle(p, 1e-2, precision)));
894 }
895
896 @Test
897 public void testContains_point() {
898
899 final Vector2D p1 = Vector2D.of(-1, 0);
900 final Vector2D p2 = Vector2D.of(0, 2);
901 final Line line = Lines.fromPoints(p1, p2, TEST_PRECISION);
902
903
904 Assert.assertTrue(line.contains(p1));
905 Assert.assertTrue(line.contains(p2));
906
907 Assert.assertFalse(line.contains(Vector2D.ZERO));
908 Assert.assertFalse(line.contains(Vector2D.of(100, 79)));
909
910 final Vector2D offset1 = Vector2D.of(0.1, 0);
911 final Vector2D offset2 = Vector2D.of(0, -0.1);
912 Vector2D v;
913 for (double t = -2; t <= 2; t += 0.1) {
914 v = p1.lerp(p2, t);
915
916 Assert.assertTrue(line.contains(v));
917
918 Assert.assertFalse(line.contains(v.add(offset1)));
919 Assert.assertFalse(line.contains(v.add(offset2)));
920 }
921 }
922
923 @Test
924 public void testContains_point_closeToEpsilon() {
925
926 final double eps = 1e-3;
927 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(eps);
928
929 final Vector2D p1 = Vector2D.of(-1, 0);
930 final Vector2D p2 = Vector2D.of(0, 2);
931 final Vector2D mid = p1.lerp(p2, 0.5);
932
933 final Line line = Lines.fromPoints(p1, p2, precision);
934 final Vector2D dir = line.getOffsetDirection();
935
936
937 Assert.assertTrue(line.contains(mid.add(dir.multiply(1e-4))));
938 Assert.assertTrue(line.contains(mid.add(dir.multiply(-1e-4))));
939
940 Assert.assertFalse(line.contains(mid.add(dir.multiply(2e-3))));
941 Assert.assertFalse(line.contains(mid.add(dir.multiply(-2e-3))));
942 }
943
944 @Test
945 public void testDistance_point() {
946
947 final Line line = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2), TEST_PRECISION);
948 final Line reversed = line.reverse();
949
950
951 Assert.assertEquals(0.0, line.distance(Vector2D.of(-0.5, 1)), TEST_EPS);
952 Assert.assertEquals(0.0, line.distance(Vector2D.of(-1.5, -1)), TEST_EPS);
953 Assert.assertEquals(0.0, line.distance(Vector2D.of(0.5, 3)), TEST_EPS);
954
955 final double d = Math.sin(Math.atan2(2, 1));
956
957 Assert.assertEquals(d, line.distance(Vector2D.ZERO), TEST_EPS);
958 Assert.assertEquals(d, line.distance(Vector2D.of(-1, 2)), TEST_EPS);
959
960 Assert.assertEquals(d, reversed.distance(Vector2D.ZERO), TEST_EPS);
961 Assert.assertEquals(d, reversed.distance(Vector2D.of(-1, 2)), TEST_EPS);
962 }
963
964 @Test
965 public void testDistance_point_permute() {
966
967 final Line line = Lines.fromPoints(Vector2D.of(-1, 0), Vector2D.of(0, 2), TEST_PRECISION);
968 final Vector2D lineOrigin = line.getOrigin();
969
970 EuclideanTestUtils.permute(-5, 5, 0.5, (x, y) -> {
971 final Vector2D pt = Vector2D.of(x, y);
972
973
974 final double dist = line.distance(pt);
975
976
977 final Vector2D vec = lineOrigin.vectorTo(pt).reject(line.getDirection());
978 Assert.assertEquals(vec.norm(), dist, TEST_EPS);
979 });
980 }
981
982 @Test
983 public void testIsParallel() {
984
985 final Vector2D dir = Vector2D.of(3, 7);
986 final Line a = Lines.fromPointAndDirection(Vector2D.of(1, 2), dir, TEST_PRECISION);
987 final Line b = Lines.fromPointAndDirection(Vector2D.of(0, -4), dir, TEST_PRECISION);
988 final Line c = Lines.fromPointAndDirection(Vector2D.of(-2, -2), dir.negate(), TEST_PRECISION);
989 final Line d = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
990
991
992 Assert.assertTrue(a.isParallel(a));
993
994 Assert.assertTrue(a.isParallel(b));
995 Assert.assertTrue(b.isParallel(a));
996
997 Assert.assertTrue(a.isParallel(c));
998 Assert.assertTrue(c.isParallel(a));
999
1000 Assert.assertFalse(a.isParallel(d));
1001 Assert.assertFalse(d.isParallel(a));
1002 }
1003
1004 @Test
1005 public void testIsParallel_closeToParallel() {
1006
1007 final double eps = 1e-3;
1008 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(eps);
1009
1010 final Vector2D p1 = Vector2D.of(1, 2);
1011 final Vector2D p2 = Vector2D.of(1, -2);
1012
1013 final Line line = Lines.fromPointAndAngle(p1, 0.0, precision);
1014
1015
1016 Assert.assertTrue(line.isParallel(Lines.fromPointAndAngle(p2, 1e-4, precision)));
1017 Assert.assertFalse(line.isParallel(Lines.fromPointAndAngle(p2, 1e-2, precision)));
1018 }
1019
1020 @Test
1021 public void testTransform() {
1022
1023 final AffineTransformMatrix2D scale = AffineTransformMatrix2D.createScale(2, 3);
1024 final AffineTransformMatrix2D reflect = AffineTransformMatrix2D.createScale(-1, 1);
1025 final AffineTransformMatrix2D translate = AffineTransformMatrix2D.createTranslation(3, 4);
1026 final AffineTransformMatrix2D rotate = AffineTransformMatrix2D.createRotation(PlaneAngleRadians.PI_OVER_TWO);
1027 final AffineTransformMatrix2D rotateAroundPt = AffineTransformMatrix2D.createRotation(Vector2D.of(0, 1), PlaneAngleRadians.PI_OVER_TWO);
1028
1029 final Vector2D p1 = Vector2D.of(0, 1);
1030 final Vector2D p2 = Vector2D.of(1, 0);
1031
1032 final Line horizontal = Lines.fromPointAndDirection(p1, Vector2D.Unit.PLUS_X, TEST_PRECISION);
1033 final Line vertical = Lines.fromPointAndDirection(p2, Vector2D.Unit.PLUS_Y, TEST_PRECISION);
1034 final Line diagonal = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.of(1, 1), TEST_PRECISION);
1035
1036
1037 Assert.assertSame(TEST_PRECISION, horizontal.transform(scale).getPrecision());
1038
1039 checkLine(horizontal.transform(scale), Vector2D.of(0, 3), Vector2D.Unit.PLUS_X);
1040 checkLine(vertical.transform(scale), Vector2D.of(2, 0), Vector2D.Unit.PLUS_Y);
1041 checkLine(diagonal.transform(scale), Vector2D.ZERO, Vector2D.of(2, 3).normalize());
1042
1043 checkLine(horizontal.transform(reflect), p1, Vector2D.Unit.MINUS_X);
1044 checkLine(vertical.transform(reflect), Vector2D.of(-1, 0), Vector2D.Unit.PLUS_Y);
1045 checkLine(diagonal.transform(reflect), Vector2D.ZERO, Vector2D.of(-1, 1).normalize());
1046
1047 checkLine(horizontal.transform(translate), Vector2D.of(0, 5), Vector2D.Unit.PLUS_X);
1048 checkLine(vertical.transform(translate), Vector2D.of(4, 0), Vector2D.Unit.PLUS_Y);
1049 checkLine(diagonal.transform(translate), Vector2D.of(-0.5, 0.5), Vector2D.of(1, 1).normalize());
1050
1051 checkLine(horizontal.transform(rotate), Vector2D.of(-1, 0), Vector2D.Unit.PLUS_Y);
1052 checkLine(vertical.transform(rotate), Vector2D.of(0, 1), Vector2D.Unit.MINUS_X);
1053 checkLine(diagonal.transform(rotate), Vector2D.ZERO, Vector2D.of(-1, 1).normalize());
1054
1055 checkLine(horizontal.transform(rotateAroundPt), Vector2D.ZERO, Vector2D.Unit.PLUS_Y);
1056 checkLine(vertical.transform(rotateAroundPt), Vector2D.of(0, 2), Vector2D.Unit.MINUS_X);
1057 checkLine(diagonal.transform(rotateAroundPt), Vector2D.of(1, 1), Vector2D.of(-1, 1).normalize());
1058 }
1059
1060 @Test
1061 public void testTransform_collapsedPoints() {
1062
1063 final AffineTransformMatrix2D scaleCollapse = AffineTransformMatrix2D.createScale(0, 1);
1064 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
1065
1066
1067 GeometryTestUtils.assertThrows(() -> {
1068 line.transform(scaleCollapse);
1069 }, IllegalArgumentException.class, "Line direction cannot be zero");
1070 }
1071
1072 @Test
1073 public void testSubspaceTransform() {
1074
1075 final Line line = Lines.fromPoints(Vector2D.of(1, 0), Vector2D.of(1, 1), TEST_PRECISION);
1076
1077
1078 checkSubspaceTransform(line.subspaceTransform(AffineTransformMatrix2D.createScale(2, 3)),
1079 Vector2D.of(2, 0), Vector2D.Unit.PLUS_Y,
1080 Vector2D.of(2, 0), Vector2D.of(2, 3));
1081
1082 checkSubspaceTransform(line.subspaceTransform(AffineTransformMatrix2D.createTranslation(2, 3)),
1083 Vector2D.of(3, 0), Vector2D.Unit.PLUS_Y,
1084 Vector2D.of(3, 3), Vector2D.of(3, 4));
1085
1086 checkSubspaceTransform(line.subspaceTransform(AffineTransformMatrix2D.createRotation(PlaneAngleRadians.PI_OVER_TWO)),
1087 Vector2D.of(0, 1), Vector2D.Unit.MINUS_X,
1088 Vector2D.of(0, 1), Vector2D.of(-1, 1));
1089 }
1090
1091 private void checkSubspaceTransform(final SubspaceTransform st, final Vector2D origin, final Vector2D dir, final Vector2D tZero, final Vector2D tOne) {
1092
1093 final Line line = st.getLine();
1094 final AffineTransformMatrix1D transform = st.getTransform();
1095
1096 checkLine(line, origin, dir);
1097
1098 EuclideanTestUtils.assertCoordinatesEqual(tZero, line.toSpace(transform.apply(Vector1D.ZERO)), TEST_EPS);
1099 EuclideanTestUtils.assertCoordinatesEqual(tOne, line.toSpace(transform.apply(Vector1D.Unit.PLUS)), TEST_EPS);
1100 }
1101
1102 @Test
1103 public void testSubspaceTransform_transformsPointsCorrectly() {
1104
1105 final Line line = Lines.fromPointAndDirection(Vector2D.of(1, 0), Vector2D.of(1, 1), TEST_PRECISION);
1106
1107 EuclideanTestUtils.permuteSkipZero(-2, 2, 0.5, (a, b) -> {
1108
1109 final AffineTransformMatrix2D transform = AffineTransformMatrix2D.createTranslation(Vector2D.of(a, b))
1110 .rotate(a * b)
1111 .scale(0.1, 4);
1112
1113
1114 final SubspaceTransform st = line.subspaceTransform(transform);
1115
1116
1117 for (double x = -5.0; x <= 5.0; x += 1) {
1118 final Vector1D subPt = Vector1D.of(x);
1119 final Vector2D expected = transform.apply(line.toSpace(subPt));
1120 final Vector2D actual = st.getLine().toSpace(
1121 st.getTransform().apply(subPt));
1122
1123 EuclideanTestUtils.assertCoordinatesEqual(expected, actual, TEST_EPS);
1124 }
1125 });
1126 }
1127
1128 @Test
1129 public void testEq() {
1130
1131 final DoublePrecisionContext precision = new EpsilonDoublePrecisionContext(1e-3);
1132
1133 final Vector2D p = Vector2D.of(1, 2);
1134 final double angle = 1.0;
1135
1136 final Line a = Lines.fromPointAndAngle(p, angle, precision);
1137 final Line b = Lines.fromPointAndAngle(Vector2D.ZERO, angle, precision);
1138 final Line c = Lines.fromPointAndAngle(p, angle + 1.0, precision);
1139
1140 final Line d = Lines.fromPointAndAngle(p, angle, precision);
1141 final Line e = Lines.fromPointAndAngle(p.add(Vector2D.of(1e-4, 1e-4)), angle, precision);
1142 final Line f = Lines.fromPointAndAngle(p, angle + 1e-4, precision);
1143
1144
1145 Assert.assertTrue(a.eq(a, precision));
1146
1147 Assert.assertTrue(a.eq(d, precision));
1148 Assert.assertTrue(d.eq(a, precision));
1149
1150 Assert.assertTrue(a.eq(e, precision));
1151 Assert.assertTrue(e.eq(a, precision));
1152
1153 Assert.assertTrue(a.eq(f, precision));
1154 Assert.assertTrue(f.eq(a, precision));
1155
1156 Assert.assertFalse(a.eq(b, precision));
1157 Assert.assertFalse(a.eq(c, precision));
1158 }
1159
1160 @Test
1161 public void testHashCode() {
1162
1163 final DoublePrecisionContext precision1 = new EpsilonDoublePrecisionContext(1e-4);
1164 final DoublePrecisionContext precision2 = new EpsilonDoublePrecisionContext(1e-5);
1165
1166 final Vector2D p = Vector2D.of(1, 2);
1167 final Vector2D v = Vector2D.of(1, 1);
1168
1169 final Line a = Lines.fromPointAndDirection(p, v, precision1);
1170 final Line b = Lines.fromPointAndDirection(Vector2D.ZERO, v, precision1);
1171 final Line c = Lines.fromPointAndDirection(p, v.negate(), precision1);
1172 final Line d = Lines.fromPointAndDirection(p, v, precision2);
1173 final Line e = Lines.fromPointAndDirection(p, v, precision1);
1174
1175
1176 final int aHash = a.hashCode();
1177
1178 Assert.assertEquals(aHash, a.hashCode());
1179 Assert.assertEquals(aHash, e.hashCode());
1180
1181 Assert.assertNotEquals(aHash, b.hashCode());
1182 Assert.assertNotEquals(aHash, c.hashCode());
1183 Assert.assertNotEquals(aHash, d.hashCode());
1184 }
1185
1186 @Test
1187 public void testEquals() {
1188
1189 final DoublePrecisionContext precision1 = new EpsilonDoublePrecisionContext(1e-4);
1190 final DoublePrecisionContext precision2 = new EpsilonDoublePrecisionContext(1e-5);
1191
1192 final Vector2D p = Vector2D.of(1, 2);
1193 final Vector2D v = Vector2D.of(1, 1);
1194
1195 final Line a = Lines.fromPointAndDirection(p, v, precision1);
1196 final Line b = Lines.fromPointAndDirection(Vector2D.ZERO, v, precision1);
1197 final Line c = Lines.fromPointAndDirection(p, v.negate(), precision1);
1198 final Line d = Lines.fromPointAndDirection(p, v, precision2);
1199 final Line e = Lines.fromPointAndDirection(p, v, precision1);
1200
1201
1202 Assert.assertEquals(a, a);
1203 Assert.assertEquals(a, e);
1204 Assert.assertEquals(e, a);
1205
1206 Assert.assertFalse(a.equals(null));
1207 Assert.assertFalse(a.equals(new Object()));
1208
1209 Assert.assertNotEquals(a, b);
1210 Assert.assertNotEquals(a, c);
1211 Assert.assertNotEquals(a, d);
1212 }
1213
1214 @Test
1215 public void testToString() {
1216
1217 final Line line = Lines.fromPointAndDirection(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION);
1218
1219
1220 final String str = line.toString();
1221
1222
1223 Assert.assertTrue(str.contains("Line"));
1224 Assert.assertTrue(str.contains("origin= (0.0, 0.0)"));
1225 Assert.assertTrue(str.contains("direction= (1.0, 0.0)"));
1226 }
1227
1228
1229
1230
1231
1232
1233
1234 private void checkLine(final Line line, final Vector2D origin, final Vector2D dir) {
1235 EuclideanTestUtils.assertCoordinatesEqual(origin, line.getOrigin(), TEST_EPS);
1236 EuclideanTestUtils.assertCoordinatesEqual(dir, line.getDirection(), TEST_EPS);
1237 }
1238 }