1 package org.djutils.draw.line;
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertFalse;
5 import static org.junit.jupiter.api.Assertions.assertNotEquals;
6 import static org.junit.jupiter.api.Assertions.assertNull;
7 import static org.junit.jupiter.api.Assertions.assertTrue;
8 import static org.junit.jupiter.api.Assertions.fail;
9
10 import java.util.Iterator;
11 import java.util.NoSuchElementException;
12
13 import org.djutils.draw.Direction3d;
14 import org.djutils.draw.bounds.Bounds3d;
15 import org.djutils.draw.point.OrientedPoint3d;
16 import org.djutils.draw.point.Point3d;
17 import org.djutils.math.AngleUtil;
18 import org.junit.jupiter.api.Test;
19
20
21
22
23
24
25
26
27
28
29
30 public class Ray3dTest
31 {
32
33
34
35 @Test
36 public void testConstructors()
37 {
38
39 verifyRay("positive x", new Ray3d(0, 0, 0, 1, 0, 0), 0, 0, 0, Math.PI / 2, 0);
40 verifyRay("positive y", new Ray3d(0, 0, 0, 0, 1, 0), 0, 0, 0, Math.PI / 2, Math.PI / 2);
41 verifyRay("positive z", new Ray3d(0, 0, 0, 0, 0, 1), 0, 0, 0, 0, 0);
42 verifyRay("negative x", new Ray3d(0, 0, 0, -1, 0, 0), 0, 0, 0, Math.PI / 2, Math.PI);
43 verifyRay("negative y", new Ray3d(0, 0, 0, 0, -1, 0), 0, 0, 0, Math.PI / 2, -Math.PI / 2);
44 verifyRay("negative z", new Ray3d(0, 0, 0, 0, 0, -1), 0, 0, 0, Math.PI, 0);
45 verifyRay("Constructor from x, y, z, dirY, dirZ", new Ray3d(1, 2, 3, 4, 5), 1, 2, 3, 4, 5);
46 verifyRay("Constructor from [x, y, z], dirY, dirZ", new Ray3d(new double[] {1, 2, 3}, 4, 5), 1, 2, 3, 4, 5);
47 Direction3d dir = new Direction3d(4, 5);
48 verifyRay("Constructor from [x, y, z], dir", new Ray3d(new double[] {1, 2, 3}, dir), 1, 2, 3, 4, 5);
49 verifyRay("Constructor from x, y, z, [dirY, dirZ]", new Ray3d(1, 2, 3, new double[] {4, 5}), 1, 2, 3, 4, 5);
50 verifyRay("Constructor from x, y, z, dir", new Ray3d(1, 2, 3, dir), 1, 2, 3, 4, 5);
51 verifyRay("Constructor from [x, y, z], [dirY, dirZ]", new Ray3d(new double[] {1, 2, 3}, new double[] {4, 5}), 1, 2, 3,
52 4, 5);
53 verifyRay("Constructor from Point3d, dirY, dirZ", new Ray3d(new Point3d(0.1, 0.2, 0.3), -0.4, -0.5), 0.1, 0.2, 0.3,
54 -0.4, -0.5);
55 dir = new Direction3d(-0.4, -0.5);
56 verifyRay("Constructor from Point3d, dir", new Ray3d(new Point3d(0.1, 0.2, 0.3), dir), 0.1, 0.2, 0.3, -0.4, -0.5);
57 verifyRay("Constructor from x, y, z, throughX, throughY, throughZ", new Ray3d(1, 2, 3, 4, 6, 15), 1, 2, 3,
58 Math.atan2(5, 12), Math.atan2(4, 3));
59 verifyRay("Constructor from x, y, z, throughX, throughY, throughZ", new Ray3d(1, 2, 3, 1, 6, 15), 1, 2, 3,
60 Math.atan2(4, 12), Math.PI / 2);
61 verifyRay("Constructor from x, y, z, throughX, throughY, throughZ", new Ray3d(1, 2, 3, 1, 2, 15), 1, 2, 3, 0,
62 Math.atan2(0, 0));
63 verifyRay("Constructor from Point3d, throughX, throughY, throughZ", new Ray3d(new Point3d(1, 2, 3), 4, 6, 15), 1, 2, 3,
64 Math.atan2(5, 12), Math.atan2(4, 3));
65 verifyRay("Constructor from Point3d, throughX, throughY, throughZ", new Ray3d(new Point3d(1, 2, 3), 1, 6, 15), 1, 2, 3,
66 Math.atan2(4, 12), Math.PI / 2);
67 verifyRay("Constructor from Point3d, throughX, throughY, throughZ", new Ray3d(new Point3d(1, 2, 3), 1, 2, 15), 1, 2, 3,
68 Math.atan2(0, 0), Math.atan2(0, 12));
69 verifyRay("Constructor from x, y, z, Point3d", new Ray3d(1, 2, 3, new Point3d(4, 6, 15)), 1, 2, 3, Math.atan2(5, 12),
70 Math.atan2(4, 3));
71 verifyRay("Constructor from x, y, z, Point3d", new Ray3d(1, 2, 3, new Point3d(1, 6, 15)), 1, 2, 3, Math.atan2(4, 12),
72 Math.PI / 2);
73 verifyRay("Constructor from x, y, z, Point3d", new Ray3d(1, 2, 3, new Point3d(1, 2, 15)), 1, 2, 3, Math.atan2(0, 12),
74 Math.atan2(0, 0));
75 verifyRay("Constructor from Point3d, Point3d", new Ray3d(new Point3d(1, 2, 3), new Point3d(4, 6, 15)), 1, 2, 3,
76 Math.atan2(5, 12), Math.atan2(4, 3));
77 verifyRay("Constructor from Point3d, Point3d", new Ray3d(new Point3d(1, 2, 3), new Point3d(1, 6, 15)), 1, 2, 3,
78 Math.atan2(4, 12), Math.PI / 2);
79 verifyRay("Constructor from Point3d, Point3d", new Ray3d(new Point3d(1, 2, 3), new Point3d(1, 2, 15)), 1, 2, 3,
80 Math.atan2(0, 0), Math.atan2(0, 12));
81
82 try
83 {
84 new Ray3d(1, 2, 3, Double.NaN, 0);
85 fail("NaN for dirY should have thrown an ArithmeticException");
86 }
87 catch (ArithmeticException e)
88 {
89
90 }
91
92 try
93 {
94 new Ray3d(1, 2, 3, 0, Double.NaN);
95 fail("NaN for dirZ should have thrown a ArithmeticException");
96 }
97 catch (ArithmeticException e)
98 {
99
100 }
101
102 try
103 {
104 new Ray3d((Point3d) null, 1, 2);
105 fail("null for point should have thrown a NullPointerException");
106 }
107 catch (NullPointerException e)
108 {
109
110 }
111
112 try
113 {
114 new Ray3d(1, 2, 3, 1, 2, 3);
115 fail("Same coordinates for through point should have thrown a DrawRuntimeException");
116 }
117 catch (IllegalArgumentException e)
118 {
119
120 }
121
122 try
123 {
124 new Ray3d(1, 2, 3, new Point3d(1, 2, 3));
125 fail("Same coordinates for through point should have thrown a DrawRuntimeException");
126 }
127 catch (IllegalArgumentException e)
128 {
129
130 }
131
132 try
133 {
134 new Ray3d(new Point3d(1, 2, 3), 1, 2, 3);
135 fail("Same coordinates for through point should have thrown a DrawRuntimeException");
136 }
137 catch (IllegalArgumentException e)
138 {
139
140 }
141
142 try
143 {
144 new Ray3d(1, 2, 3, (Point3d) null);
145 fail("null for through point should have thrown a NullPointerException");
146 }
147 catch (NullPointerException e)
148 {
149
150 }
151
152 try
153 {
154 new Ray3d(null, new Point3d(4, 5, 6));
155 fail("null for point should have thrown a NullPointerException");
156 }
157 catch (NullPointerException e)
158 {
159
160 }
161
162 try
163 {
164 new Ray3d(new Point3d(1, 2, 3), (Point3d) null);
165 fail("null for through point should have thrown a NullPointerException");
166 }
167 catch (NullPointerException e)
168 {
169
170 }
171
172 try
173 {
174 new Ray3d(1, 2, 3, new double[] {0.5});
175 fail("too short directionVector should have thrown an IllegalArgumentException");
176 }
177 catch (IllegalArgumentException e)
178 {
179
180 }
181
182 Ray3d ray = new Ray3d(1, 2, 3, 0.2, 0.3);
183 assertTrue(ray.toString().startsWith("Ray3d"), "toString returns something descriptive");
184 assertTrue(ray.toString().indexOf(ray.toString(true)) > 0, "toString can suppress the class name");
185 }
186
187
188
189
190
191
192
193
194
195
196
197 private void verifyRay(final String description, final Ray3d ray, final double expectedX, final double expectedY,
198 final double expectedZ, final double expectedDirY, final double expectedDirZ)
199 {
200 assertEquals(expectedX, ray.getX(), 0.0001, description + " getX");
201 assertEquals(expectedX, ray.x, 0.0001, description + " x");
202 assertEquals(expectedY, ray.getY(), 0.0001, description + " getY");
203 assertEquals(expectedY, ray.y, 0.0001, description + " y");
204 assertEquals(expectedZ, ray.getZ(), 0.0001, description + " getZ");
205 assertEquals(expectedZ, ray.z, 0.0001, description + " z");
206 assertEquals(expectedDirZ, ray.getDirZ(), 0.0001, description + " getDirZ");
207 assertEquals(expectedDirZ, ray.dirZ, 0.0001, description + " dirZ");
208 assertEquals(expectedDirY, ray.getDirY(), 0.0001, description + " getDirY");
209 assertEquals(expectedDirY, ray.dirY, 0.0001, description + " dirY");
210 Point3d startPoint = ray.getEndPoint();
211 assertEquals(expectedX, startPoint.x, 0.0001, description + " getStartPoint x");
212 assertEquals(expectedY, startPoint.y, 0.0001, description + " getStartPoint y");
213 assertEquals(expectedZ, startPoint.z, 0.0001, description + " getStartPoint z");
214 Ray3d negated = ray.neg();
215 assertEquals(-expectedX, negated.x, 0.0001, description + " neg x");
216 assertEquals(-expectedY, negated.y, 0.0001, description + " neg y");
217 assertEquals(-expectedZ, negated.z, 0.0001, description + " neg z");
218 assertEquals(AngleUtil.normalizeAroundZero(expectedDirZ + Math.PI), negated.dirZ, 0.0001, description + " neg dirZ");
219 assertEquals(AngleUtil.normalizeAroundZero(expectedDirY + Math.PI), negated.dirY, 0.0001, description + " neg dirY");
220 Ray3d flipped = ray.flip();
221 assertEquals(expectedX, flipped.getX(), 0.0001, description + " getX");
222 assertEquals(expectedX, flipped.x, 0.0001, description + " x");
223 assertEquals(expectedY, flipped.getY(), 0.0001, description + " getY");
224 assertEquals(expectedY, flipped.y, 0.0001, description + " y");
225 assertEquals(expectedZ, flipped.getZ(), 0.0001, description + " getZ");
226 assertEquals(expectedZ, flipped.z, 0.0001, description + " z");
227 assertEquals(AngleUtil.normalizeAroundZero(expectedDirZ + Math.PI), flipped.getDirZ(), 0.0001,
228 description + " getdirZ");
229 assertEquals(AngleUtil.normalizeAroundZero(expectedDirZ + Math.PI), flipped.dirZ, 0.0001, description + " dirZ");
230 assertEquals(AngleUtil.normalizeAroundZero(Math.PI - expectedDirY), flipped.getDirY(), 0.0001,
231 description + " getDirY");
232 assertEquals(AngleUtil.normalizeAroundZero(Math.PI - expectedDirY), flipped.dirY, 0.0001, description + " dirY");
233 assertEquals(2, ray.size(), description + " size");
234 Iterator<Point3d> iterator = ray.iterator();
235
236 assertTrue(iterator.hasNext());
237 Point3d point = iterator.next();
238 assertEquals(expectedX, point.x, 0.0001, description + " iterator first point x");
239 assertEquals(expectedY, point.y, 0.0001, description + " iterator first point y");
240 assertEquals(expectedZ, point.z, 0.0001, description + " iterator first point z");
241 assertTrue(iterator.hasNext());
242 point = iterator.next();
243
244 assertTrue(Double.isInfinite(point.x) || Double.isInfinite(point.y) || Double.isInfinite(point.z),
245 description + " iterator second point is at infinity");
246 assertFalse(iterator.hasNext());
247 try
248 {
249 iterator.next();
250 fail("Should have thrown a NoSuchElementException");
251 }
252 catch (NoSuchElementException nsee)
253 {
254
255 }
256 }
257
258
259
260
261 @Test
262 public void boundsTest()
263 {
264
265
266 verifyBounds(new Ray3d(1, 2, 3, 0, 1).getBounds(), 1, 2, 3, 1, 2, Double.POSITIVE_INFINITY);
267
268
269
270 verifyBounds(new Ray3d(1, 2, 3, 0, 0).getBounds(), 1, 2, 3, 1, 2, Double.POSITIVE_INFINITY);
271
272
273 verifyBounds(new Ray3d(1, 2, 3, 1.1, 0.2).getBounds(), 1, 2, 3, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY,
274 Double.POSITIVE_INFINITY);
275
276
277 verifyBounds(new Ray3d(1, 2, 3, 1, Math.PI / 2).getBounds(), 1, 2, 3, Double.POSITIVE_INFINITY,
278 Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
279
280
281 verifyBounds(new Ray3d(1, 2, 3, 1, 2).getBounds(), Double.NEGATIVE_INFINITY, 2, 3, 1, Double.POSITIVE_INFINITY,
282 Double.POSITIVE_INFINITY);
283
284
285 verifyBounds(new Ray3d(1, 2, 3, 1, Math.PI).getBounds(), Double.NEGATIVE_INFINITY, 2, 3, 1, Double.POSITIVE_INFINITY,
286 Double.POSITIVE_INFINITY);
287
288
289 verifyBounds(new Ray3d(1, 2, 3, 1, 4).getBounds(), Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 3, 1, 2,
290 Double.POSITIVE_INFINITY);
291
292
293 verifyBounds(new Ray3d(1, 2, 3, 1, -1).getBounds(), 1, Double.NEGATIVE_INFINITY, 3, Double.POSITIVE_INFINITY, 2,
294 Double.POSITIVE_INFINITY);
295
296
297 verifyBounds(new Ray3d(1, 2, 3, 1, -Math.PI / 2).getBounds(), 1, Double.NEGATIVE_INFINITY, 3, Double.POSITIVE_INFINITY,
298 2, Double.POSITIVE_INFINITY);
299
300
301 verifyBounds(new Ray3d(1, 2, 3, 3, 0.2).getBounds(), 1, 2, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
302 Double.POSITIVE_INFINITY, 3);
303
304
305 verifyBounds(new Ray3d(1, 2, 3, 3, 2).getBounds(), Double.NEGATIVE_INFINITY, 2, Double.NEGATIVE_INFINITY, 1,
306 Double.POSITIVE_INFINITY, 3);
307
308
309 verifyBounds(new Ray3d(1, 2, 3, 3, Math.PI).getBounds(), Double.NEGATIVE_INFINITY, 2, Double.NEGATIVE_INFINITY, 1,
310 Double.POSITIVE_INFINITY, 3);
311
312
313 verifyBounds(new Ray3d(1, 2, 3, 3, 4).getBounds(), Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY,
314 Double.NEGATIVE_INFINITY, 1, 2, 3);
315
316
317 verifyBounds(new Ray3d(1, 2, 3, 3, -1).getBounds(), 1, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY,
318 Double.POSITIVE_INFINITY, 2, 3);
319
320
321 verifyBounds(new Ray3d(1, 2, 3, 3, -Math.PI / 2).getBounds(), 1, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY,
322 Double.POSITIVE_INFINITY, 2, 3);
323
324
325 verifyBounds(new Ray3d(1, 2, 3, -1.1, 0.2).getBounds(), Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 3, 1, 2,
326 Double.POSITIVE_INFINITY);
327
328
329 verifyBounds(new Ray3d(1, 2, 3, -1, Math.PI / 2).getBounds(), Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 3, 1,
330 2, Double.POSITIVE_INFINITY);
331
332
333 verifyBounds(new Ray3d(1, 2, 3, -1, 2).getBounds(), 1, Double.NEGATIVE_INFINITY, 3, Double.POSITIVE_INFINITY, 2,
334 Double.POSITIVE_INFINITY);
335
336
337 verifyBounds(new Ray3d(1, 2, 3, -1, Math.PI).getBounds(), 1, Double.NEGATIVE_INFINITY, 3, Double.POSITIVE_INFINITY, 2,
338 Double.POSITIVE_INFINITY);
339
340
341 verifyBounds(new Ray3d(1, 2, 3, -1, 4).getBounds(), 1, 2, 3, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY,
342 Double.POSITIVE_INFINITY);
343
344
345 verifyBounds(new Ray3d(1, 2, 3, -1, -1).getBounds(), Double.NEGATIVE_INFINITY, 2, 3, 1, Double.POSITIVE_INFINITY,
346 Double.POSITIVE_INFINITY);
347
348
349 }
350
351
352
353
354
355
356
357
358
359
360
361 private void verifyBounds(final Bounds3d bounds, final double expectedMinX, final double expectedMinY,
362 final double expectedMinZ, final double expectedMaxX, final double expectedMaxY, final double expectedMaxZ)
363 {
364 verifyBound(expectedMinX, bounds.getMinX(), "Bounds minX");
365 verifyBound(expectedMinY, bounds.getMinY(), "Bounds minY");
366 verifyBound(expectedMinZ, bounds.getMinZ(), "Bounds minZ");
367 verifyBound(expectedMaxX, bounds.getMaxX(), "Bounds maxX");
368 verifyBound(expectedMaxY, bounds.getMaxY(), "Bounds maxY");
369 verifyBound(expectedMaxZ, bounds.getMaxZ(), "Bounds maxZ");
370 }
371
372
373
374
375
376
377
378 private void verifyBound(final double expected, final double got, final String name)
379 {
380 if (expected == Double.POSITIVE_INFINITY)
381 {
382 assertTrue(got == Double.POSITIVE_INFINITY, name);
383 }
384 else if (expected == Double.NEGATIVE_INFINITY)
385 {
386 assertTrue(got == Double.NEGATIVE_INFINITY, name);
387 }
388 else
389 {
390 assertEquals(expected, got, 0.0001, name);
391 }
392 }
393
394
395
396
397 @Test
398 public void testLocation()
399 {
400 try
401 {
402 new Ray3d(1, 2, 3, 1, 0.5).getLocation(Double.NaN);
403 fail("NaN position should have thrown an ArithmeticException");
404 }
405 catch (ArithmeticException e)
406 {
407
408 }
409
410 try
411 {
412 new Ray3d(1, 2, 3, 1, 0.5).getLocation(-1);
413 fail("Negative position should have thrown an IllegalArgumentException");
414 }
415 catch (IllegalArgumentException e)
416 {
417
418 }
419
420 try
421 {
422 new Ray3d(1, 2, 3, 1, 0.5).getLocation(Double.POSITIVE_INFINITY);
423 fail("Infite position should have thrown an IllegalArgumentException");
424 }
425 catch (IllegalArgumentException e)
426 {
427
428 }
429
430 try
431 {
432 new Ray3d(1, 2, 3, 1, 0.5).getLocation(Double.NEGATIVE_INFINITY);
433 fail("Infinte position should have thrown an IllegalArgumentException");
434 }
435 catch (IllegalArgumentException e)
436 {
437
438 }
439
440 try
441 {
442 new Ray3d(1, 2, 3, 1, 0.5).getLocationExtended(Double.POSITIVE_INFINITY);
443 fail("Infinite position should have thrown a IllegalArgumentException");
444 }
445 catch (IllegalArgumentException e)
446 {
447
448 }
449
450 try
451 {
452 new Ray3d(1, 2, 3, 1, 0.5).getLocationExtended(Double.NEGATIVE_INFINITY);
453 fail("Infinite position should have thrown a IllegalArgumentException");
454 }
455 catch (IllegalArgumentException e)
456 {
457
458 }
459
460 try
461 {
462 new Ray3d(1, 2, 3, 1, 0.5).getLocationExtended(Double.NaN);
463 fail("NaN position should have thrown an ArithmeticException");
464 }
465 catch (ArithmeticException e)
466 {
467
468 }
469
470 for (double dirZ : new double[] {0, 1, 2, 3, 4, 5, -1, -2, Math.PI})
471 {
472 for (double dirY : new double[] {0, 1, 2, 3, 4, 5, -1, -2, Math.PI})
473 {
474 Ray3d ray = new Ray3d(1, 2, 3, dirZ, dirY);
475 for (double position : new double[] {0, 10, 0.1, -2})
476 {
477 Ray3d result = ray.getLocationExtended(position);
478 assertEquals(Math.abs(position), ray.distance(result), 0.001,
479 "result is position distance away from base of ray");
480 assertEquals(ray.dirZ, result.dirZ, 0.00001, "result has same dirZ as ray");
481 assertTrue(ray.epsilonEquals(result.getLocationExtended(-position), 0.0001),
482 "Reverse position on result yields ray");
483 if (position > 0)
484 {
485
486 assertEquals(ray.dirY, result.dirY, 0.0001, "result lies on ray (dirY)");
487 assertEquals(ray.dirZ, result.dirZ, 0.0001, "result lies in on ray (dirZ)");
488 }
489 if (position < 0)
490 {
491 assertEquals(result.dirY, ray.dirY, 0.0001, "ray lies on result (dirY)");
492 assertEquals(result.dirZ, ray.dirZ, 0.0001, "ray lies on result (dirZ)");
493 }
494 }
495 }
496 }
497 }
498
499
500
501
502 @Test
503 public void testClosestPointAndProjectOrthogonal()
504 {
505 Ray3d ray = new Ray3d(1, 2, 3, 0.4, 0.5);
506 try
507 {
508 ray.closestPointOnRay(null);
509 fail("Null for point should have thrown a NullPointerException");
510 }
511 catch (NullPointerException npe)
512 {
513
514 }
515
516 Point3d result = ray.closestPointOnRay(new Point3d(1, 2, 0));
517 assertEquals(ray.x, result.x, 0, "result is start point");
518 assertEquals(ray.y, result.y, 0, "result is start point");
519 assertEquals(ray.z, result.z, 0, "result is start point");
520 result = ray.closestPointOnRay(new Point3d(1, 2, 0));
521 assertEquals(ray.x, result.x, 0, "result is start point");
522 assertEquals(ray.y, result.y, 0, "result is start point");
523 assertEquals(ray.z, result.z, 0, "result is start point");
524 result = ray.closestPointOnRay(new Point3d(0, 2, 3));
525 assertEquals(ray.x, result.x, 0, "result is start point");
526 assertEquals(ray.y, result.y, 0, "result is start point");
527 assertEquals(ray.z, result.z, 0, "result is start point");
528 result = ray.closestPointOnRay(new Point3d(1, 2, 3));
529 assertEquals(ray.x, result.x, 0, "result is start point");
530 assertEquals(ray.y, result.y, 0, "result is start point");
531 assertEquals(ray.z, result.z, 0, "result is start point");
532
533 assertNull(ray.projectOrthogonal(new Point3d(1, 0, 3)), "projection misses the ray");
534 assertNull(ray.projectOrthogonal(new Point3d(0, 2, 3)), "projection misses the ray");
535 assertNull(ray.projectOrthogonal(new Point3d(1, 2, 2)), "projection misses the ray");
536 assertEquals(new Point3d(1, 2, 3), ray.projectOrthogonal(new Point3d(1, 2, 3)), "projection hits start point of ray");
537 assertEquals(0, new LineSegment3d(ray.getLocationExtended(-100), ray.getLocation(100))
538 .closestPointOnSegment(new Point3d(1, 0, -1)).distance(ray.projectOrthogonalExtended(new Point3d(1, 0, -1))),
539 0.0001, "extended projection returns same point as projection on sufficiently long line segment");
540
541 Point3d projectingPoint = new Point3d(10, 10, 10);
542 result = ray.closestPointOnRay(projectingPoint);
543 double distance = result.distance(ray.getEndPoint());
544 assertTrue(distance > 0, "distance from start is > 0");
545
546
547 assertTrue(ray.getLocation(distance - 0.1).distance(projectingPoint) < distance,
548 "Point on ray closer than result is further from projectingPoint");
549 assertTrue(ray.getLocation(distance + 0.1).distance(projectingPoint) < distance,
550 "Point on ray further than result is further from projectingPoint");
551 assertEquals(0, result.distance(ray.projectOrthogonalExtended(projectingPoint)), 0.0001,
552 "projectOrthogonalExtended returns same result as long as orthogonal projection exists");
553 }
554
555
556
557
558 @Test
559 public void testProject()
560 {
561 Ray3d ray = new Ray3d(1, 2, 3, 20, 10, 5);
562 assertTrue(Double.isNaN(ray.projectOrthogonalFractional(new Point3d(1, 1, 1))), "projects outside");
563 assertTrue(ray.projectOrthogonalFractionalExtended(new Point3d(1, 1, 1)) < 0, "projects before start");
564 assertEquals(-new Point3d(1 - 19 - 19, 2 - 8 - 8, 3 - 2 - 2).distance(ray),
565 ray.projectOrthogonalFractionalExtended(new Point3d(1 - 19 - 19 + 8, 2 - 8 - 8 - 19, 3 - 2 - 2)), 0.0001,
566 "projects at");
567
568 for (int x = -2; x < 5; x++)
569 {
570 for (int y = -2; y < 5; y++)
571 {
572 for (int z = -2; z < 5; z++)
573 {
574 Point3d point = new Point3d(x, y, z);
575 double fraction = ray.projectOrthogonalFractionalExtended(point);
576 if (fraction < 0)
577 {
578 assertTrue(Double.isNaN(ray.projectOrthogonalFractional(point)), "non extended version yields NaN");
579 assertNull(ray.projectOrthogonal(point), "non extended projectOrthogonal yields null");
580 }
581 else
582 {
583 assertEquals(fraction, ray.projectOrthogonalFractional(point), 0.00001,
584 "non extended version yields same");
585 assertEquals(ray.projectOrthogonal(point), ray.projectOrthogonalExtended(point),
586 "non extended version yields same as extended version");
587 }
588 Point3d projected = ray.projectOrthogonalExtended(point);
589 assertEquals(fraction, ray.projectOrthogonalFractionalExtended(projected), 0.00001,
590 "projecting projected point yields same");
591 }
592 }
593 }
594 }
595
596
597
598
599 @Test
600 public void epsilonEqualsTest()
601 {
602 Ray3d ray = new Ray3d(1, 2, 3, 0.5, -0.5);
603 try
604 {
605 ray.epsilonEquals((Ray3d) null, 1, 1);
606 fail("Null pointer should have thrown a NullPointerException");
607 }
608 catch (NullPointerException npe)
609 {
610
611 }
612
613 try
614 {
615 ray.epsilonEquals(ray, -0.1, 1);
616 fail("Negative epsilonCoordinate should have thrown an IllegalArgumentException");
617 }
618 catch (IllegalArgumentException npe)
619 {
620
621 }
622
623 try
624 {
625 ray.epsilonEquals(ray, 1, -0.1);
626 fail("Negative epsilonDirection should have thrown an IllegalArgumentException");
627 }
628 catch (IllegalArgumentException npe)
629 {
630
631 }
632
633 try
634 {
635 ray.epsilonEquals(ray, Double.NaN, 1);
636 fail("NaN epsilonCoordinate should have thrown an ArithmeticException");
637 }
638 catch (ArithmeticException ae)
639 {
640
641 }
642
643 try
644 {
645 ray.epsilonEquals(ray, 1, Double.NaN);
646 fail("NaN epsilonDirection should have thrown an ArithmeticException");
647 }
648 catch (ArithmeticException ae)
649 {
650
651 }
652
653 double[] deltas = new double[] {0.0, -0.125, 0.125, -1, 1};
654 for (double dX : deltas)
655 {
656 for (double dY : deltas)
657 {
658 for (double dZ : deltas)
659 {
660 for (double dDirZ : deltas)
661 {
662 for (double dDirY : deltas)
663 {
664 for (double epsilon : new double[] {0, 0.125, 0.5, 0.9, 1.0, 1.1})
665 {
666 Ray3d other = new Ray3d(ray.x + dX, ray.y + dY, ray.z + dZ, ray.dirY + dDirY, ray.dirZ + dDirZ);
667 boolean result = ray.epsilonEquals(other, epsilon, Double.POSITIVE_INFINITY);
668 boolean expected =
669 Math.abs(dX) <= epsilon && Math.abs(dY) <= epsilon && Math.abs(dZ) <= epsilon;
670 assertEquals(expected, result, "result of epsilonEquals checking x, y, z");
671
672 result = ray.epsilonEquals(other, Double.POSITIVE_INFINITY, epsilon);
673 expected = Math.abs(dDirZ) <= epsilon && Math.abs(dDirY) <= epsilon;
674 assertEquals(expected, result, "result of epsilonEquals checking dirY and dirZ");
675
676 other = new Ray3d(ray.x + dX, ray.y + dY, ray.z + dZ, Math.PI - ray.dirY + dDirY,
677 Math.PI + ray.dirZ + dDirZ);
678 result = ray.epsilonEquals(other, epsilon, Double.POSITIVE_INFINITY);
679 expected = Math.abs(dX) <= epsilon && Math.abs(dY) <= epsilon && Math.abs(dZ) <= epsilon;
680 assertEquals(expected, result, "result of epsilonEquals checking x, y, z");
681
682 result = ray.epsilonEquals(other, Double.POSITIVE_INFINITY, epsilon);
683 expected = Math.abs(dDirZ) <= Double.POSITIVE_INFINITY && Math.abs(Math.PI + dDirY) <= epsilon;
684 if (result != expected)
685 {
686 System.out.println("Oops: compare " + ray + " to " + other + " using epsilonEquals("
687 + Double.POSITIVE_INFINITY + "," + epsilon + ")");
688 ray.epsilonEquals(other, Double.POSITIVE_INFINITY, epsilon);
689 }
690 assertEquals(expected, result, "result of epsilonEquals checking dirY and dirZ");
691 }
692 }
693 }
694 }
695 }
696 }
697 }
698
699
700
701
702 @Test
703 public void equalsAndHashCodeTest()
704 {
705 Ray3d ray = new Ray3d(1, 2, 3, 11, 12, 13);
706 assertEquals(ray, ray, "equal to itself");
707 assertNotEquals(ray, null, "not equal to null");
708 assertNotEquals(ray, new OrientedPoint3d(1, 2, 3), "not equal to different object with same parent class");
709 assertNotEquals(ray, new Ray3d(1, 2, 3, 11, 12, 10), "not equal to ray with different dirY");
710 assertNotEquals(ray, new Ray3d(1, 2, 3, 11, 10, 13), "not equal to ray with different dirZ");
711 assertNotEquals(ray, new Ray3d(2, 2, 3, 12, 12, 13), "not equal to ray with different start x");
712 assertNotEquals(ray, new Ray3d(1, 3, 3, 11, 13, 13), "not equal to ray with different start y");
713 assertEquals(ray, new Ray3d(1, 2, 3, 21, 22, 23), "equal to ray with same x, y and direction");
714 assertNotEquals(ray, "not a ray", "not equal to an different kind of object");
715
716 assertNotEquals(ray.hashCode(), new Ray3d(2, 2, 3, 12, 12, 13), "hashCode depends on x");
717 assertNotEquals(ray.hashCode(), new Ray3d(1, 3, 3, 11, 13, 13), "hashCode depends on y");
718 assertNotEquals(ray.hashCode(), new Ray3d(1, 2, 4, 11, 12, 14), "hashCode depends on y");
719 assertNotEquals(ray.hashCode(), new Ray3d(1, 2, 3, 11, 12, 10), "hashCode depends on dirY");
720 assertNotEquals(ray.hashCode(), new Ray3d(1, 2, 3, 11, 10, 13), "hashCode depends on dirZ");
721 }
722
723 }