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