View Javadoc
1   package org.djutils.draw.point;
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.assertNotNull;
7   import static org.junit.jupiter.api.Assertions.assertNull;
8   import static org.junit.jupiter.api.Assertions.assertTrue;
9   import static org.junit.jupiter.api.Assertions.fail;
10  
11  import java.awt.geom.Point2D;
12  
13  import org.djutils.draw.DrawRuntimeException;
14  import org.djutils.draw.bounds.Bounds3d;
15  import org.djutils.draw.line.LineSegment3d;
16  import org.djutils.draw.line.PolyLine3d;
17  import org.djutils.exceptions.Try;
18  import org.junit.jupiter.api.Test;
19  
20  /**
21   * Point3dTest.java.
22   * <p>
23   * Copyright (c) 2020-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
24   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
25   * </p>
26   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
27   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
28   */
29  public class Point3dTest
30  {
31      /**
32       * Test the Point3d construction methods.
33       */
34      @SuppressWarnings("unlikely-arg-type")
35      @Test
36      public void testPoint3dConstruction()
37      {
38          Point3d p = new Point3d(10.0, -20.0, 16.0);
39          assertNotNull(p);
40          assertEquals(10.0, p.x, 0, "Access x");
41          assertEquals(-20.0, p.y, 0, "Access y");
42          assertEquals(16.0, p.z, 0, "Access z");
43          assertEquals(3, p.getDimensions(), "Dimensions is 3");
44  
45          assertEquals(1, p.size(), "Size method returns 1");
46  
47          Point2d projection = p.project();
48          assertEquals(10.0, projection.x, 0);
49          assertEquals(-20.0, projection.y, 0);
50  
51          try
52          {
53              new Point3d(Double.NaN, 0, 0);
54              fail("NaN should have thrown an IllegalArgumentException");
55          }
56          catch (IllegalArgumentException iae)
57          {
58              // Ignore expected exception
59          }
60  
61          try
62          {
63              new Point3d(0, Double.NaN, 0);
64              fail("NaN should have thrown an IllegalArgumentException");
65          }
66          catch (IllegalArgumentException iae)
67          {
68              // Ignore expected exception
69          }
70  
71          try
72          {
73              new Point3d(0, 0, Double.NaN);
74              fail("NaN should have thrown an IllegalArgumentException");
75          }
76          catch (IllegalArgumentException iae)
77          {
78              // Ignore expected exception
79          }
80  
81          double[] p3Arr = new double[] { 5.0, 6.0, 7.0 };
82          p = new Point3d(p3Arr);
83          assertEquals(5.0, p.x, 0);
84          assertEquals(6.0, p.y, 0);
85          assertEquals(7.0, p.z, 0);
86          Try.testFail(new Try.Execution()
87          {
88              @Override
89              public void execute() throws Throwable
90              {
91                  new Point3d(new double[] {});
92              }
93          }, "Should throw IAE", IllegalArgumentException.class);
94  
95          Try.testFail(new Try.Execution()
96          {
97              @Override
98              public void execute() throws Throwable
99              {
100                 new Point3d(new double[] { 1.0 });
101             }
102         }, "Should throw IAE", IllegalArgumentException.class);
103 
104         Try.testFail(new Try.Execution()
105         {
106             @Override
107             public void execute() throws Throwable
108             {
109                 new Point3d(new double[] { 1.0, 2.0 });
110             }
111         }, "Should throw IAE", IllegalArgumentException.class);
112 
113         Try.testFail(new Try.Execution()
114         {
115             @Override
116             public void execute() throws Throwable
117             {
118                 new Point3d(new double[] { 1.0, 2.0, 3.0, 4.0 });
119             }
120         }, "Should throw IAE", IllegalArgumentException.class);
121 
122         Try.testFail(new Try.Execution()
123         {
124             @Override
125             public void execute() throws Throwable
126             {
127                 new Point3d((Point2d) null, 0);
128             }
129         }, "Should throw NPE", NullPointerException.class);
130 
131         Try.testFail(new Try.Execution()
132         {
133             @Override
134             public void execute() throws Throwable
135             {
136                 new Point3d((Point2D.Double) null, 0);
137             }
138         }, "Should throw NPE", NullPointerException.class);
139 
140         Try.testFail(new Try.Execution()
141         {
142             @Override
143             public void execute() throws Throwable
144             {
145                 new Point3d(new Point2D.Double(Double.NaN, 2), 0);
146             }
147         }, "Should throw IAE", IllegalArgumentException.class);
148 
149         Try.testFail(new Try.Execution()
150         {
151             @Override
152             public void execute() throws Throwable
153             {
154                 new Point3d(new Point2D.Double(1, Double.NaN), 0);
155             }
156         }, "Should throw IAE", IllegalArgumentException.class);
157 
158         // equals and hashCode
159         assertTrue(p.equals(p));
160         assertEquals(p.hashCode(), p.hashCode());
161         Point2d p2d = new Point2d(1.0, 1.0);
162         assertFalse(p.equals(p2d));
163         assertFalse(p.equals(null));
164         assertNotEquals(p2d.hashCode(), p.hashCode());
165         assertEquals(p, p.translate(0.0, 0.0, 0.0), "Translating over 0,0,0 returns p");
166         assertNotEquals(p, p.translate(1.0, 0.0, 0.0));
167         assertNotEquals(p, p.translate(0.0, 1.0, 0.0));
168         assertNotEquals(p, p.translate(0.0, 0.0, 1.0));
169 
170         // toString
171         p = new Point3d(10.0, 20.0, 30.0);
172         assertEquals("Point3d [x=10.000000, y=20.000000, z=30.000000]", p.toString());
173         assertEquals("Point3d [x=10.0, y=20.0, z=30.0]", p.toString("%.1f"));
174         assertEquals("[x=10, y=20, z=30]", p.toString("%.0f", true));
175 
176         // epsilonEquals
177         assertTrue(p.epsilonEquals(p, 0.1));
178         assertTrue(p.epsilonEquals(p, 0.001));
179         assertTrue(p.epsilonEquals(p, 0.0));
180         Point3d p3 = p.translate(0.001, 0.0, 0.0);
181         assertTrue(p.epsilonEquals(p3, 0.09));
182         assertTrue(p3.epsilonEquals(p, 0.09));
183         assertFalse(p.epsilonEquals(p3, 0.0009));
184         assertFalse(p3.epsilonEquals(p, 0.0009));
185         p3 = p.translate(0.0, 0.001, 0.0);
186         assertTrue(p.epsilonEquals(p3, 0.09));
187         assertTrue(p3.epsilonEquals(p, 0.09));
188         assertFalse(p.epsilonEquals(p3, 0.0009));
189         assertFalse(p3.epsilonEquals(p, 0.0009));
190         p3 = p.translate(0.0, 0.0, 0.001);
191         assertTrue(p.epsilonEquals(p3, 0.09));
192         assertTrue(p3.epsilonEquals(p, 0.09));
193         assertFalse(p.epsilonEquals(p3, 0.0009));
194         assertFalse(p3.epsilonEquals(p, 0.0009));
195 
196         p2d = new Point2d(123, 456);
197         p3 = new Point3d(p2d, 789);
198         assertEquals(123, p3.x, 0, "x");
199         assertEquals(456, p3.y, 0, "y");
200         assertEquals(789, p3.z, 0, "z");
201 
202         Point2D p2D = new java.awt.geom.Point2D.Double(123, 456);
203         p3 = new Point3d(p2D, 789);
204         assertEquals(123, p3.x, 0, "x");
205         assertEquals(456, p3.y, 0, "y");
206         assertEquals(789, p3.z, 0, "z");
207     }
208 
209     /**
210      * Test the Point3d operators.
211      */
212     @Test
213     public void testPoint3dOperators()
214     {
215         Point3d p = new Point3d(-0.1, -0.2, -0.3);
216         assertEquals(0.1, p.abs().x, 1E-6);
217         assertEquals(0.2, p.abs().y, 1E-6);
218         assertEquals(0.3, p.abs().z, 1E-6);
219         p = p.neg();
220         assertEquals(0.1, p.x, 1E-6);
221         assertEquals(0.2, p.y, 1E-6);
222         assertEquals(0.3, p.z, 1E-6);
223         p = p.scale(1.0);
224         assertEquals(0.1, p.x, 1E-6);
225         assertEquals(0.2, p.y, 1E-6);
226         assertEquals(0.3, p.z, 1E-6);
227         p = p.scale(10.0);
228         assertEquals(1.0, p.x, 1E-6);
229         assertEquals(2.0, p.y, 1E-6);
230         assertEquals(3.0, p.z, 1E-6);
231         p = p.translate(5.0, -1.0, 0.5);
232         assertEquals(6.0, p.x, 1E-6);
233         assertEquals(1.0, p.y, 1E-6);
234         assertEquals(3.5, p.z, 1E-6);
235         Point3d p3d = p.translate(1.0, 1.0, 1.0);
236         assertEquals(7.0, p3d.x, 1E-6);
237         assertEquals(2.0, p3d.y, 1E-6);
238         assertEquals(4.5, p3d.z, 1E-6);
239         p3d = p.translate(6.0, 1.0);
240         assertEquals(12.0, p3d.x, 1E-6);
241         assertEquals(2.0, p3d.y, 1E-6);
242         assertEquals(3.5, p3d.z, 1E-6);
243 
244         try
245         {
246             p.translate(Double.NaN, 2.0);
247             fail("NaN translation should have thrown an IllegalArgumentException");
248         }
249         catch (IllegalArgumentException iae)
250         {
251             // Ignore expected exception
252         }
253 
254         try
255         {
256             p.translate(1.0, Double.NaN);
257             fail("NaN translation should have thrown an IllegalArgumentException");
258         }
259         catch (IllegalArgumentException iae)
260         {
261             // Ignore expected exception
262         }
263 
264         try
265         {
266             p.translate(Double.NaN, 2.0, 3.0);
267             fail("NaN translation should have thrown an IllegalArgumentException");
268         }
269         catch (IllegalArgumentException iae)
270         {
271             // Ignore expected exception
272         }
273 
274         try
275         {
276             p.translate(1.0, Double.NaN, 3.0);
277             fail("NaN translation should have thrown an IllegalArgumentException");
278         }
279         catch (IllegalArgumentException iae)
280         {
281             // Ignore expected exception
282         }
283 
284         try
285         {
286             p.translate(1.0, 2.0, Double.NaN);
287             fail("NaN translation should have thrown an IllegalArgumentException");
288         }
289         catch (IllegalArgumentException iae)
290         {
291             // Ignore expected exception
292         }
293 
294         // interpolate
295         Point3d p1 = new Point3d(1.0, 1.0, 1.0);
296         Point3d p2 = new Point3d(5.0, 5.0, 5.0);
297         assertEquals(p1, p1.interpolate(p2, 0.0), "Interpolate at 0.0 returns this");
298         assertEquals(p2, p2.interpolate(p1, 0.0));
299         assertEquals(p1, p1.interpolate(p1, 0.0));
300         assertEquals(new Point3d(3.0, 3.0, 3.0), p1.interpolate(p2, 0.5));
301 
302         // distance
303         assertEquals(Math.sqrt(48.0), p1.distance(p2), 0.001);
304         assertEquals(48.0, p1.distanceSquared(p2), 0.001);
305         assertEquals(Math.sqrt(32.0), p1.horizontalDistance(p2), 0.001);
306         assertEquals(32.0, p1.horizontalDistanceSquared(p2), 0.001);
307 
308         // direction
309         assertEquals(Math.toRadians(45.0), p2.horizontalDirection(), 0.001);
310         assertEquals(Math.toRadians(45.0), p1.horizontalDirection(p2), 0.001);
311         assertEquals(0.0, new Point3d(0.0, 0.0, 0.0).horizontalDirection(), 0.001);
312         assertEquals(Math.atan2(Math.sqrt(2.0), 1), p1.verticalDirection(p2), 0.001);
313         assertEquals(Math.PI / 2, p1.verticalDirection(new Point3d(2.0, 2.0, 1.0)), 0.01);
314         assertEquals(0, p1.verticalDirection(new Point3d(1.0, 1.0, 2.0)), 0.01);
315 
316         // normalize
317         Point3d pn = p2.normalize();
318         assertEquals(1.0 / Math.sqrt(3.0), pn.x, 0.001);
319         assertEquals(1.0 / Math.sqrt(3.0), pn.y, 0.001);
320         assertEquals(1.0 / Math.sqrt(3.0), pn.z, 0.001);
321 
322         Try.testFail(new Try.Execution()
323         {
324             @Override
325             public void execute() throws Throwable
326             {
327                 new Point3d(0.0, 0.0, 0.0).normalize();
328             }
329         }, "Should throw DRtE", DrawRuntimeException.class);
330 
331         assertEquals(1, p1.size(), "size of a Point3d is 1");
332         Point2d projection = p1.project();
333         assertEquals(p1.x, projection.x, 0, "projected x");
334         assertEquals(p1.y, projection.y, 0, "projected y");
335 
336         Bounds3d bounds = p1.getBounds();
337         assertEquals(p1.x, bounds.getMinX(), 0, "Bounds min x");
338         assertEquals(p1.y, bounds.getMinY(), 0, "Bounds min y");
339         assertEquals(p1.z, bounds.getMinZ(), 0, "Bounds min z");
340         assertEquals(p1.x, bounds.getMaxX(), 0, "Bounds max x");
341         assertEquals(p1.y, bounds.getMaxY(), 0, "Bounds max y");
342         assertEquals(p1.z, bounds.getMaxZ(), 0, "Bounds max z");
343     }
344 
345     /**
346      * Test the Point3d operators for NPE.
347      */
348     @Test
349     public void testPoint3dOperatorsNPE()
350     {
351         final Point3d p1 = new Point3d(1.0, 1.0, 1.0);
352 
353         Try.testFail(new Try.Execution()
354         {
355             @Override
356             public void execute() throws Throwable
357             {
358                 p1.interpolate(null, 0.5);
359             }
360         }, "Should throw NPE", NullPointerException.class);
361 
362         Try.testFail(new Try.Execution()
363         {
364             @Override
365             public void execute() throws Throwable
366             {
367                 p1.distance(null);
368             }
369         }, "Should throw NPE", NullPointerException.class);
370 
371         Try.testFail(new Try.Execution()
372         {
373             @Override
374             public void execute() throws Throwable
375             {
376                 p1.distanceSquared(null);
377             }
378         }, "Should throw NPE", NullPointerException.class);
379 
380         // FIXME
381         // Try.testFail(new Try.Execution()
382         // {
383         // @Override
384         // public void execute() throws Throwable
385         // {
386         // p1.horizontalDistance((Point2d) null);
387         // }
388         // }, "Should throw NPE", NullPointerException.class);
389         //
390         // Try.testFail(new Try.Execution()
391         // {
392         // @Override
393         // public void execute() throws Throwable
394         // {
395         // p1.horizontalDistanceSquared((Point3d) null);
396         // }
397         // }, "Should throw NPE", NullPointerException.class);
398 
399     }
400 
401     /**
402      * Test the closestPointOnSegment and the closestPointOnLine methods.
403      * @throws DrawRuntimeException if that happens uncaught; this test has failed
404      */
405     @Test
406     public void testClosestPointOnSegmentAndLine() throws DrawRuntimeException
407     {
408         Point3d p1 = new Point3d(-2, 3, 5);
409         for (Point3d p2 : new Point3d[] { new Point3d(7, 4, -5)/* angled */, new Point3d(-3, 6, 5) /* also angled */,
410                 new Point3d(-2, -5, 5) /* vertical */, new Point3d(8, 3, 5)/* horizontal */, new Point3d(-2, 3, 1)/* z */ })
411         {
412             PolyLine3d line = new PolyLine3d(p1, p2);
413             for (double x = -10; x <= 10; x += 0.5)
414             {
415                 for (double y = -10; y <= 10; y += 0.5)
416                 {
417                     for (double z = -10; z <= 10; z += 0.5)
418                     {
419                         Point3d p = new Point3d(x, y, z);
420                         // Figure out the correct result using a totally different method (binary search over the line segment)
421                         double fraction = 0.5;
422                         double step = 0.25;
423                         Point3d approximation = line.getLocationFraction(fraction);
424                         double distance = approximation.distance(p);
425                         // 10 iterations should get us to within one thousandth
426                         for (int iteration = 0; iteration < 10; iteration++)
427                         {
428                             // Try stepping up
429                             double upFraction = fraction + step;
430                             Point3d upApproximation = line.getLocationFraction(upFraction);
431                             double upDistance = upApproximation.distance(p);
432                             if (upDistance < distance)
433                             {
434                                 distance = upDistance;
435                                 fraction = upFraction;
436                                 approximation = upApproximation;
437                             }
438                             else
439                             {
440                                 // Try stepping down
441                                 double downFraction = fraction - step;
442                                 Point3d downApproximation = line.getLocationFraction(downFraction);
443                                 double downDistance = downApproximation.distance(p);
444                                 if (downDistance < distance)
445                                 {
446                                     distance = downDistance;
447                                     fraction = downFraction;
448                                     approximation = downApproximation;
449                                 }
450                             }
451                             step /= 2;
452                         }
453                         Point3d result = p.closestPointOnSegment(p1, p2);
454                         assertEquals(0, approximation.distance(result),
455                                 line.getLength() / 1000, "distance should be less than one thousandth of line length");
456                         assertEquals(p1, p.closestPointOnSegment(p1, p1),
457                                 "zero length line segment should always return start point");
458                         result = p.closestPointOnSegment(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
459                         assertEquals(0, approximation.distance(result),
460                                 line.getLength() / 1000, "distance should be less than one thousandth of line length");
461 
462                         if (fraction > 0.001 && fraction < 0.999)
463                         {
464                             result = p.closestPointOnLine(p1, p2);
465                             assertEquals(0, approximation.distance(result),
466                                     line.getLength() / 1000, "distance should be less than one thousandth of line length");
467                             result = p.closestPointOnLine(p1, p2);
468                             assertEquals(0, approximation.distance(result),
469                                     line.getLength() / 1000, "distance should be less than one thousandth of line length");
470                             result = p.closestPointOnLine(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
471                             assertEquals(0, approximation.distance(result),
472                                     line.getLength() / 1000, "distance should be less than one thousandth of line length");
473                         }
474                         else
475                         {
476                             // extrapolating
477                             double range = Math.max(Math.max(line.getLength(), p.distance(p1)), p.distance(p2));
478                             step = 5.0;
479                             fraction = 0.5;
480                             distance = range;
481                             // 10 iterations should get us to within one thousandth
482                             for (int iteration = 0; iteration < 20; iteration++)
483                             {
484                                 // Try stepping up
485                                 double upFraction = fraction + step;
486                                 Point3d upApproximation = line.getLocationFractionExtended(upFraction);
487                                 double upDistance = upApproximation.distance(p);
488                                 if (upDistance < distance)
489                                 {
490                                     distance = upDistance;
491                                     fraction = upFraction;
492                                     approximation = upApproximation;
493                                 }
494                                 else
495                                 {
496                                     // Try stepping down
497                                     double downFraction = fraction - step;
498                                     Point3d downApproximation = line.getLocationFractionExtended(downFraction);
499                                     double downDistance = downApproximation.distance(p);
500                                     if (downDistance < distance)
501                                     {
502                                         distance = downDistance;
503                                         fraction = downFraction;
504                                         approximation = downApproximation;
505                                     }
506                                 }
507                                 step /= 2;
508                             }
509                             result = p.closestPointOnLine(p1, p2);
510                             assertEquals(0, approximation.distance(result),
511                                     range / 1000, "distance should be less than one thousandth of range");
512                             result = p.closestPointOnLine(p1, p2);
513                             assertEquals(0, approximation.distance(result),
514                                     range / 1000, "distance should be less than one thousandth of range");
515                             result = p.closestPointOnLine(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
516                             assertEquals(0, approximation.distance(result),
517                                     range / 1000, "distance should be less than one thousandth of range");
518                             if (fraction < -0.001 || fraction > 1.001)
519                             {
520                                 assertNull(new LineSegment3d(p1, p2).projectOrthogonal(p),
521                                         "projectOrthogonal should return null");
522                                 assertEquals(result,
523                                         new LineSegment3d(p1, p2).projectOrthogonalExtended(p), "projectOrthogonalExtended should return same result as closestPointOnLine");
524                             }
525                         }
526                     }
527                 }
528             }
529         }
530 
531         try
532         {
533             p1.closestPointOnLine(null, new Point3d(5, 6, 7));
534             fail("null should have thrown a NullPointerException");
535         }
536         catch (NullPointerException npe)
537         {
538             // Ignore expected exception
539         }
540 
541         try
542         {
543             p1.closestPointOnLine(new Point3d(5, 6, 7), null);
544             fail("null should have thrown a NullPointerException");
545         }
546         catch (NullPointerException npe)
547         {
548             // Ignore expected exception
549         }
550 
551         try
552         {
553             p1.closestPointOnSegment(Double.NaN, 7, 8, 9, 10, 11);
554             fail("NaN value should have thrown a DrawRuntimeException");
555         }
556         catch (DrawRuntimeException dre)
557         {
558             // Ignore expected exception
559         }
560 
561         try
562         {
563             p1.closestPointOnSegment(6, Double.NaN, 8, 9, 10, 11);
564             fail("NaN value should have thrown a DrawRuntimeException");
565         }
566         catch (DrawRuntimeException dre)
567         {
568             // Ignore expected exception
569         }
570 
571         try
572         {
573             p1.closestPointOnSegment(6, 7, Double.NaN, 9, 10, 11);
574             fail("NaN value should have thrown a DrawRuntimeException");
575         }
576         catch (DrawRuntimeException dre)
577         {
578             // Ignore expected exception
579         }
580 
581         try
582         {
583             p1.closestPointOnSegment(6, 7, 8, Double.NaN, 10, 11);
584             fail("NaN value should have thrown a DrawRuntimeException");
585         }
586         catch (DrawRuntimeException dre)
587         {
588             // Ignore expected exception
589         }
590 
591         try
592         {
593             p1.closestPointOnSegment(6, 7, 8, 9, Double.NaN, 11);
594             fail("NaN value should have thrown a DrawRuntimeException");
595         }
596         catch (DrawRuntimeException dre)
597         {
598             // Ignore expected exception
599         }
600 
601         try
602         {
603             p1.closestPointOnSegment(6, 7, 8, 9, 10, Double.NaN);
604             fail("NaN value should have thrown a DrawRuntimeException");
605         }
606         catch (DrawRuntimeException dre)
607         {
608             // Ignore expected exception
609         }
610 
611         try
612         {
613             p1.closestPointOnLine(Double.NaN, 7, 8, 9, 10, 11);
614             fail("NaN value should have thrown a DrawRuntimeException");
615         }
616         catch (DrawRuntimeException dre)
617         {
618             // Ignore expected exception
619         }
620 
621         try
622         {
623             p1.closestPointOnLine(6, Double.NaN, 8, 9, 10, 11);
624             fail("NaN value should have thrown a DrawRuntimeException");
625         }
626         catch (DrawRuntimeException dre)
627         {
628             // Ignore expected exception
629         }
630 
631         try
632         {
633             p1.closestPointOnLine(6, 7, Double.NaN, 9, 10, 11);
634             fail("NaN value should have thrown a DrawRuntimeException");
635         }
636         catch (DrawRuntimeException dre)
637         {
638             // Ignore expected exception
639         }
640 
641         try
642         {
643             p1.closestPointOnLine(6, 7, 8, Double.NaN, 10, 11);
644             fail("NaN value should have thrown a DrawRuntimeException");
645         }
646         catch (DrawRuntimeException dre)
647         {
648             // Ignore expected exception
649         }
650 
651         try
652         {
653             p1.closestPointOnLine(6, 7, 8, 9, Double.NaN, 11);
654             fail("NaN value should have thrown a DrawRuntimeException");
655         }
656         catch (DrawRuntimeException dre)
657         {
658             // Ignore expected exception
659         }
660 
661         try
662         {
663             p1.closestPointOnLine(6, 7, 8, 9, 10, Double.NaN);
664             fail("NaN value should have thrown a DrawRuntimeException");
665         }
666         catch (DrawRuntimeException dre)
667         {
668             // Ignore expected exception
669         }
670 
671         try
672         {
673             p1.closestPointOnLine(6, 7, 8, 6, 7, 8);
674             fail("identical points should have thrown a DrawRuntimeException");
675         }
676         catch (DrawRuntimeException dre)
677         {
678             // Ignore expected exception
679         }
680 
681     }
682 
683 }