1 package org.djutils.draw.line;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertFalse;
5 import static org.junit.Assert.assertNotEquals;
6 import static org.junit.Assert.assertNotNull;
7 import static org.junit.Assert.assertNull;
8 import static org.junit.Assert.assertTrue;
9 import static org.junit.Assert.fail;
10
11 import java.awt.geom.Path2D;
12 import java.awt.geom.PathIterator;
13 import java.lang.reflect.InvocationTargetException;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.NoSuchElementException;
19
20 import org.djutils.draw.DrawRuntimeException;
21 import org.djutils.draw.bounds.Bounds3d;
22 import org.djutils.draw.line.PolyLine.TransitionFunction;
23 import org.djutils.draw.point.Point2d;
24 import org.djutils.draw.point.Point3d;
25 import org.junit.Test;
26
27
28
29
30
31
32
33
34
35
36 public class PolyLine3dTest
37 {
38
39
40
41
42 @Test
43 public final void constructorsTest() throws DrawRuntimeException
44 {
45 double[] values = { -999, 0, 99, 9999 };
46 Point3d[] points = new Point3d[0];
47 try
48 {
49 runConstructors(points);
50 fail("Should have thrown a DrawRuntimeException");
51 }
52 catch (DrawRuntimeException exception)
53 {
54
55 }
56 for (double x0 : values)
57 {
58 for (double y0 : values)
59 {
60 for (double z0 : values)
61 {
62 points = new Point3d[1];
63 points[0] = new Point3d(x0, y0, z0);
64 try
65 {
66 runConstructors(points);
67 fail("Should have thrown a DrawRuntimeException");
68 }
69 catch (DrawRuntimeException exception)
70 {
71
72 }
73 for (double x1 : values)
74 {
75 for (double y1 : values)
76 {
77 for (double z1 : values)
78 {
79 points = new Point3d[2];
80 points[0] = new Point3d(x0, y0, z0);
81 points[1] = new Point3d(x1, y1, z1);
82 if (0 == points[0].distance(points[1]))
83 {
84 try
85 {
86 runConstructors(points);
87 fail("Should have thrown a DrawRuntimeException");
88 }
89 catch (DrawRuntimeException exception)
90 {
91
92 }
93 }
94 else
95 {
96 runConstructors(points);
97 for (double x2 : values)
98 {
99 for (double y2 : values)
100 {
101 for (double z2 : values)
102 {
103 points = new Point3d[3];
104 points[0] = new Point3d(x0, y0, z0);
105 points[1] = new Point3d(x1, y1, z1);
106 points[2] = new Point3d(x2, y2, z2);
107 if (0 == points[1].distance(points[2]))
108 {
109 try
110 {
111 runConstructors(points);
112 fail("Should have thrown a DrawRuntimeException");
113 }
114 catch (DrawRuntimeException exception)
115 {
116
117 }
118 }
119 else
120 {
121 runConstructors(points);
122 }
123 }
124 }
125 }
126 }
127 }
128 }
129 }
130 }
131 }
132 }
133 }
134
135
136
137
138
139
140 private void runConstructors(final Point3d[] points) throws DrawRuntimeException
141 {
142 verifyPoints(new PolyLine3d(points), points);
143 List<Point3d> list = new ArrayList<>();
144 for (int i = 0; i < points.length; i++)
145 {
146 list.add(points[i]);
147 }
148 PolyLine3d line = new PolyLine3d(list);
149 verifyPoints(line, points);
150
151 verifyPoints(new PolyLine3d(line.getPoints()), points);
152 assertEquals("length at index 0", 0.0, line.lengthAtIndex(0), 0);
153 double length = 0;
154 for (int i = 1; i < points.length; i++)
155 {
156 length += Math.sqrt(Math.pow(points[i].x - points[i - 1].x, 2) + Math.pow(points[i].y - points[i - 1].y, 2)
157 + Math.pow(points[i].z - points[i - 1].z, 2));
158 assertEquals("length at index", length, line.lengthAtIndex(i), 0.0001);
159 }
160 assertEquals("length", length, line.getLength(), 10 * Math.ulp(length));
161
162 assertEquals("size", points.length, line.size());
163
164 Bounds3d b3d = line.getBounds();
165 Bounds3d ref = new Bounds3d(points);
166 assertEquals("bounds is correct", ref, b3d);
167
168 try
169 {
170 line.get(-1);
171 fail("Negative index should have thrown an IndexOutOfBoundsException");
172 }
173 catch (IndexOutOfBoundsException ioobe)
174 {
175
176 }
177
178 try
179 {
180 line.get(line.size() + 1);
181 fail("Too large index should have thrown an IndexOutOfBoundsException");
182 }
183 catch (IndexOutOfBoundsException ioobe)
184 {
185
186 }
187
188 try
189 {
190 new PolyLine3d((List<Point3d>) null);
191 fail("null list should have thrown a NullPointerException");
192 }
193 catch (NullPointerException npe)
194 {
195
196 }
197
198
199 Path2D path = new Path2D.Double();
200 path.moveTo(points[0].x, points[0].y);
201
202 for (int i = 1; i < points.length; i++)
203 {
204
205 if (points[i].x != points[i - 1].x || points[i].y != points[i - 1].y)
206 {
207 path.lineTo(points[i].x, points[i].y);
208 }
209 }
210 }
211
212
213
214
215
216 public final void printPath2D(final Path2D path)
217 {
218 PathIterator pi = path.getPathIterator(null);
219 double[] p = new double[6];
220 while (!pi.isDone())
221 {
222 int segType = pi.currentSegment(p);
223 if (segType == PathIterator.SEG_MOVETO)
224 {
225 System.out.print(" move to " + new Point3d(p[0], p[1], 0.0));
226 }
227 if (segType == PathIterator.SEG_LINETO)
228 {
229 System.out.print(" line to " + new Point3d(p[0], p[1], 0.0));
230 }
231 else if (segType == PathIterator.SEG_CLOSE)
232 {
233 System.out.print(" close");
234 }
235 pi.next();
236 }
237 System.out.println("");
238 }
239
240
241
242
243
244
245
246 private void verifyPoints(final PolyLine3d line, final Point3d[] points) throws DrawRuntimeException
247 {
248 assertEquals("Line should have same number of points as point array", line.size(), points.length);
249 for (int i = 0; i < points.length; i++)
250 {
251 assertEquals("x of point i should match", points[i].x, line.get(i).x, Math.ulp(points[i].x));
252 assertEquals("y of point i should match", points[i].y, line.get(i).y, Math.ulp(points[i].y));
253 assertEquals("z of point i should match", points[i].z, line.get(i).z, Math.ulp(points[i].z));
254 assertEquals("x of point i should match", points[i].x, line.getX(i), Math.ulp(points[i].x));
255 assertEquals("y of point i should match", points[i].y, line.getY(i), Math.ulp(points[i].y));
256 assertEquals("z of point i should match", points[i].z, line.getZ(i), Math.ulp(points[i].z));
257 if (i < points.length - 1)
258 {
259 LineSegment3d segment = line.getSegment(i);
260 assertEquals("begin x of line segment i should match", points[i].x, segment.startX, Math.ulp(points[i].x));
261 assertEquals("begin y of line segment i should match", points[i].y, segment.startY, Math.ulp(points[i].y));
262 assertEquals("begin z of line segment i should match", points[i].z, segment.startZ, Math.ulp(points[i].z));
263 assertEquals("end x of line segment i should match", points[i + 1].x, segment.endX, Math.ulp(points[i + 1].x));
264 assertEquals("end y of line segment i should match", points[i + 1].y, segment.endY, Math.ulp(points[i + 1].y));
265 assertEquals("end z of line segment i should match", points[i + 1].z, segment.endZ, Math.ulp(points[i + 1].z));
266 }
267 else
268 {
269 try
270 {
271 line.getSegment(i);
272 fail("Too large index should have thrown a DrawRuntimeException");
273 }
274 catch (DrawRuntimeException dre)
275 {
276
277 }
278
279 try
280 {
281 line.getSegment(-1);
282 fail("Negative index should have thrown a DrawRuntimeException");
283 }
284 catch (DrawRuntimeException dre)
285 {
286
287 }
288
289 }
290 }
291 }
292
293
294
295
296
297
298 @Test
299 public void testConstructors() throws DrawRuntimeException, DrawRuntimeException
300 {
301 runConstructors(new Point3d[] { new Point3d(1.2, 3.4, 5.5), new Point3d(2.3, 4.5, 6.6), new Point3d(3.4, 5.6, 7.7) });
302
303 try
304 {
305 new PolyLine3d(new double[] { 1, 2, 3 }, new double[] { 4, 5, 6 }, new double[] { 7, 8 });
306 fail("double arrays of unequal length should have thrown a DrawRuntimeException");
307 }
308 catch (DrawRuntimeException dre)
309 {
310
311 }
312
313 try
314 {
315 new PolyLine3d(new double[] { 1, 2, 3 }, new double[] { 4, 5 }, new double[] { 7, 8, 9 });
316 fail("double arrays of unequal length should have thrown a DrawRuntimeException");
317 }
318 catch (DrawRuntimeException dre)
319 {
320
321 }
322
323 try
324 {
325 new PolyLine3d(new double[] { 1, 2 }, new double[] { 4, 5, 6 }, new double[] { 7, 8, 9 });
326 fail("double arrays of unequal length should have thrown a DrawRuntimeException");
327 }
328 catch (DrawRuntimeException dre)
329 {
330
331 }
332
333 try
334 {
335 new PolyLine3d(null, new double[] { 1, 2 }, new double[] { 3, 4 });
336 fail("null double array should have thrown a NullPointerException");
337 }
338 catch (NullPointerException npe)
339 {
340
341 }
342
343 try
344 {
345 new PolyLine3d(new double[] { 1, 2 }, null, new double[] { 5, 6 });
346 fail("null double array should have thrown a NullPointerException");
347 }
348 catch (NullPointerException npe)
349 {
350
351 }
352
353 try
354 {
355 new PolyLine3d(new double[] { 1, 2 }, new double[] { 3, 4 }, null);
356 fail("null double array should have thrown a NullPointerException");
357 }
358 catch (NullPointerException npe)
359 {
360
361 }
362
363 try
364 {
365 new PolyLine3d((List<Point3d>) null);
366 fail("null list should have thrown a nullPointerException");
367 }
368 catch (NullPointerException npe)
369 {
370
371 }
372
373 List<Point3d> shortList = new ArrayList<>();
374 try
375 {
376 new PolyLine3d(shortList);
377 fail("empty list should have thrown a DrawRuntimeException");
378 }
379 catch (DrawRuntimeException dre)
380 {
381
382 }
383
384 shortList.add(new Point3d(1, 2, 3));
385 try
386 {
387 new PolyLine3d(shortList);
388 fail("one-point list should have thrown a DrawRuntimeException");
389 }
390 catch (DrawRuntimeException dre)
391 {
392
393 }
394
395 Point3d p1 = new Point3d(1, 2, 3);
396 Point3d p2 = new Point3d(3, 4, 5);
397 PolyLine3d pl = new PolyLine3d(p1, p2);
398 assertEquals("two points", 2, pl.size());
399 assertEquals("p1", p1, pl.get(0));
400 assertEquals("p2", p2, pl.get(1));
401
402 pl = new PolyLine3d(p1, p2, (Point3d[]) null);
403 assertEquals("two points", 2, pl.size());
404 assertEquals("p1", p1, pl.get(0));
405 assertEquals("p2", p2, pl.get(1));
406
407 pl = new PolyLine3d(p1, p2, new Point3d[0]);
408 assertEquals("two points", 2, pl.size());
409 assertEquals("p1", p1, pl.get(0));
410 assertEquals("p2", p2, pl.get(1));
411
412 try
413 {
414 new PolyLine3d(new Point3d[] {});
415 fail("empty array should have thrown a DrawRuntimeException");
416 }
417 catch (DrawRuntimeException dre)
418 {
419
420 }
421
422 try
423 {
424 new PolyLine3d(new Point3d[] { new Point3d(1, 2, 3) });
425 fail("single point should have thrown a DrawRuntimeException");
426 }
427 catch (DrawRuntimeException dre)
428 {
429
430 }
431
432 try
433 {
434 new PolyLine3d(new Point3d[] { new Point3d(1, 2, 3), new Point3d(1, 2, 3) });
435 fail("duplicate point should have thrown a DrawRuntimeException");
436 }
437 catch (DrawRuntimeException dre)
438 {
439
440 }
441
442 try
443 {
444 new PolyLine3d(new Point3d[] { new Point3d(1, 2, 3), new Point3d(1, 2, 3), new Point3d(3, 4, 5) });
445 fail("duplicate point should have thrown a DrawRuntimeException");
446 }
447 catch (DrawRuntimeException dre)
448 {
449
450 }
451
452 try
453 {
454 new PolyLine3d(new Point3d[] { new Point3d(-1, -2, -3), new Point3d(1, 2, 3), new Point3d(1, 2, 3),
455 new Point3d(3, 4, 5) });
456 fail("duplicate point should have thrown a DrawRuntimeException");
457 }
458 catch (DrawRuntimeException dre)
459 {
460
461 }
462 }
463
464
465
466
467
468 @Test
469 public final void exceptionTest() throws DrawRuntimeException
470 {
471 PolyLine3d line = new PolyLine3d(new Point3d[] { new Point3d(1, 2, 3), new Point3d(4, 5, 6) });
472 try
473 {
474 line.get(-1);
475 fail("Should have thrown an IndexOutOfBoundsException");
476 }
477 catch (IndexOutOfBoundsException ioobe)
478 {
479
480 }
481
482 try
483 {
484 line.get(2);
485 fail("Should have thrown an IndexOutOfBoundsException");
486 }
487 catch (IndexOutOfBoundsException ioobe)
488 {
489
490 }
491 }
492
493
494
495
496
497 @Test
498 public final void locationExtendedTest() throws DrawRuntimeException
499 {
500 Point3d p0 = new Point3d(10, 20, 30);
501 Point3d p1 = new Point3d(40, 50, 60);
502 Point3d p2 = new Point3d(90, 80, 70);
503 PolyLine3d polyLine = new PolyLine3d(new Point3d[] { p0, p1, p2 });
504 double expectedPhi1 = Math.atan2(p1.y - p0.y, p1.x - p0.x);
505 double expectedTheta1 = Math.atan2(Math.hypot(p1.x - p0.x, p1.y - p0.y), p1.z - p0.z);
506 checkGetLocation(polyLine, -10, null, expectedPhi1, expectedTheta1);
507 checkGetLocation(polyLine, -0.0001, p0, expectedPhi1, expectedTheta1);
508 checkGetLocation(polyLine, 0, p0, expectedPhi1, expectedTheta1);
509 checkGetLocation(polyLine, 0.0001, p0, expectedPhi1, expectedTheta1);
510 double expectedPhi2 = Math.atan2(p2.y - p1.y, p2.x - p1.x);
511 double expectedTheta2 = Math.atan2(Math.hypot(p2.x - p1.x, p2.y - p1.y), p2.z - p1.z);
512 checkGetLocation(polyLine, 0.9999, p2, expectedPhi2, expectedTheta2);
513 checkGetLocation(polyLine, 1.0, p2, expectedPhi2, expectedTheta2);
514 checkGetLocation(polyLine, 1.0001, p2, expectedPhi2, expectedTheta2);
515 checkGetLocation(polyLine, 10, null, expectedPhi2, expectedTheta2);
516 }
517
518
519
520
521
522
523
524
525
526
527 private void checkGetLocation(final PolyLine3d line, final double fraction, final Point3d expectedPoint,
528 final double expectedPhi, final double expectedTheta) throws DrawRuntimeException
529 {
530 double length = line.getLength();
531 checkRay3d(line.getLocationExtended(fraction * length), expectedPoint, expectedPhi, expectedTheta);
532 if (fraction < 0 || fraction > 1)
533 {
534 try
535 {
536 line.getLocation(fraction * length);
537 fail("getLocation should have thrown a DrawRuntimeException");
538 }
539 catch (DrawRuntimeException dre)
540 {
541
542 }
543 try
544 {
545 line.getLocationFraction(fraction);
546 fail("getLocation should have thrown a DrawRuntimeException");
547 }
548 catch (DrawRuntimeException ne)
549 {
550
551 }
552 }
553 else
554 {
555 checkRay3d(line.getLocation(fraction * length), expectedPoint, expectedPhi, expectedTheta);
556 checkRay3d(line.getLocationFraction(fraction), expectedPoint, expectedPhi, expectedTheta);
557 }
558
559 }
560
561
562
563
564
565
566
567
568 private void checkRay3d(final Ray3d dp, final Point3d expectedPoint, final double expectedPhi, final double expectedTheta)
569 {
570 if (null != expectedPoint)
571 {
572 Point3d p = new Point3d(dp.x, dp.y, dp.z);
573 assertEquals("locationExtended(0) returns approximately expected point", 0, expectedPoint.distance(p), 0.1);
574 }
575 assertEquals("Phi (rotation of projection from X axis)", expectedPhi, dp.getPhi(), 0.001);
576 assertEquals("Theta (rotation from Z axis)", expectedTheta, dp.getTheta(), 0.001);
577 }
578
579
580
581
582
583 @Test
584 public final void filterTest() throws DrawRuntimeException
585 {
586 Point3d[] tooShort = new Point3d[] {};
587 try
588 {
589 new PolyLine3d(true, tooShort);
590 fail("Array with no points should have thrown an exception");
591 }
592 catch (DrawRuntimeException dre)
593 {
594
595 }
596
597 tooShort = new Point3d[] { new Point3d(1, 2, 3) };
598 try
599 {
600 new PolyLine3d(true, tooShort);
601 fail("Array with one point should have thrown an exception");
602 }
603 catch (DrawRuntimeException dre)
604 {
605
606 }
607
608 Point3d p0 = new Point3d(1, 2, 3);
609 Point3d p1 = new Point3d(4, 5, 6);
610 Point3d[] points = new Point3d[] { p0, p1 };
611 PolyLine3d result = new PolyLine3d(true, points);
612 assertTrue("first point is p0", p0.equals(result.get(0)));
613 assertTrue("second point is p1", p1.equals(result.get(1)));
614 Point3d p1Same = new Point3d(4, 5, 6);
615 result = new PolyLine3d(true, new Point3d[] { p0, p0, p0, p0, p1Same, p0, p1, p1, p1Same, p1, p1 });
616 assertEquals("result should contain 4 points", 4, result.size());
617 assertTrue("first point is p0", p0.equals(result.get(0)));
618 assertTrue("second point is p1", p1.equals(result.get(1)));
619 assertTrue("third point is p0", p0.equals(result.get(0)));
620 assertTrue("last point is p1", p1.equals(result.get(1)));
621 new PolyLine3d(true, new Point3d[] { p0, new Point3d(1, 3, 4) });
622 new PolyLine3d(true, new Point3d[] { p0, new Point3d(1, 2, 4) });
623
624 try
625 {
626 PolyLine3d.cleanPoints(true, null);
627 fail("null iterator should have thrown a NullPointerException");
628 }
629 catch (NullPointerException npe)
630 {
631
632 }
633
634 try
635 {
636 PolyLine3d.cleanPoints(true, new Iterator<Point3d>()
637 {
638 @Override
639 public boolean hasNext()
640 {
641 return false;
642 }
643
644 @Override
645 public Point3d next()
646 {
647 return null;
648 }
649 });
650 fail("Iterator that has no data should have thrown a DrawRuntimeException");
651 }
652 catch (DrawRuntimeException dre)
653 {
654
655 }
656
657 Iterator<Point3d> iterator =
658 PolyLine3d.cleanPoints(true, Arrays.stream(new Point3d[] { new Point3d(1, 2, 3) }).iterator());
659 iterator.next();
660 assertFalse("iterator should now be out of data", iterator.hasNext());
661 try
662 {
663 iterator.next();
664 fail("Iterator that has no nore data should have thrown a NoSuchElementException");
665 }
666 catch (NoSuchElementException nse)
667 {
668
669 }
670
671
672 iterator = PolyLine3d.cleanPoints(false,
673 Arrays.stream(new Point3d[] { new Point3d(1, 2, 3), new Point3d(1, 2, 3), new Point3d(1, 2, 3) }).iterator());
674 assertTrue("iterator has initial point", iterator.hasNext());
675 iterator.next();
676 assertTrue("iterator has second point", iterator.hasNext());
677 iterator.next();
678 assertTrue("iterator has second point", iterator.hasNext());
679 iterator.next();
680 assertFalse("iterator has no more data", iterator.hasNext());
681 }
682
683
684
685
686
687 @Test
688 public final void equalsTest() throws DrawRuntimeException
689 {
690 Point3d p0 = new Point3d(1.1, 2.2, 3.3);
691 Point3d p1 = new Point3d(2.1, 2.2, 3.3);
692 Point3d p2 = new Point3d(3.1, 2.2, 3.3);
693
694 PolyLine3d line = new PolyLine3d(new Point3d[] { p0, p1, p2 });
695 assertTrue("Line3d is equal to itself", line.equals(line));
696 assertFalse("Line3d is not equal to null", line.equals(null));
697 assertFalse("Line3d is not equals to some other kind of Object", line.equals(new Object()));
698 PolyLine3d line2 = new PolyLine3d(new Point3d[] { p0, p1, p2 });
699 assertTrue("Line3d is equal ot other Line3d that has the exact same list of Point3d", line.equals(line2));
700 Point3d p2Same = new Point3d(3.1, 2.2, 3.3);
701 line2 = new PolyLine3d(new Point3d[] { p0, p1, p2Same });
702 assertTrue("Line3d is equal ot other Line3d that has the exact same list of Point3d; even if some of "
703 + "those point are different instances with the same coordinates", line.equals(line2));
704 Point3d p2NotSame = new Point3d(3.1, 2.2, 3.35);
705 line2 = new PolyLine3d(new Point3d[] { p0, p1, p2NotSame });
706 assertFalse("Line3d is not equal ot other Line3d that differs in one coordinate", line.equals(line2));
707 line2 = new PolyLine3d(new Point3d[] { p0, p1, p2, p2NotSame });
708 assertFalse("Line3d is not equal ot other Line3d that has more points (but is identical up to the common length)",
709 line.equals(line2));
710 assertFalse("Line3d is not equal ot other Line3d that has fewer points (but is identical up to the common length)",
711 line2.equals(line));
712 }
713
714
715
716
717
718 @Test
719 public final void concatenateTest() throws DrawRuntimeException
720 {
721 Point3d p0 = new Point3d(1.1, 2.2, 3.3);
722 Point3d p1 = new Point3d(2.1, 2.2, 3.3);
723 Point3d p2 = new Point3d(3.1, 2.2, 3.3);
724 Point3d p3 = new Point3d(4.1, 2.2, 3.3);
725 Point3d p4 = new Point3d(5.1, 2.2, 3.3);
726 Point3d p5 = new Point3d(6.1, 2.2, 3.3);
727
728 PolyLine3d l0 = new PolyLine3d(p0, p1, p2);
729 PolyLine3d l1 = new PolyLine3d(p2, p3);
730 PolyLine3d l2 = new PolyLine3d(p3, p4, p5);
731 PolyLine3d ll = PolyLine3d.concatenate(l0, l1, l2);
732 assertEquals("size is 6", 6, ll.size());
733 assertEquals("point 0 is p0", p0, ll.get(0));
734 assertEquals("point 1 is p1", p1, ll.get(1));
735 assertEquals("point 2 is p2", p2, ll.get(2));
736 assertEquals("point 3 is p3", p3, ll.get(3));
737 assertEquals("point 4 is p4", p4, ll.get(4));
738 assertEquals("point 5 is p5", p5, ll.get(5));
739
740 ll = PolyLine3d.concatenate(l1);
741 assertEquals("size is 2", 2, ll.size());
742 assertEquals("point 0 is p2", p2, ll.get(0));
743 assertEquals("point 1 is p3", p3, ll.get(1));
744
745 try
746 {
747 PolyLine3d.concatenate(l0, l2);
748 fail("Gap should have throw an exception");
749 }
750 catch (DrawRuntimeException dre)
751 {
752
753 }
754 try
755 {
756 PolyLine3d.concatenate();
757 fail("concatenate of empty list should have thrown an exception");
758 }
759 catch (DrawRuntimeException dre)
760 {
761
762 }
763
764
765 PolyLine3d thirdLine = new PolyLine3d(p4, p5);
766 for (double tolerance : new double[] { 0.1, 0.01, 0.001, 0.0001, 0.00001 })
767 {
768 for (double actualError : new double[] { tolerance * 0.9, tolerance * 1.1 })
769 {
770 int maxDirection = 10;
771 for (int direction = 0; direction < maxDirection; direction++)
772 {
773 double dx = actualError * Math.cos(Math.PI * 2 * direction / maxDirection);
774 double dy = actualError * Math.sin(Math.PI * 2 * direction / maxDirection);
775 PolyLine3d otherLine = new PolyLine3d(new Point3d(p2.x + dx, p2.y + dy, p2.z), p3, p4);
776 if (actualError < tolerance)
777 {
778 try
779 {
780 PolyLine3d.concatenate(tolerance, l0, otherLine);
781 }
782 catch (DrawRuntimeException dre)
783 {
784 PolyLine3d.concatenate(tolerance, l0, otherLine);
785 fail("concatenation with error " + actualError + " and tolerance " + tolerance
786 + " should not have failed");
787 }
788 try
789 {
790 PolyLine3d.concatenate(tolerance, l0, otherLine, thirdLine);
791 }
792 catch (DrawRuntimeException dre)
793 {
794 fail("concatenation with error " + actualError + " and tolerance " + tolerance
795 + " should not have failed");
796 }
797 }
798 else
799 {
800 try
801 {
802 PolyLine3d.concatenate(tolerance, l0, otherLine);
803 }
804 catch (DrawRuntimeException dre)
805 {
806
807 }
808 try
809 {
810 PolyLine3d.concatenate(tolerance, l0, otherLine, thirdLine);
811 }
812 catch (DrawRuntimeException dre)
813 {
814
815 }
816 }
817 }
818 }
819 }
820 }
821
822
823
824
825
826 @Test
827 public final void reverseAndProjectTest() throws DrawRuntimeException
828 {
829 Point3d p0 = new Point3d(1.1, 2.21, 3.1);
830 Point3d p1 = new Point3d(2.1, 2.22, 3.2);
831 Point3d p2 = new Point3d(2.1, 2.23, 3.3);
832 Point3d p2x = new Point3d(p2.x, p2.y, p2.z + 1);
833 Point3d p3 = new Point3d(4.1, 2.24, 3.4);
834 Point3d p4 = new Point3d(5.1, 2.25, 3.5);
835 Point3d p5 = new Point3d(6.1, 2.26, 3.6);
836
837 PolyLine3d l01 = new PolyLine3d(p0, p1);
838 PolyLine3d r = l01.reverse();
839 assertEquals("result has size 2", 2, r.size());
840 assertEquals("point 0 is p1", p1, r.get(0));
841 assertEquals("point 1 is p0", p0, r.get(1));
842
843 PolyLine3d l05 = new PolyLine3d(p0, p1, p2, p3, p4, p5);
844 r = l05.reverse();
845 assertEquals("result has size 6", 6, r.size());
846 assertEquals("point 0 is p5", p5, r.get(0));
847 assertEquals("point 1 is p4", p4, r.get(1));
848 assertEquals("point 2 is p3", p3, r.get(2));
849 assertEquals("point 3 is p2", p2, r.get(3));
850 assertEquals("point 4 is p1", p1, r.get(4));
851 assertEquals("point 5 is p0", p0, r.get(5));
852
853 PolyLine2d l2d = l05.project();
854 assertEquals("result has size 6", 6, l2d.size());
855 assertEquals("point 0 is p5", p0.project(), l2d.get(0));
856 assertEquals("point 1 is p4", p1.project(), l2d.get(1));
857 assertEquals("point 2 is p3", p2.project(), l2d.get(2));
858 assertEquals("point 3 is p2", p3.project(), l2d.get(3));
859 assertEquals("point 4 is p1", p4.project(), l2d.get(4));
860 assertEquals("point 5 is p0", p5.project(), l2d.get(5));
861
862 l05 = new PolyLine3d(p0, p1, p2, p2x, p3, p4, p5);
863 l2d = l05.project();
864 assertEquals("result has size 6", 6, l2d.size());
865 assertEquals("point 0 is p5", p0.project(), l2d.get(0));
866 assertEquals("point 1 is p4", p1.project(), l2d.get(1));
867 assertEquals("point 2 is p3", p2.project(), l2d.get(2));
868 assertEquals("point 3 is p2", p3.project(), l2d.get(3));
869 assertEquals("point 4 is p1", p4.project(), l2d.get(4));
870 assertEquals("point 5 is p0", p5.project(), l2d.get(5));
871
872 PolyLine3d l22x = new PolyLine3d(p2, p2x);
873 try
874 {
875 l22x.project();
876 fail("Projecting a Polyline3d that entirely projects to one point should have thrown an exception");
877 }
878 catch (DrawRuntimeException dre)
879 {
880
881 }
882 }
883
884
885
886
887
888 @SuppressWarnings("checkstyle:methodlength")
889 @Test
890 public final void extractTest() throws DrawRuntimeException
891 {
892 Point3d p0 = new Point3d(1, 2, 3);
893 Point3d p1 = new Point3d(2, 3, 4);
894 Point3d p1a = new Point3d(2.01, 3.01, 4.01);
895 Point3d p1b = new Point3d(2.02, 3.02, 4.02);
896 Point3d p1c = new Point3d(2.03, 3.03, 4.03);
897 Point3d p2 = new Point3d(12, 13, 14);
898
899 PolyLine3d l = new PolyLine3d(p0, p1);
900 PolyLine3d e = l.extractFractional(0, 1);
901 assertEquals("size of extraction is 2", 2, e.size());
902 assertEquals("point 0 is p0", p0, e.get(0));
903 assertEquals("point 1 is p1", p1, e.get(1));
904 try
905 {
906 l.extractFractional(-0.1, 1);
907 fail("negative start should have thrown an exception");
908 }
909 catch (DrawRuntimeException exception)
910 {
911
912 }
913 try
914 {
915 l.extractFractional(Double.NaN, 1);
916 fail("NaN start should have thrown an exception");
917 }
918 catch (DrawRuntimeException exception)
919 {
920
921 }
922 try
923 {
924 l.extractFractional(0, 1.1);
925 fail("end > 1 should have thrown an exception");
926 }
927 catch (DrawRuntimeException exception)
928 {
929
930 }
931 try
932 {
933 l.extractFractional(0, Double.NaN);
934 fail("NaN end should have thrown an exception");
935 }
936 catch (DrawRuntimeException exception)
937 {
938
939 }
940 try
941 {
942 l.extractFractional(0.6, 0.4);
943 fail("start > end should have thrown an exception");
944 }
945 catch (DrawRuntimeException exception)
946 {
947
948 }
949 try
950 {
951 l.extract(-0.1, 1);
952 fail("negative start should have thrown an exception");
953 }
954 catch (DrawRuntimeException exception)
955 {
956
957 }
958 try
959 {
960 l.extract(Double.NaN, 1);
961 fail("NaN start should have thrown an exception");
962 }
963 catch (DrawRuntimeException exception)
964 {
965
966 }
967 try
968 {
969 l.extract(0, l.getLength() + 0.1);
970 fail("end > length should have thrown an exception");
971 }
972 catch (DrawRuntimeException exception)
973 {
974
975 }
976 try
977 {
978 l.extract(0, Double.NaN);
979 fail("NaN end should have thrown an exception");
980 }
981 catch (DrawRuntimeException exception)
982 {
983
984 }
985 try
986 {
987 l.extract(0.6, 0.4);
988 fail("start > end should have thrown an exception");
989 }
990 catch (DrawRuntimeException exception)
991 {
992
993 }
994
995 for (int i = 0; i < 10; i++)
996 {
997 for (int j = i + 1; j < 10; j++)
998 {
999 double start = i * l.getLength() / 10;
1000 double end = j * l.getLength() / 10;
1001
1002 for (PolyLine3d extractedLine : new PolyLine3d[] { l.extract(start, end),
1003 l.extractFractional(1.0 * i / 10, 1.0 * j / 10) })
1004 {
1005 assertEquals("size of extract is 2", 2, extractedLine.size());
1006 assertEquals("x of 0", p0.x + (p1.x - p0.x) * i / 10, extractedLine.get(0).x, 0.0001);
1007 assertEquals("y of 0", p0.y + (p1.y - p0.y) * i / 10, extractedLine.get(0).y, 0.0001);
1008 assertEquals("z of 0", p0.z + (p1.z - p0.z) * i / 10, extractedLine.get(0).z, 0.0001);
1009 assertEquals("x of 1", p0.x + (p1.x - p0.x) * j / 10, extractedLine.get(1).x, 0.0001);
1010 assertEquals("y of 1", p0.y + (p1.y - p0.y) * j / 10, extractedLine.get(1).y, 0.0001);
1011 assertEquals("z of 1", p0.z + (p1.z - p0.z) * j / 10, extractedLine.get(1).z, 0.0001);
1012 }
1013 }
1014 }
1015
1016 for (PolyLine3d line : new PolyLine3d[] { new PolyLine3d(p0, p1, p2), new PolyLine3d(p0, p1, p1a, p1b, p1c, p2) })
1017 {
1018 for (int i = 0; i < 110; i++)
1019 {
1020 if (10 == i)
1021 {
1022 continue;
1023 }
1024 for (int j = i + 1; j < 110; j++)
1025 {
1026 if (10 == j)
1027 {
1028 continue;
1029 }
1030 double start = i * line.getLength() / 110;
1031 double end = j * line.getLength() / 110;
1032
1033
1034
1035 for (PolyLine3d extractedLine : new PolyLine3d[] { line.extract(start, end),
1036 line.extractFractional(1.0 * i / 110, 1.0 * j / 110) })
1037 {
1038 int expectedSize = i < 10 && j > 10 ? line.size() : 2;
1039 assertEquals("size is " + expectedSize, expectedSize, extractedLine.size());
1040 if (i < 10)
1041 {
1042 assertEquals("x of 0", p0.x + (p1.x - p0.x) * i / 10, extractedLine.get(0).x, 0.0001);
1043 assertEquals("y of 0", p0.y + (p1.y - p0.y) * i / 10, extractedLine.get(0).y, 0.0001);
1044 assertEquals("z of 0", p0.z + (p1.z - p0.z) * i / 10, extractedLine.get(0).z, 0.0001);
1045 }
1046 else
1047 {
1048 assertEquals("x of 0", p1.x + (p2.x - p1.x) * (i - 10) / 100, extractedLine.get(0).x, 0.0001);
1049 assertEquals("y of 0", p1.y + (p2.y - p1.y) * (i - 10) / 100, extractedLine.get(0).y, 0.0001);
1050 assertEquals("z of 0", p1.z + (p2.z - p1.z) * (i - 10) / 100, extractedLine.get(0).z, 0.0001);
1051 }
1052 if (j < 10)
1053 {
1054 assertEquals("x of 1", p0.x + (p1.x - p0.x) * j / 10, extractedLine.get(1).x, 0.0001);
1055 assertEquals("y of 1", p0.y + (p1.y - p0.y) * j / 10, extractedLine.get(1).y, 0.0001);
1056 assertEquals("z of 1", p0.z + (p1.z - p0.z) * j / 10, extractedLine.get(1).z, 0.0001);
1057 }
1058 else
1059 {
1060 assertEquals("x of last", p1.x + (p2.x - p1.x) * (j - 10) / 100, extractedLine.getLast().x, 0.0001);
1061 assertEquals("y of last", p1.y + (p2.y - p1.y) * (j - 10) / 100, extractedLine.getLast().y, 0.0001);
1062 assertEquals("z of last", p1.z + (p2.z - p1.z) * (j - 10) / 100, extractedLine.getLast().z, 0.0001);
1063 }
1064 if (extractedLine.size() > 2)
1065 {
1066 assertEquals("x of mid", p1.x, extractedLine.get(1).x, 0.0001);
1067 assertEquals("y of mid", p1.y, extractedLine.get(1).y, 0.0001);
1068 assertEquals("z of mid", p1.z, extractedLine.get(1).z, 0.0001);
1069 }
1070 }
1071 }
1072 }
1073 }
1074 }
1075
1076
1077
1078
1079
1080 @Test
1081 @SuppressWarnings("unlikely-arg-type")
1082 public final void testOtherMethods() throws DrawRuntimeException
1083 {
1084 Point3d[] array =
1085 new Point3d[] { new Point3d(1, 2, 3), new Point3d(3, 4, 5), new Point3d(3.2, 4.1, 5.1), new Point3d(5, 6, 7) };
1086 PolyLine3d line = new PolyLine3d(Arrays.stream(array).iterator());
1087 assertEquals("size", array.length, line.size());
1088 for (int i = 0; i < array.length; i++)
1089 {
1090 assertEquals("i-th point", array[i], line.get(i));
1091 }
1092 int nextIndex = 0;
1093 for (Iterator<Point3d> iterator = line.getPoints(); iterator.hasNext();)
1094 {
1095 assertEquals("i-th point from line iterator", array[nextIndex++], iterator.next());
1096 }
1097 assertEquals("iterator returned all points", array.length, nextIndex);
1098
1099 PolyLine3d filtered = line.noiseFilteredLine(0.0);
1100 assertEquals("filtered with 0 tolerance returns line", line, filtered);
1101 filtered = line.noiseFilteredLine(0.01);
1102 assertEquals("filtered with very low tolerance returns line", line, filtered);
1103 filtered = line.noiseFilteredLine(0.5);
1104 assertEquals("size of filtered line is 3", 3, filtered.size());
1105 assertEquals("first point of filtered line matches", line.getFirst(), filtered.getFirst());
1106 assertEquals("last point of filtered line matches", line.getLast(), filtered.getLast());
1107 assertEquals("mid point of filtered line is point 1 of unfiltered line", line.get(1), filtered.get(1));
1108 filtered = line.noiseFilteredLine(10);
1109 assertEquals("size of filtered line is 2", 2, filtered.size());
1110 assertEquals("first point of filtered line matches", line.getFirst(), filtered.getFirst());
1111 assertEquals("last point of filtered line matches", line.getLast(), filtered.getLast());
1112
1113 array = new Point3d[] { new Point3d(1, 2, 3), new Point3d(3, 4, 5), new Point3d(3.2, 4.1, 5.1), new Point3d(1, 2, 3) };
1114 line = new PolyLine3d(Arrays.stream(array).iterator());
1115 filtered = line.noiseFilteredLine(10);
1116 assertEquals("size of filtered line is 3", 3, filtered.size());
1117 assertEquals("first point of filtered line matches", line.getFirst(), filtered.getFirst());
1118 assertEquals("last point of filtered line matches", line.getLast(), filtered.getLast());
1119 assertEquals("mid point of filtered line is point 1 of unfiltered line", line.get(1), filtered.get(1));
1120
1121 array = new Point3d[] { new Point3d(1, 2, 3), new Point3d(3, 4, 5), new Point3d(1.1, 2.1, 3), new Point3d(1, 2, 3) };
1122 line = new PolyLine3d(Arrays.stream(array).iterator());
1123 filtered = line.noiseFilteredLine(0.5);
1124 assertEquals("size of filtered line is 3", 3, filtered.size());
1125 assertEquals("first point of filtered line matches", line.getFirst(), filtered.getFirst());
1126 assertEquals("last point of filtered line matches", line.getLast(), filtered.getLast());
1127 assertEquals("mid point of filtered line is point 1 of unfiltered line", line.get(1), filtered.get(1));
1128
1129 array = new Point3d[] { new Point3d(1, 2, 3), new Point3d(3, 4, 5) };
1130 line = new PolyLine3d(Arrays.stream(array).iterator());
1131 filtered = line.noiseFilteredLine(10);
1132 assertEquals("Filtering a two-point line returns that line", line, filtered);
1133
1134 array = new Point3d[] { new Point3d(1, 2, 3), new Point3d(1, 2, 3), new Point3d(1, 2, 3), new Point3d(3, 4, 5) };
1135 line = new PolyLine3d(true, array);
1136 assertEquals("cleaned line has 2 points", 2, line.size());
1137 assertEquals("first point", array[0], line.getFirst());
1138 assertEquals("last point", array[array.length - 1], line.getLast());
1139
1140 array = new Point3d[] { new Point3d(1, 2, 3), new Point3d(1, 2, 3), new Point3d(3, 4, 5), new Point3d(3, 4, 5) };
1141 line = new PolyLine3d(true, array);
1142 assertEquals("cleaned line has 2 points", 2, line.size());
1143 assertEquals("first point", array[0], line.getFirst());
1144 assertEquals("last point", array[array.length - 1], line.getLast());
1145
1146 array = new Point3d[] { new Point3d(0, -1, 3), new Point3d(1, 2, 4), new Point3d(1, 2, 4), new Point3d(3, 4, 4) };
1147 line = new PolyLine3d(true, array);
1148 assertEquals("cleaned line has 2 points", 3, line.size());
1149 assertEquals("first point", array[0], line.getFirst());
1150 assertEquals("last point", array[array.length - 1], line.getLast());
1151
1152 array = new Point3d[] { new Point3d(0, -1, 3), new Point3d(1, 2, 4), new Point3d(1, 2, 4), new Point3d(1, 2, 4),
1153 new Point3d(3, 4, 5) };
1154 line = new PolyLine3d(true, array);
1155 assertEquals("cleaned line has 3 points", 3, line.size());
1156 assertEquals("first point", array[0], line.getFirst());
1157 assertEquals("mid point", array[1], line.get(1));
1158 assertEquals("last point", array[array.length - 1], line.getLast());
1159
1160 try
1161 {
1162 new PolyLine3d(true, new Point3d[0]);
1163 fail("Too short array should have thrown a DrawRuntimeException");
1164 }
1165 catch (DrawRuntimeException dre)
1166 {
1167
1168 }
1169
1170 try
1171 {
1172 new PolyLine3d(true, new Point3d[] { new Point3d(1, 2, 3) });
1173 fail("Too short array should have thrown a DrawRuntimeException");
1174 }
1175 catch (DrawRuntimeException dre)
1176 {
1177
1178 }
1179
1180 try
1181 {
1182 new PolyLine3d(true, new Point3d[] { new Point3d(1, 2, 3), new Point3d(1, 2, 3) });
1183 fail("All duplicate points in array should have thrown a DrawRuntimeException");
1184 }
1185 catch (DrawRuntimeException dre)
1186 {
1187
1188 }
1189
1190 try
1191 {
1192 new PolyLine3d(true, new Point3d[] { new Point3d(1, 2, 3), new Point3d(1, 2, 3), new Point3d(1, 2, 3) });
1193 fail("All duplicate points in array should have thrown a DrawRuntimeException");
1194 }
1195 catch (DrawRuntimeException dre)
1196 {
1197
1198 }
1199
1200 array = new Point3d[] { new Point3d(1, 2, 3), new Point3d(4, 6, 9), new Point3d(8, 9, 15) };
1201 line = new PolyLine3d(array);
1202
1203 try
1204 {
1205 line.getLocation(-0.1);
1206 fail("negative location should have thrown a DrawRuntimeException");
1207 }
1208 catch (DrawRuntimeException dre)
1209 {
1210
1211 }
1212
1213 double length = line.getLength();
1214 assertEquals("Length of line is about 15.6", 15.6, length, 0.1);
1215
1216 try
1217 {
1218 line.getLocation(length + 0.1);
1219 fail("location beyond length should have thrown a DrawRuntimeException");
1220 }
1221 catch (DrawRuntimeException dre)
1222 {
1223
1224 }
1225
1226 try
1227 {
1228 line.getLocation(-0.1);
1229 fail("negative location should have thrown a DrawRuntimeException");
1230 }
1231 catch (DrawRuntimeException dre)
1232 {
1233
1234 }
1235
1236 assertEquals("Length of line is 15.6", 15.6, length, 0.1);
1237
1238 try
1239 {
1240 line.getLocationFraction(1.1);
1241 fail("location beyond length should have thrown a DrawRuntimeException");
1242 }
1243 catch (DrawRuntimeException dre)
1244 {
1245
1246 }
1247
1248 try
1249 {
1250 line.getLocationFraction(-0.1);
1251 fail("negative location should have thrown a DrawRuntimeException");
1252 }
1253 catch (DrawRuntimeException dre)
1254 {
1255
1256 }
1257
1258 for (double position : new double[] { -1, 0, 2.5, 4.9, 5.1, 7.5, 9.9, 10, 11 })
1259 {
1260 Ray3d ray = line.getLocationExtended(position);
1261 if (position < length / 2)
1262 {
1263 Ray3d expected =
1264 new Ray3d(array[0].interpolate(array[1], position / (length / 2)), Math.atan2(4, 3), Math.atan2(5, 6));
1265 assertTrue("interpolated/extrapolated point", expected.epsilonEquals(ray, 0.0001, 0.00001));
1266 }
1267 else
1268 {
1269 Ray3d expected = new Ray3d(array[1].interpolate(array[2], (position - length / 2) / (length / 2)),
1270 Math.atan2(3, 4), Math.atan2(5, 6));
1271 assertTrue("interpolated/extrapolated point", expected.epsilonEquals(ray, 0.0001, 0.00001));
1272 }
1273 ray = line.getLocationFractionExtended(position / line.getLength());
1274 if (position < length / 2)
1275 {
1276 Ray3d expected =
1277 new Ray3d(array[0].interpolate(array[1], position / (length / 2)), Math.atan2(4, 3), Math.atan2(5, 6));
1278 assertTrue("interpolated/extrapolated point", expected.epsilonEquals(ray, 0.0001, 0.00001));
1279 }
1280 else
1281 {
1282 Ray3d expected = new Ray3d(array[1].interpolate(array[2], (position - length / 2) / (length / 2)),
1283 Math.atan2(3, 4), Math.atan2(5, 6));
1284 assertTrue("interpolated/extrapolated point", expected.epsilonEquals(ray, 0.0001, 0.00001));
1285 }
1286 }
1287
1288
1289 array = new Point3d[] { new Point3d(1, 2, 3), new Point3d(4, 6, 8), new Point3d(8, 9, 13) };
1290 line = new PolyLine3d(array);
1291 for (double x = -15; x <= 20; x++)
1292 {
1293 for (double y = -15; y <= 20; y++)
1294 {
1295 for (double z = -15; z <= 20; z++)
1296 {
1297 Point3d xyz = new Point3d(x, y, z);
1298
1299 double result = line.projectOrthogonalFractional(xyz);
1300 if (!Double.isNaN(result))
1301 {
1302 assertTrue("result must be >= 0.0", result >= 0);
1303 assertTrue("result must be <= 1.0", result <= 1.0);
1304 Ray3d ray = line.getLocationFraction(result);
1305 Point3d projected = line.projectOrthogonal(xyz);
1306 assertEquals("if fraction is between 0 and 1; projectOrthogonal yiels point at that fraction", ray.x,
1307 projected.x, 00001);
1308 assertEquals("if fraction is between 0 and 1; projectOrthogonal yiels point at that fraction", ray.y,
1309 projected.y, 00001);
1310 assertEquals("if fraction is between 0 and 1; projectOrthogonal yiels point at that fraction", ray.z,
1311 projected.z, 00001);
1312 }
1313 else
1314 {
1315 assertNull("point projects outside line", line.projectOrthogonal(xyz));
1316 }
1317 result = line.projectOrthogonalFractionalExtended(xyz);
1318 if (!Double.isNaN(result))
1319 {
1320 Point3d resultPoint = line.getLocationFractionExtended(result);
1321 if (result >= 0.0 && result <= 1.0)
1322 {
1323 Point3d closestPointOnLine = line.closestPointOnPolyLine(xyz);
1324 assertEquals("resultPoint is equal to closestPoint", resultPoint, closestPointOnLine);
1325 assertEquals("getLocationFraction returns same as getLocationfractionExtended", resultPoint,
1326 line.getLocationFraction(result));
1327 }
1328 else
1329 {
1330 try
1331 {
1332 line.getLocationFraction(result);
1333 fail("illegal fraction should have thrown a DrawRuntimeException");
1334 }
1335 catch (DrawRuntimeException dre)
1336 {
1337
1338 }
1339 if (result < 0)
1340 {
1341 assertEquals("resultPoint lies on extention of start segment",
1342 resultPoint.distance(line.get(1)) - resultPoint.distance(line.getFirst()),
1343 line.getFirst().distance(line.get(1)), 0.0001);
1344 }
1345 else
1346 {
1347
1348 assertEquals("resultPoint lies on extention of end segment",
1349 resultPoint.distance(line.get(line.size() - 2)) - resultPoint.distance(line.getLast()),
1350 line.getLast().distance(line.get(line.size() - 2)), 0.0001);
1351 }
1352 }
1353 }
1354 else
1355 {
1356 assertNull("point projects outside extended line", line.projectOrthogonalExtended(xyz));
1357 Point3d closestPointOnLine = line.closestPointOnPolyLine(xyz);
1358 assertNotNull("closest point is never null", closestPointOnLine);
1359 boolean found = false;
1360 for (int index = 0; index < line.size(); index++)
1361 {
1362 Point3d linePoint = line.get(index);
1363 if (linePoint.x == closestPointOnLine.x && linePoint.y == closestPointOnLine.y)
1364 {
1365 found = true;
1366 }
1367 }
1368 assertTrue("closestPointOnLine is one of the construction points of the line", found);
1369 }
1370 Point3d closestPointOnLine = line.closestPointOnPolyLine(xyz);
1371 assertNotNull("closest point is never null", closestPointOnLine);
1372 }
1373 }
1374 }
1375 Point3d toleranceResultPoint = line.getLocationFraction(-0.01, 0.01);
1376 assertEquals("tolerance result matches extended fraction result", line.getLocationFraction(0), toleranceResultPoint);
1377 toleranceResultPoint = line.getLocationFraction(1.01, 0.01);
1378 assertEquals("tolerance result matches extended fraction result", line.getLocationFraction(1), toleranceResultPoint);
1379
1380 try
1381 {
1382 line.getLocationFraction(-.011, 0.01);
1383 fail("fraction outside tolerance should have thrown a DrawRuntimeException");
1384 }
1385 catch (DrawRuntimeException dre)
1386 {
1387
1388 }
1389
1390 try
1391 {
1392 line.getLocationFraction(1.011, 0.01);
1393 fail("fraction outside tolerance should have thrown a DrawRuntimeException");
1394 }
1395 catch (DrawRuntimeException dre)
1396 {
1397
1398 }
1399
1400
1401 array = new Point3d[] { new Point3d(1, 2, 3), new Point3d(4, 6, 8), new Point3d(8, 9, 10) };
1402 line = new PolyLine3d(array);
1403 length = line.getLength();
1404 for (double to : new double[] { -10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20 })
1405 {
1406 if (to <= 0 || to > length)
1407 {
1408 try
1409 {
1410 line.truncate(to);
1411 fail("illegal truncate should have thrown a DrawRuntimeException");
1412 }
1413 catch (DrawRuntimeException dre)
1414 {
1415
1416 }
1417 }
1418 else
1419 {
1420 PolyLine3d truncated = line.truncate(to);
1421 assertEquals("truncated line start with start point of line", line.getFirst(), truncated.getFirst());
1422 assertEquals("Length of truncated line is truncate position", to, truncated.getLength(), 0.0001);
1423 }
1424 for (double from : new double[] { -10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20 })
1425 {
1426 if (from >= to || from < 0 || to > length)
1427 {
1428 try
1429 {
1430 line.extract(from, to);
1431 fail("Illegal range should have thrown a DrawRuntimeException");
1432 }
1433 catch (DrawRuntimeException dre)
1434 {
1435
1436 }
1437 }
1438 else
1439 {
1440 PolyLine3d fragment = line.extract(from, to);
1441 Point3d fromPoint = line.getLocation(from);
1442 assertTrue("fragment starts at from", fromPoint.epsilonEquals(fragment.getFirst(), 0.00001));
1443 Point3d toPoint = line.getLocation(to);
1444 assertTrue("fragment ends at to", toPoint.epsilonEquals(fragment.getLast(), 0.00001));
1445 assertEquals("Length of fragment", to - from, fragment.getLength(), 0.0001);
1446 if (from == 0)
1447 {
1448 assertEquals("fragment starts at begin of line", line.getFirst(), fragment.getFirst());
1449 }
1450 if (to == length)
1451 {
1452 assertEquals("fragment ends at end of line", line.getLast(), fragment.getLast());
1453 }
1454 }
1455 }
1456 }
1457 try
1458 {
1459 line.extract(Double.NaN, 10.0);
1460 fail("NaN value should have thrown a DrawRuntimeException");
1461 }
1462 catch (DrawRuntimeException dre)
1463 {
1464
1465 }
1466
1467 try
1468 {
1469 line.extract(0.0, Double.NaN);
1470 fail("NaN value should have thrown a DrawRuntimeException");
1471 }
1472 catch (DrawRuntimeException dre)
1473 {
1474
1475 }
1476
1477
1478 assertNotEquals("hash code takes x coordinate of first point into account",
1479 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1480 new PolyLine3d(new Point3d(1, 0, 0), new Point3d(1, 1, 1)).hashCode());
1481 assertNotEquals("hash code takes y coordinate of first point into account",
1482 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1483 new PolyLine3d(new Point3d(0, 1, 0), new Point3d(1, 1, 1)).hashCode());
1484 assertNotEquals("hash code takes z coordinate of first point into account",
1485 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1486 new PolyLine3d(new Point3d(0, 0, 1), new Point3d(1, 1, 1)).hashCode());
1487 assertNotEquals("hash code takes x coordinate of second point into account",
1488 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1489 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(2, 1, 1)).hashCode());
1490 assertNotEquals("hash code takes y coordinate of second point into account",
1491 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1492 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 2, 1)).hashCode());
1493 assertNotEquals("hash code takes z coordinate of second point into account",
1494 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1495 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 2)).hashCode());
1496
1497
1498 assertTrue("line is equal to itself", line.equals(line));
1499 assertFalse("line is not equal to a different line",
1500 line.equals(new PolyLine3d(new Point3d(123, 456, 789), new Point3d(789, 101112, 987))));
1501 assertFalse("line is not equal to null", line.equals(null));
1502 assertFalse("line is not equal to a different kind of object", line.equals("unlikely"));
1503 assertTrue("Line is equal to line from same set of points", line.equals(new PolyLine3d(line.getPoints())));
1504
1505 Point3d[] otherArray = Arrays.copyOf(array, array.length);
1506 otherArray[otherArray.length - 1] = new Point3d(otherArray[otherArray.length - 1].x,
1507 otherArray[otherArray.length - 1].y + 5, otherArray[otherArray.length - 1].z);
1508 PolyLine3d other = new PolyLine3d(otherArray);
1509 assertFalse("PolyLine3d that differs in y of last point is different", line.equals(other));
1510 }
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521 @Test
1522 public final void testFind() throws DrawRuntimeException, NoSuchMethodException, SecurityException, IllegalAccessException,
1523 IllegalArgumentException, InvocationTargetException
1524 {
1525
1526 List<Point3d> points = new ArrayList<>();
1527 for (int i = 0; i < 20; i++)
1528 {
1529 points.add(new Point3d(Math.pow(2, i) - 1, 10, 20));
1530 }
1531 PolyLine3d line = new PolyLine3d(points);
1532 double end = points.get(points.size() - 1).x;
1533 for (int i = 0; i < end; i++)
1534 {
1535 double pos = i + 0.5;
1536 int index = line.find(pos);
1537 assertTrue("segment starts before pos", line.get(index).x <= pos);
1538 assertTrue("next segment starts after pos", line.get(index + 1).x >= pos);
1539 }
1540 assertEquals("pos 0 returns index 0", 0, line.find(0.0));
1541 }
1542
1543
1544
1545
1546
1547 @Test
1548 public final void testTruncate() throws DrawRuntimeException
1549 {
1550 Point3d from = new Point3d(10, 20, 30);
1551 Point3d to = new Point3d(70, 80, 90);
1552 double length = from.distance(to);
1553 PolyLine3d line = new PolyLine3d(from, to);
1554 PolyLine3d truncatedLine = line.truncate(length);
1555 assertEquals("Start of line truncated at full length is the same as start of the input line", truncatedLine.get(0),
1556 from);
1557 assertEquals("End of line truncated at full length is about the same as end of input line", 0,
1558 truncatedLine.get(1).distance(to), 0.0001);
1559 try
1560 {
1561 line.truncate(-0.1);
1562 fail("truncate at negative length should have thrown DrawRuntimeException");
1563 }
1564 catch (DrawRuntimeException dre)
1565 {
1566
1567 }
1568 try
1569 {
1570 line.truncate(length + 0.1);
1571 fail("truncate at length beyond length of line should have thrown DrawExDrawRuntimeExceptionception");
1572 }
1573 catch (DrawRuntimeException dre)
1574 {
1575
1576 }
1577 truncatedLine = line.truncate(length / 2);
1578 assertEquals("Start of truncated line is the same as start of the input line", truncatedLine.get(0), from);
1579 Point3d halfWay = new Point3d((from.x + to.x) / 2, (from.y + to.y) / 2, (from.z + to.z) / 2);
1580 assertEquals("End of 50%, truncated 2-point line should be at the half way point", 0,
1581 halfWay.distance(truncatedLine.get(1)), 0.0001);
1582 Point3d intermediatePoint = new Point3d(20, 20, 20);
1583 line = new PolyLine3d(from, intermediatePoint, to);
1584 length = from.distance(intermediatePoint) + intermediatePoint.distance(to);
1585 truncatedLine = line.truncate(length);
1586 assertEquals("Start of line truncated at full length is the same as start of the input line", truncatedLine.get(0),
1587 from);
1588 assertEquals("End of line truncated at full length is about the same as end of input line", 0,
1589 truncatedLine.get(2).distance(to), 0.0001);
1590 truncatedLine = line.truncate(from.distance(intermediatePoint));
1591 assertEquals("Start of line truncated at full length is the same as start of the input line", truncatedLine.get(0),
1592 from);
1593 assertEquals("Line truncated at intermediate point ends at that intermediate point", 0,
1594 truncatedLine.get(1).distance(intermediatePoint), 0.0001);
1595 }
1596
1597
1598
1599
1600 @Test
1601 public void testExports()
1602 {
1603 Point3d[] points = new Point3d[] { new Point3d(123.456, 345.678, 901.234), new Point3d(234.567, 456.789, 12.345),
1604 new Point3d(-12.345, -34.567, 45.678) };
1605 PolyLine3d pl = new PolyLine3d(points);
1606 String[] out = pl.toExcel().split("\\n");
1607 assertEquals("Excel output consists of one line per point", points.length, out.length);
1608 for (int index = 0; index < points.length; index++)
1609 {
1610 String[] fields = out[index].split("\\t");
1611 assertEquals("each line consists of three fields", 3, fields.length);
1612 try
1613 {
1614 double x = Double.parseDouble(fields[0].trim());
1615 assertEquals("x matches", points[index].x, x, 0.001);
1616 }
1617 catch (NumberFormatException nfe)
1618 {
1619 fail("First field " + fields[0] + " does not parse as a double");
1620 }
1621 try
1622 {
1623 double y = Double.parseDouble(fields[1].trim());
1624 assertEquals("y matches", points[index].y, y, 0.001);
1625 }
1626 catch (NumberFormatException nfe)
1627 {
1628 fail("Second field " + fields[1] + " does not parse as a double");
1629 }
1630 try
1631 {
1632 double z = Double.parseDouble(fields[2].trim());
1633 assertEquals("z matches", points[index].z, z, 0.001);
1634 }
1635 catch (NumberFormatException nfe)
1636 {
1637 fail("Second field " + fields[2] + " does not parse as a double");
1638 }
1639 }
1640 }
1641
1642
1643
1644
1645
1646
1647 @SuppressWarnings("unlikely-arg-type")
1648 @Test
1649 public void testToStringHashCodeAndEquals() throws NullPointerException, DrawRuntimeException
1650 {
1651 PolyLine3d line = new PolyLine3d(new Point3d[] { new Point3d(1, 2, 3), new Point3d(4, 6, 8), new Point3d(8, 9, 10) });
1652 assertTrue("toString returns something descriptive", line.toString().startsWith("PolyLine3d ["));
1653 assertFalse("toString does not contain startPhi", line.toString().contains("startPhi"));
1654 assertFalse("toString does not contain startTheta", line.toString().contains("startTheta"));
1655 assertTrue("toString can suppress the class name", line.toString().indexOf(line.toString(true)) > 0);
1656
1657
1658 assertNotEquals("hash code takes x coordinate into account",
1659 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1660 new PolyLine3d(new Point3d(1, 0, 0), new Point3d(1, 1, 1)).hashCode());
1661 assertNotEquals("hash code takes y coordinate into account",
1662 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1663 new PolyLine3d(new Point3d(0, 1, 0), new Point3d(1, 1, 1)).hashCode());
1664 assertNotEquals("hash code takes z coordinate into account",
1665 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1666 new PolyLine3d(new Point3d(0, 0, 1), new Point3d(1, 1, 1)).hashCode());
1667 assertNotEquals("hash code takes x coordinate into account",
1668 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1669 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(2, 1, 1)).hashCode());
1670 assertNotEquals("hash code takes y coordinate into account",
1671 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1672 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 2, 1)).hashCode());
1673 assertNotEquals("hash code takes z coordinate into account",
1674 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 1)).hashCode(),
1675 new PolyLine3d(new Point3d(0, 0, 0), new Point3d(1, 1, 2)).hashCode());
1676
1677
1678 assertTrue("line is equal to itself", line.equals(line));
1679 assertFalse("line is not equal to a different line",
1680 line.equals(new PolyLine2d(new Point2d(123, 456), new Point2d(789, 101112))));
1681 assertFalse("line is not equal to null", line.equals(null));
1682 assertFalse("line is not equal to a different kind of object", line.equals("unlikely"));
1683 assertEquals("equals verbatim copy", line,
1684 new PolyLine3d(new Point3d[] { new Point3d(1, 2, 3), new Point3d(4, 6, 8), new Point3d(8, 9, 10) }));
1685 assertNotEquals("equals checks x", line,
1686 new PolyLine3d(new Point3d[] { new Point3d(2, 2, 3), new Point3d(4, 6, 8), new Point3d(8, 9, 10) }));
1687 assertNotEquals("equals checks y", line,
1688 new PolyLine3d(new Point3d[] { new Point3d(1, 2, 3), new Point3d(4, 7, 8), new Point3d(8, 9, 10) }));
1689 assertNotEquals("equals checks z", line,
1690 new PolyLine3d(new Point3d[] { new Point3d(1, 2, 3), new Point3d(4, 6, 8), new Point3d(8, 9, 11) }));
1691 assertTrue("Line is equal to line from same set of points", line.equals(new PolyLine3d(line.getPoints())));
1692 }
1693
1694
1695
1696
1697
1698 @Test
1699 public void testProjectProblem() throws DrawRuntimeException
1700 {
1701 PolyLine3d polyLine3d = new PolyLine3d(new Point3d(1, 1, 2), new Point3d(11, 1, 5), new Point3d(16, 6, 0),
1702 new Point3d(21, 6, 0), new Point3d(21, 0, 0));
1703 double x = 11;
1704 double y = 1;
1705 Point2d point = new Point2d(x, y);
1706
1707 PolyLine2d projectedLine = polyLine3d.project();
1708
1709 int bestSegmentIndex = -1;
1710 double bestDistance = Double.POSITIVE_INFINITY;
1711 double bestSegmentDirection = Double.NaN;
1712 Point2d prevPoint = null;
1713
1714 for (int index = 0; index < projectedLine.size(); index++)
1715 {
1716 Point2d nextPoint = projectedLine.get(index);
1717 if (null != prevPoint)
1718 {
1719 Point2d closestOnSegment = point.closestPointOnSegment(prevPoint, nextPoint);
1720 double distance = closestOnSegment.distance(point);
1721 if (distance < bestDistance)
1722 {
1723 bestDistance = distance;
1724 bestSegmentIndex = index;
1725 bestSegmentDirection = prevPoint.directionTo(nextPoint);
1726 }
1727 }
1728 prevPoint = nextPoint;
1729 }
1730
1731
1732 double prevDirection = projectedLine.get(bestSegmentIndex - 1).directionTo(projectedLine.get(bestSegmentIndex));
1733 double nextDirection = bestSegmentIndex < projectedLine.size() - 1
1734 ? projectedLine.get(bestSegmentIndex).directionTo(projectedLine.get(bestSegmentIndex + 1))
1735 : projectedLine.get(projectedLine.size() - 2).directionTo(projectedLine.getLast());
1736 Ray2d prevRay =
1737 new Ray2d(projectedLine.get(bestSegmentIndex - 1), (prevDirection + bestSegmentDirection) / 2 + Math.PI / 2);
1738 Ray2d nextRay =
1739 new Ray2d(projectedLine.get(bestSegmentIndex), (bestSegmentDirection + nextDirection) / 2 + Math.PI / 2);
1740
1741 Point2d prevRayProjection = prevRay.projectOrthogonalExtended(point);
1742 Point2d nextRayProjection = nextRay.projectOrthogonalExtended(point);
1743 Point2d projectionOnBestSegment =
1744 prevRay.interpolate(nextRay, point.distance(prevRayProjection) / prevRayProjection.distance(nextRayProjection));
1745
1746
1747 for (int index = 1; index < polyLine3d.size(); index++)
1748 {
1749
1750
1751 if (polyLine3d.getX(index - 1) == prevRay.x && polyLine3d.getY(index - 1) == prevRay.y
1752 && polyLine3d.getX(index) == nextRay.x && polyLine3d.getY(index) == nextRay.y)
1753 {
1754 double lengthAtPrevRay = polyLine3d.lengthAtIndex(index - 1);
1755 double fraction = (lengthAtPrevRay + prevRay.distance(projectionOnBestSegment) / prevRay.distance(nextRay)
1756 * (polyLine3d.lengthAtIndex(index) - lengthAtPrevRay)) / polyLine3d.getLength();
1757
1758 polyLine3d.getLocationFraction(fraction);
1759 }
1760 }
1761 }
1762
1763
1764
1765
1766 @Test
1767 public void testTransitionLine()
1768 {
1769
1770 PolyLine3d bezier = Bezier.cubic(64, new Ray3d(-5, 0, 2, 0, 0, 2), new Ray3d(0, 5, 2, 0, 7, 2));
1771
1772 double length = bezier.getLength();
1773 double prevDir = Double.NaN;
1774 for (int step = 0; step <= 1000; step++)
1775 {
1776 double distance = length * step / 1000;
1777 Ray3d ray = bezier.getLocation(distance);
1778 double direction = Math.toDegrees(ray.phi);
1779 if (step > 0)
1780 {
1781 assertEquals("phi changes very little at step " + step, prevDir, direction, 2);
1782 }
1783 prevDir = Math.toDegrees(ray.phi);
1784 }
1785
1786 PolyLine3d transitioningOffsetLine = bezier.offsetLine(0, 2);
1787
1788 length = transitioningOffsetLine.getLength();
1789 prevDir = Double.NaN;
1790 for (int step = 0; step <= 1000; step++)
1791 {
1792 double distance = length * step / 1000;
1793 Ray3d ray = transitioningOffsetLine.getLocation(distance);
1794 double direction = Math.toDegrees(ray.phi);
1795 if (step > 0)
1796 {
1797 assertEquals("phi changes very little at step " + step, prevDir, direction, 2);
1798 }
1799 prevDir = Math.toDegrees(ray.phi);
1800 }
1801 PolyLine3d endLine = bezier.offsetLine(-2);
1802
1803 TransitionFunction transitionFunction = new TransitionFunction()
1804 {
1805 @Override
1806 public double function(final double fraction)
1807 {
1808 return 0.5 - Math.cos(fraction * Math.PI) / 2;
1809 }
1810 };
1811 PolyLine3d cosineSmoothTransitioningLine = bezier.transitionLine(endLine, transitionFunction);
1812
1813 length = cosineSmoothTransitioningLine.getLength();
1814 prevDir = Double.NaN;
1815 for (int step = 0; step <= 1000; step++)
1816 {
1817 double distance = length * step / 1000;
1818 Ray3d ray = cosineSmoothTransitioningLine.getLocation(distance);
1819 double direction = Math.toDegrees(ray.phi);
1820 if (step > 0)
1821 {
1822 assertEquals("phi changes very little at step " + step, prevDir, direction, 4);
1823 }
1824 prevDir = Math.toDegrees(ray.phi);
1825 }
1826
1827
1828
1829 PolyLine3d cosineSmoothTransitioningLine2 =
1830 endLine.reverse().transitionLine(bezier.reverse(), transitionFunction).reverse();
1831
1832 assertEquals("Lengths are equal", cosineSmoothTransitioningLine.getLength(), cosineSmoothTransitioningLine2.getLength(),
1833 0.001);
1834 for (int step = 0; step <= 1000; step++)
1835 {
1836 Ray3d ray1 = cosineSmoothTransitioningLine.getLocation(step * cosineSmoothTransitioningLine.getLength() / 1000);
1837 Ray3d ray2 = cosineSmoothTransitioningLine2.getLocation(step * cosineSmoothTransitioningLine2.getLength() / 1000);
1838 assertEquals("rays are almost equal in x", ray1.x, ray2.x, 0.001);
1839 assertEquals("rays are almost equal in y", ray1.y, ray2.y, 0.001);
1840 assertEquals("rays are almost equal in z", ray1.z, ray2.z, 0.001);
1841 assertEquals("rays are almost equal in phi", ray1.phi, ray2.phi, 0.0001);
1842 assertEquals("rays are almost equal in theta", ray1.theta, ray2.theta, 0.0001);
1843 }
1844
1845 assertEquals("offset by zero returns original", bezier, bezier.offsetLine(0, 0));
1846 assertEquals("offset by constant with two arguments returns same as offset with one argument", bezier.offsetLine(3, 3),
1847 bezier.offsetLine(3));
1848 }
1849
1850
1851
1852
1853 @Test
1854 public void testDegenerate()
1855 {
1856 try
1857 {
1858 new PolyLine3d(Double.NaN, 2, 2.5, 3, -1);
1859 fail("NaN should have thrown a DrawRuntimeException");
1860 }
1861 catch (DrawRuntimeException dre)
1862 {
1863
1864 }
1865
1866 try
1867 {
1868 new PolyLine3d(1, Double.NaN, 2.5, 3, -1);
1869 fail("NaN should have thrown a DrawRuntimeException");
1870 }
1871 catch (DrawRuntimeException dre)
1872 {
1873
1874 }
1875
1876 try
1877 {
1878 new PolyLine3d(1, 2, Double.NaN, 3, -1);
1879 fail("NaN should have thrown a DrawRuntimeException");
1880 }
1881 catch (DrawRuntimeException dre)
1882 {
1883
1884 }
1885
1886 try
1887 {
1888 new PolyLine3d(1, 2, 2.5, Double.NaN, -1);
1889 fail("NaN should have thrown a DrawRuntimeException");
1890 }
1891 catch (DrawRuntimeException dre)
1892 {
1893
1894 }
1895
1896 try
1897 {
1898 new PolyLine3d(1, 2, 2.5, 3, Double.NaN);
1899 fail("NaN should have thrown a DrawRuntimeException");
1900 }
1901 catch (DrawRuntimeException dre)
1902 {
1903
1904 }
1905
1906 try
1907 {
1908 new PolyLine3d(1, 2, 2.5, Double.POSITIVE_INFINITY, -1);
1909 fail("NaN should have thrown a DrawRuntimeException");
1910 }
1911 catch (DrawRuntimeException dre)
1912 {
1913
1914 }
1915
1916 try
1917 {
1918 new PolyLine3d(1, 2, 2.5, 3, Double.POSITIVE_INFINITY);
1919 fail("NaN should have thrown a DrawRuntimeException");
1920 }
1921 catch (DrawRuntimeException dre)
1922 {
1923
1924 }
1925
1926 try
1927 {
1928 new PolyLine3d(1, 2, 2.5, Double.NEGATIVE_INFINITY, -1);
1929 fail("NaN should have thrown a DrawRuntimeException");
1930 }
1931 catch (DrawRuntimeException dre)
1932 {
1933
1934 }
1935
1936 try
1937 {
1938 new PolyLine3d(1, 2, 2.5, 3, Double.NEGATIVE_INFINITY);
1939 fail("NaN should have thrown a DrawRuntimeException");
1940 }
1941 catch (DrawRuntimeException dre)
1942 {
1943
1944 }
1945
1946 PolyLine3d l = new PolyLine3d(1, 2, 2.5, 3, -1);
1947 assertEquals("length is 0", 0, l.getLength(), 0);
1948 assertEquals("size is 1", 1, l.size());
1949 assertEquals("getX(0) is 1", 1, l.getX(0), 0);
1950 assertEquals("getY(0) is 2", 2, l.getY(0), 0);
1951 Ray3d r = l.getLocation(0.0);
1952 assertEquals("heading at 0", 3, r.getPhi(), 0);
1953 assertEquals("x at 0 is 1", 1, r.getX(), 0);
1954 assertEquals("y at 0 is 2", 2, r.getY(), 0);
1955 assertEquals("bounds", new Bounds3d(l.get(0)), l.getBounds());
1956 try
1957 {
1958 l.getLocation(0.1);
1959 fail("location at position != 0 should have thrown a DrawRuntimeException");
1960 }
1961 catch (DrawRuntimeException dre)
1962 {
1963
1964 }
1965
1966 try
1967 {
1968 l.getLocation(-0.1);
1969 fail("location at position != 0 should have thrown a DrawRuntimeException");
1970 }
1971 catch (DrawRuntimeException dre)
1972 {
1973
1974 }
1975
1976 try
1977 {
1978 new PolyLine3d(new Point3d(1, 2, 2.5), Double.NaN, -1);
1979 fail("NaN should have thrown a DrawRuntimeException");
1980 }
1981 catch (DrawRuntimeException dre)
1982 {
1983
1984 }
1985
1986 try
1987 {
1988 new PolyLine3d(new Point3d(1, 2, 2.5), 3, Double.NaN);
1989 fail("NaN should have thrown a DrawRuntimeException");
1990 }
1991 catch (DrawRuntimeException dre)
1992 {
1993
1994 }
1995
1996 try
1997 {
1998 new PolyLine3d((Ray3d) null);
1999 fail("null pointer should have thrown a NullPointerException");
2000 }
2001 catch (NullPointerException npe)
2002 {
2003
2004 }
2005
2006 assertEquals("closest point is the point", r, l.closestPointOnPolyLine(new Point3d(4, -2, 7)));
2007
2008 PolyLine3d straightXisZ = new PolyLine3d(1, 2, 5, 0, 0);
2009 for (int x = -10; x <= 10; x += 1)
2010 {
2011 for (int y = -10; y <= 10; y += 1)
2012 {
2013 for (int z = -10; z <= 10; z += 1)
2014 {
2015 Point3d testPoint = new Point3d(x, y, z);
2016 assertEquals("closest point extended", r.projectOrthogonalExtended(testPoint),
2017 l.projectOrthogonalExtended(testPoint));
2018 assertEquals("closest point on degenerate line is the point of the degenerate line", l.getLocation(0.0),
2019 l.closestPointOnPolyLine(testPoint));
2020 if (z == 5)
2021 {
2022 assertEquals("projection on X==Z degenerate line hits", straightXisZ.get(0),
2023 straightXisZ.projectOrthogonal(testPoint));
2024 }
2025 else
2026 {
2027 assertNull("projection on X==Z degenerate line misses", straightXisZ.projectOrthogonal(testPoint));
2028 }
2029 if (x == 1 && y == 2 && z == 2.5)
2030 {
2031 assertEquals("NonExtended projection will return point for exact match", testPoint,
2032 l.projectOrthogonal(testPoint));
2033 assertEquals("NonExtended fractional projection returns 0 for exact match", 0,
2034 l.projectOrthogonalFractional(testPoint), 0);
2035 assertEquals("Extended fractional projection returns 0 for exact match", 0,
2036 l.projectOrthogonalFractionalExtended(testPoint), 0);
2037 }
2038 else
2039 {
2040 assertNull("For non-nice directions nonExtended projection will return null if point does not match",
2041 l.projectOrthogonal(testPoint));
2042 assertTrue("For non-nice directions non-extended fractional projection will return NaN if point does "
2043 + "not match", Double.isNaN(l.projectOrthogonalFractional(testPoint)));
2044 if (l.getLocation(0.0).projectOrthogonalFractional(testPoint) > 0)
2045 {
2046 assertTrue(
2047 "ProjectOrthogonalFractionalExtended returns POSITIVE_INFINITY of projection misses "
2048 + "along startHeading side",
2049 Double.POSITIVE_INFINITY == l.projectOrthogonalFractionalExtended(testPoint));
2050 }
2051 else
2052 {
2053 assertTrue(
2054 "ProjectOrthogonalFractionalExtended returns POSITIVE_INFINITY of projection misses "
2055 + ", but not along startHeading side",
2056 Double.NEGATIVE_INFINITY == l.projectOrthogonalFractionalExtended(testPoint));
2057 }
2058 }
2059 if (z == 5)
2060 {
2061 assertEquals("Non-Extended projection will return point for matching X for line along X",
2062 straightXisZ.get(0), straightXisZ.projectOrthogonal(testPoint));
2063 }
2064 else
2065 {
2066 assertNull("Non-Extended projection will return null for non matching X for line along X",
2067 straightXisZ.projectOrthogonal(testPoint));
2068 }
2069 }
2070 }
2071 }
2072
2073 l = new PolyLine3d(new Point3d(1, 2, 2.5), 3, -1);
2074 assertEquals("length is 0", 0, l.getLength(), 0);
2075 assertEquals("size is 1", 1, l.size());
2076 assertEquals("getX(0) is 1", 1, l.getX(0), 0);
2077 assertEquals("getY(0) is 2", 2, l.getY(0), 0);
2078 assertEquals("getZ(0) is 2.5", 2.5, l.getZ(0), 0);
2079 r = l.getLocation(0.0);
2080 assertEquals("phi at 0", 3, r.getPhi(), 0);
2081 assertEquals("theta at 0", -1, r.getTheta(), 0);
2082 assertEquals("x at 0 is 1", 1, r.getX(), 0);
2083 assertEquals("y at 0 is 2", 2, r.getY(), 0);
2084 assertEquals("z at 0 is 2.5", 2.5, r.getZ(), 0);
2085
2086 l = new PolyLine3d(new Ray3d(1, 2, 2.5, 3, -1));
2087 assertEquals("length is 0", 0, l.getLength(), 0);
2088 assertEquals("size is 1", 1, l.size());
2089 assertEquals("getX(0) is 1", 1, l.getX(0), 0);
2090 assertEquals("getY(0) is 2", 2, l.getY(0), 0);
2091 assertEquals("getZ(0) is 2.5", 2.5, l.getZ(0), 0);
2092 r = l.getLocation(0.0);
2093 assertEquals("phi at 0", 3, r.getPhi(), 0);
2094 assertEquals("theta at 0", -1, r.getTheta(), 0);
2095 assertEquals("x at 0 is 1", 1, r.getX(), 0);
2096 assertEquals("y at 0 is 2", 2, r.getY(), 0);
2097 assertEquals("z at 0 is 2.5", 2.5, r.getZ(), 0);
2098
2099 PolyLine3d notEqual = new PolyLine3d(1, 2, 2.5, 4, -1);
2100 assertNotEquals("Check that the equals method verifies the startPhi", l, notEqual);
2101 notEqual = new PolyLine3d(1, 2, 2.5, 3, -2);
2102 assertNotEquals("Check that the equals method verifies the startTheta", l, notEqual);
2103
2104 assertTrue("toString contains startPhi", l.toString().contains("startPhi"));
2105 assertTrue("toString contains startTheta", l.toString().contains("startTheta"));
2106 }
2107
2108
2109
2110
2111
2112
2113
2114 public static String marker(final double x, final double y)
2115 {
2116 final double markerSize = 0.05;
2117 return String.format("M%f,%f L%f,%f M%f,%f L%f,%f", x - markerSize / 2, y - markerSize / 2, x + markerSize / 2,
2118 y + markerSize / 2, x - markerSize / 2, y + markerSize / 2, x + markerSize / 2, y - markerSize / 2);
2119 }
2120
2121
2122
2123
2124
2125 @Test
2126 public void testOTS2Problem() throws DrawRuntimeException
2127 {
2128
2129 PolyLine3d line = new PolyLine3d(new Point3d(100, 0, 0), new Point3d(100.1, 0, 0));
2130 double length = line.getLength();
2131 line.getLocation(length - Math.ulp(length));
2132
2133
2134 line = new PolyLine3d(new Point3d(0, 0, 0), new Point3d(110.1, 0, 0), new Point3d(111, 0, 0));
2135 length = line.getLength();
2136 line.getLocation(length - Math.ulp(length));
2137
2138
2139 List<Point3d> list = new ArrayList<>();
2140 list.add(new Point3d(1, 2, 3));
2141 list.add(new Ray3d(2, 3, 4, 0, 0));
2142 new PolyLine3d(list);
2143 }
2144
2145 }