View Javadoc
1   package org.djutils.draw.line;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertTrue;
5   import static org.junit.Assert.fail;
6   
7   import org.djutils.draw.DrawRuntimeException;
8   import org.djutils.draw.point.Point2d;
9   import org.djutils.draw.point.Point3d;
10  import org.junit.Test;
11  
12  /**
13   * Test the Bézier class.
14   * <p>
15   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
16   * BSD-style license. See <a href="https://opentrafficsim.org/docs/v2/license.html">OpenTrafficSim License</a>.
17   * </p>
18   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Jan 2, 2017 <br>
19   * @author <a href="https://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
20   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
21   * @author <a href="https://www.transport.citg.tudelft.nl">Wouter Schakel</a>
22   */
23  public class BezierTest
24  {
25  
26      /**
27       * Test the various 2d methods in the Bezier class.
28       * @throws DrawRuntimeException when this happens uncaught this test has failed
29       * @throws DrawRuntimeException when this happens uncaught; this test has failed
30       */
31      @Test
32      public final void bezierTest2d() throws DrawRuntimeException, DrawRuntimeException
33      {
34          Point2d from = new Point2d(10, 0);
35          Point2d control1 = new Point2d(20, 0);
36          Point2d control2 = new Point2d(00, 20);
37          Point2d to = new Point2d(0, 10);
38          for (int n : new int[] {2, 3, 4, 100})
39          {
40              PolyLine2d line = Bezier.cubic(n, from, control1, control2, to);
41              assertTrue("result has n points", line.size() == n);
42              assertTrue("result starts with from", line.get(0).equals(from));
43              assertTrue("result ends with to", line.get(line.size() - 1).equals(to));
44              for (int i = 1; i < line.size() - 1; i++)
45              {
46                  Point2d p = line.get(i);
47                  assertTrue("x of intermediate point has reasonable value", p.x > 0 && p.x < 15);
48                  assertTrue("y of intermediate point has reasonable value", p.y > 0 && p.y < 15);
49              }
50          }
51          for (int n = -1; n <= 1; n++)
52          {
53              try
54              {
55                  Bezier.cubic(n, from, control1, control2, to);
56              }
57              catch (DrawRuntimeException e)
58              {
59                  // Ignore expected exception
60              }
61          }
62          for (int n : new int[] {2, 3, 4, 100})
63          {
64              for (double shape : new double[] {0.5, 1.0, 2.0})
65              {
66                  for (boolean weighted : new boolean[] {false, true})
67                  {
68                      Ray2d start = new Ray2d(from.x, from.y, Math.PI / 2);
69                      Ray2d end = new Ray2d(to.x, to.y, Math.PI);
70                      PolyLine2d line = 1.0 == shape ? Bezier.cubic(n, start, end) : Bezier.cubic(n, start, end, shape, weighted);
71                      for (int i = 1; i < line.size() - 1; i++)
72                      {
73                          Point2d p = line.get(i);
74                          assertTrue("x of intermediate point has reasonable value", p.x > 0 && p.x < 15);
75                          assertTrue("y of intermediate point has reasonable value", p.y > 0 && p.y < 15);
76                      }
77                  }
78              }
79          }
80          // Pity that the value 64 is private in the Bezier class.
81          assertEquals("Number of points is 64", 64,
82                  Bezier.cubic(new Ray2d(from.x, from.y, Math.PI / 2), new Ray2d(to.x, to.y, -Math.PI / 2)).size());
83          assertEquals("Number of points is 64", 64, Bezier.bezier(from, control1, control2, to).size());
84          control1 = new Point2d(5, 0);
85          control2 = new Point2d(0, 5);
86          for (int n : new int[] {2, 3, 4, 100})
87          {
88              PolyLine2d line = Bezier.cubic(n, from, control1, control2, to);
89              for (int i = 1; i < line.size() - 1; i++)
90              {
91                  Point2d p = line.get(i);
92                  // System.out.println("Point " + i + " of " + n + " is " + p);
93                  assertTrue("x of intermediate point has reasonable value", p.x > 0 && p.x < 10);
94                  assertTrue("y of intermediate point has reasonable value", p.y > 0 && p.y < 10);
95              }
96          }
97          for (int n : new int[] {2, 3, 4, 100})
98          {
99              PolyLine2d line = Bezier.cubic(n, new Ray2d(from.x, from.y, Math.PI), new Ray2d(to.x, to.y, Math.PI / 2));
100             for (int i = 1; i < line.size() - 1; i++)
101             {
102                 Point2d p = line.get(i);
103                 assertTrue("x of intermediate point has reasonable value", p.x > 0 && p.x < 10);
104                 assertTrue("y of intermediate point has reasonable value", p.y > 0 && p.y < 10);
105             }
106         }
107 
108         Point2d start = new Point2d(1, 1);
109         Point2d c1 = new Point2d(11, 1);
110         // Point2d c3 = new Point2d(5, 1);
111         Point2d c2 = new Point2d(1, 11);
112         Point2d end = new Point2d(11, 11);
113         double autoDistance = start.distance(end) / 2;
114         Point2d c1Auto = new Point2d(start.x + autoDistance, start.y);
115         Point2d c2Auto = new Point2d(end.x - autoDistance, end.y);
116         // Should produce a right leaning S shape; something between a slash and an S
117         PolyLine2d reference = Bezier.bezier(256, start, c1, c2, end);
118         PolyLine2d referenceAuto = Bezier.bezier(256, start, c1Auto, c2Auto, end);
119         // System.out.print("ref " + reference.toPlot());
120         Ray2d startRay = new Ray2d(start, start.directionTo(c1));
121         Ray2d endRay = new Ray2d(end, c2.directionTo(end));
122         for (double epsilonPosition : new double[] {3, 1, 0.1, 0.05, 0.02})
123         {
124             // System.out.println("epsilonPosition " + epsilonPosition);
125             PolyLine2d line = Bezier.bezier(epsilonPosition, start, c1, c2, end);
126             // System.out.print("epsilonPosition " + epsilonPosition + " yields " + line.toPlot());
127             // for (int percent = 0; percent <= 100; percent++)
128             // {
129             // Ray2d ray = reference.getLocationFraction(percent / 100.0);
130             // double position = line.projectRay(ray);
131             // Point2d pointAtPosition = line.getLocation(position);
132             // double positionError = ray.distance(pointAtPosition);
133             // System.out.print(String.format(" %.3f", positionError));
134             // if (positionError >= epsilonPosition)
135             // {
136             // System.out.println();
137             // System.out.println("percent " + percent + ", on " + ray + " projected to " + pointAtPosition
138             // + " positionError " + positionError);
139             // }
140             // assertTrue("Actual error " + positionError + " exceeds epsilon " + epsilonPosition,
141             // positionError < epsilonPosition);
142             // }
143             // System.out.println();
144             compareBeziers("bezier with 2 explicit control points", reference, line, 100, epsilonPosition);
145             line = Bezier.cubic(epsilonPosition, start, c1, c2, end);
146             compareBeziers("cubic with 2 explicit control points", reference, line, 100, epsilonPosition);
147             line = Bezier.cubic(epsilonPosition, startRay, endRay);
148             compareBeziers("cubic with automatic control points", referenceAuto, line, 100, epsilonPosition);
149         }
150 
151         try
152         {
153             Bezier.cubic(0.1, startRay, endRay, 0, true);
154             fail("Illegal shape value should have thrown a DrawRuntimeException");
155         }
156         catch (DrawRuntimeException dre)
157         {
158             // Ignore expected exception
159         }
160 
161         try
162         {
163             Bezier.cubic(0.1, startRay, endRay, 0);
164             fail("Illegal shape value should have thrown a DrawRuntimeException");
165         }
166         catch (DrawRuntimeException dre)
167         {
168             // Ignore expected exception
169         }
170 
171         try
172         {
173             Bezier.cubic(0.1, startRay, endRay, -1);
174             fail("Illegal shape value should have thrown a DrawRuntimeException");
175         }
176         catch (DrawRuntimeException dre)
177         {
178             // Ignore expected exception
179         }
180 
181         try
182         {
183             Bezier.cubic(0.1, startRay, endRay, -1, true);
184             fail("Illegal shape value should have thrown a DrawRuntimeException");
185         }
186         catch (DrawRuntimeException dre)
187         {
188             // Ignore expected exception
189         }
190 
191         try
192         {
193             Bezier.cubic(0.1, startRay, endRay, Double.NaN, true);
194             fail("Illegal shape value should have thrown a DrawRuntimeException");
195         }
196         catch (DrawRuntimeException dre)
197         {
198             // Ignore expected exception
199         }
200 
201         try
202         {
203             Bezier.cubic(0.1, startRay, endRay, Double.NaN);
204             fail("Illegal shape value should have thrown a DrawRuntimeException");
205         }
206         catch (DrawRuntimeException dre)
207         {
208             // Ignore expected exception
209         }
210 
211         try
212         {
213             Bezier.cubic(0.1, startRay, endRay, Double.POSITIVE_INFINITY);
214             fail("Illegal shape value should have thrown a DrawRuntimeException");
215         }
216         catch (DrawRuntimeException dre)
217         {
218             // Ignore expected exception
219         }
220 
221         try
222         {
223             Bezier.cubic(0.1, startRay, endRay, Double.POSITIVE_INFINITY, true);
224             fail("Illegal shape value should have thrown a DrawRuntimeException");
225         }
226         catch (DrawRuntimeException dre)
227         {
228             // Ignore expected exception
229         }
230 
231         try
232         {
233             Bezier.bezier(0.1, new Point2d[] {start});
234             fail("Too few points have thrown a DrawRuntimeException");
235         }
236         catch (DrawRuntimeException dre)
237         {
238             // Ignore expected exception
239         }
240 
241         try
242         {
243             Bezier.bezier(0.1, new Point2d[] {});
244             fail("Too few points have thrown a DrawRuntimeException");
245         }
246         catch (DrawRuntimeException dre)
247         {
248             // Ignore expected exception
249         }
250 
251         try
252         {
253             Bezier.bezier(0, start, c1, c2, end);
254             fail("illegal epsilon have thrown a DrawRuntimeException");
255         }
256         catch (DrawRuntimeException dre)
257         {
258             // Ignore expected exception
259         }
260 
261         try
262         {
263             Bezier.bezier(-0.1, start, c1, c2, end);
264             fail("illegal epsilon have thrown a DrawRuntimeException");
265         }
266         catch (DrawRuntimeException dre)
267         {
268             // Ignore expected exception
269         }
270 
271         try
272         {
273             Bezier.bezier(Double.NaN, start, c1, c2, end);
274             fail("illegal epsilon have thrown a DrawRuntimeException");
275         }
276         catch (DrawRuntimeException dre)
277         {
278             // Ignore expected exception
279         }
280 
281     }
282 
283     /**
284      * Compare B&eacute;zier curve approximations.
285      * @param description String; description of the test
286      * @param reference PolyLine2d; reference B&eacute;zier curve approximation
287      * @param candidate PolyLine2d; candidate B&eacute;zier curve approximation
288      * @param numberOfPoints int; number of point to compare the curves at, minus one; this method checks at 0% and at 100%
289      * @param epsilon double; upper limit of the distance between the two curves
290      * @throws DrawRuntimeException if that happens uncaught; a test has failed
291      */
292     public void compareBeziers(final String description, final PolyLine2d reference, final PolyLine2d candidate,
293             final int numberOfPoints, final double epsilon) throws DrawRuntimeException
294     {
295         for (int step = 0; step <= numberOfPoints; step++)
296         {
297             double fraction = 1.0 * step / numberOfPoints;
298             Ray2d ray = reference.getLocationFraction(fraction);
299             double position = candidate.projectRay(ray);
300             Point2d pointAtPosition = candidate.getLocation(position);
301             double positionError = ray.distance(pointAtPosition);
302             if (positionError >= epsilon)
303             {
304                 System.out.println("fraction " + fraction + ", on " + ray + " projected to " + pointAtPosition
305                         + " positionError " + positionError);
306                 System.out.print("connection: " + new PolyLine2d(ray, pointAtPosition).toPlot());
307                 System.out.print("reference: " + reference.toPlot());
308                 System.out.print("candidate: " + candidate.toPlot());
309             }
310             assertTrue(description + " actual error is less than epsilon ", positionError < epsilon);
311         }
312     }
313 
314     /**
315      * Test the various 3d methods in the Bezier class.
316      * @throws DrawRuntimeException when this happens uncaught this test has failed
317      */
318     @Test
319     public final void bezierTest3d() throws DrawRuntimeException
320     {
321         Point3d from = new Point3d(10, 0, 0);
322         Point3d control1 = new Point3d(20, 0, 10);
323         Point3d control2 = new Point3d(0, 20, 20);
324         Point3d to = new Point3d(0, 10, 30);
325         for (int n : new int[] {2, 3, 4, 100})
326         {
327             PolyLine3d line = Bezier.cubic(n, from, control1, control2, to);
328             assertTrue("result has n points", line.size() == n);
329             assertTrue("result starts with from", line.get(0).equals(from));
330             assertTrue("result ends with to", line.get(line.size() - 1).equals(to));
331             for (int i = 1; i < line.size() - 1; i++)
332             {
333                 Point3d p = line.get(i);
334                 // System.out.println(p);
335                 assertTrue("z of intermediate point has reasonable value", p.z > line.get(i - 1).z && p.z < line.get(i + 1).z);
336                 assertTrue("x of intermediate point has reasonable value", p.x > 0 && p.x < 15);
337                 assertTrue("y of intermediate point has reasonable value", p.y > 0 && p.y < 15);
338             }
339         }
340         for (int n = -1; n <= 1; n++)
341         {
342             try
343             {
344                 Bezier.cubic(n, from, control1, control2, to);
345             }
346             catch (DrawRuntimeException e)
347             {
348                 // Ignore expected exception
349             }
350         }
351         for (int n : new int[] {2, 3, 4, 100})
352         {
353             for (double shape : new double[] {0.5, 1.0, 2.0})
354             {
355                 for (boolean weighted : new boolean[] {false, true})
356                 {
357                     Ray3d start = new Ray3d(from.x, from.y, from.z, Math.PI / 2, Math.PI / 3);
358                     Ray3d end = new Ray3d(to.x, to.y, to.z, Math.PI, 0);
359                     PolyLine3d line = 1.0 == shape ? Bezier.cubic(n, start, end) : Bezier.cubic(n, start, end, shape, weighted);
360                     for (int i = 1; i < line.size() - 1; i++)
361                     {
362                         Point3d p = line.get(i);
363                         // System.out.println(p);
364                         assertTrue("x of intermediate point has reasonable value", p.x > -10 && p.x < 20);
365                         assertTrue("y of intermediate point has reasonable value", p.y > -10 && p.y < 30);
366                         assertTrue("z of intermediate point has reasonable value", p.z > -10 && p.z < 40);
367                     }
368                 }
369             }
370         }
371         // Pity that the value 64 is private in the Bezier class.
372         assertEquals("Number of points is 64", 64, Bezier.cubic(new Ray3d(from.x, from.y, from.z, Math.PI / 2, -Math.PI / 2),
373                 new Ray3d(to.x, to.y, to.z, Math.PI, -Math.PI / 2)).size());
374         assertEquals("Number of points is 64", 64, Bezier.bezier(from, control1, control2, to).size());
375         control1 = new Point3d(5, 0, 10);
376         control2 = new Point3d(0, 5, 20);
377         for (int n : new int[] {2, 3, 4, 100})
378         {
379             PolyLine3d line = Bezier.cubic(n, from, control1, control2, to);
380             assertEquals("from x", from.x, line.getFirst().x, 0);
381             assertEquals("from y", from.y, line.getFirst().y, 0);
382             assertEquals("from z", from.z, line.getFirst().z, 0);
383             assertEquals("to x", to.x, line.getLast().x, 0);
384             assertEquals("to y", to.y, line.getLast().y, 0);
385             assertEquals("to z", to.z, line.getLast().z, 0);
386             for (int i = 0; i < line.size(); i++)
387             {
388                 Point3d p = line.get(i);
389                 // System.out.println(p);
390                 assertTrue("x of intermediate point has reasonable value", p.x > -10 && p.x < 20);
391                 assertTrue("y of intermediate point has reasonable value", p.y > -10 && p.y < 30);
392                 assertTrue("z of intermediate point has reasonable value", p.z > -10 && p.z <= 30);
393             }
394         }
395         for (int n : new int[] {2, 3, 4, 100})
396         {
397             PolyLine3d line = Bezier.cubic(n, new Ray3d(from.x, from.y, from.z, Math.PI / 2, Math.PI / 2),
398                     new Ray3d(to.x, to.y, to.z, 0, Math.PI / 2));
399             for (int i = 0; i < line.size(); i++)
400             {
401                 Point3d p = line.get(i);
402                 // System.out.println(p);
403                 assertTrue("x of intermediate point has reasonable value", p.x > -10 && p.x < 20);
404                 assertTrue("y of intermediate point has reasonable value", p.y > -10 && p.y < 30);
405                 assertTrue("z of intermediate point has reasonable value", p.z > -10 && p.z <= 30);
406             }
407         }
408     }
409 
410     /**
411      * Test the various exceptions of the 2d methods in the Bezier class.
412      */
413     @Test
414     public void testExceptions2d()
415     {
416         Ray2d ray1 = new Ray2d(2, 3, 4);
417         Ray2d ray2 = new Ray2d(2, 3, 5);
418         Ray2d ray3 = new Ray2d(4, 5, 6);
419         Point2d cp1 = new Point2d(2.5, 13.5);
420         Point2d cp2 = new Point2d(3.5, 14.5);
421         try
422         {
423             Bezier.cubic(null, ray2);
424             fail("null should have thrown a NullPointerException");
425         }
426         catch (NullPointerException npe)
427         {
428             // Ignore expected exception
429         }
430 
431         try
432         {
433             Bezier.cubic(ray1, null);
434             fail("null should have thrown a NullPointerException");
435         }
436         catch (NullPointerException npe)
437         {
438             // Ignore expected exception
439         }
440 
441         try
442         {
443             Bezier.cubic(ray1, ray2);
444             fail("Coinciding start and end points should have thrown a DrawRuntimeException");
445         }
446         catch (DrawRuntimeException dre)
447         {
448             // Ignore expected exception
449         }
450 
451         try
452         {
453             Bezier.cubic(Bezier.DEFAULT_BEZIER_SIZE, ray1, ray3, -1);
454             fail("Illegal shape value should have thrown a DrawRuntimeException");
455         }
456         catch (DrawRuntimeException dre)
457         {
458             // Ignore expected exception
459         }
460 
461         try
462         {
463             Bezier.cubic(Bezier.DEFAULT_BEZIER_SIZE, ray1, ray3, Double.NaN);
464             fail("Illegal shape value should have thrown a DrawRuntimeException");
465         }
466         catch (DrawRuntimeException dre)
467         {
468             // Ignore expected exception
469         }
470 
471         try
472         {
473             Bezier.cubic(Bezier.DEFAULT_BEZIER_SIZE, ray1, ray3, Double.POSITIVE_INFINITY);
474             fail("Illegal shape value should have thrown a DrawRuntimeException");
475         }
476         catch (DrawRuntimeException dre)
477         {
478             // Ignore expected exception
479         }
480 
481         PolyLine2d result = Bezier.bezier(2, ray1, ray3); // Should succeed
482         assertEquals("size should be 2", 2, result.size());
483         assertEquals("start of result is at start", 0, ray1.distanceSquared(result.getFirst()), 0);
484         assertEquals("end of result is at start", 0, ray3.distanceSquared(result.getLast()), 0);
485         result = Bezier.bezier(ray1, ray3); // Should succeed
486         assertEquals("size should be default", Bezier.DEFAULT_BEZIER_SIZE, result.size());
487         assertEquals("start of result is at start", 0, ray1.distanceSquared(result.getFirst()), 0);
488         assertEquals("end of result is at start", 0, ray3.distanceSquared(result.getLast()), 0);
489         try
490         {
491             Bezier.bezier(1, ray1, ray3);
492             fail("size smaller than 2 should have thrown a DrawRuntimeException");
493         }
494         catch (DrawRuntimeException dre)
495         {
496             // Ignore expected exception
497         }
498 
499         try
500         {
501             Bezier.bezier(ray1);
502             fail("cannot make a Bezier from only one point; should have thrown a DrawRuntimeException");
503         }
504         catch (DrawRuntimeException dre)
505         {
506             // Ignore expected exception
507         }
508 
509         result = Bezier.cubic(2, ray1, cp1, cp2, ray3);
510         assertEquals("size should be 2", 2, result.size());
511         assertEquals("start of result is at start", 0, ray1.distanceSquared(result.getFirst()), 0);
512         assertEquals("end of result is at start", 0, ray3.distanceSquared(result.getLast()), 0);
513         result = Bezier.cubic(4, ray1, cp1, cp2, ray3);
514         assertEquals("size should be 4", 4, result.size());
515         assertEquals("start of result is at start", 0, ray1.distanceSquared(result.getFirst()), 0);
516         assertEquals("end of result is at start", 0, ray3.distanceSquared(result.getLast()), 0);
517 
518         try
519         {
520             Bezier.cubic(1, ray1, cp1, cp2, ray3);
521             fail("Cannot construct a Bezier approximation that has only one point");
522         }
523         catch (DrawRuntimeException dre)
524         {
525             // Ignore expected exception
526         }
527 
528     }
529 
530     /**
531      * Test the various exceptions of the 3d methods in the Bezier class.
532      */
533     @Test
534     public void testExceptions3d()
535     {
536         Ray3d ray1 = new Ray3d(2, 3, 4, 5, 6, 7);
537         Ray3d ray2 = new Ray3d(2, 3, 4, 7, 9, 11);
538         Ray3d ray3 = new Ray3d(4, 5, 6, 1, 2, 3);
539         Point3d cp1 = new Point3d(2.5, 13.5, 7);
540         Point3d cp2 = new Point3d(3.5, 14.5, 9);
541         try
542         {
543             Bezier.cubic(null, ray2);
544             fail("null should have thrown a NullPointerException");
545         }
546         catch (NullPointerException npe)
547         {
548             // Ignore expected exception
549         }
550 
551         try
552         {
553             Bezier.cubic(ray1, null);
554             fail("null should have thrown a NullPointerException");
555         }
556         catch (NullPointerException npe)
557         {
558             // Ignore expected exception
559         }
560 
561         try
562         {
563             Bezier.cubic(ray1, ray2);
564             fail("Coinciding start and end points should have thrown a DrawRuntimeException");
565         }
566         catch (DrawRuntimeException dre)
567         {
568             // Ignore expected exception
569         }
570 
571         try
572         {
573             Bezier.cubic(Bezier.DEFAULT_BEZIER_SIZE, ray1, ray3, -1);
574             fail("Illegal shape value should have thrown a DrawRuntimeException");
575         }
576         catch (DrawRuntimeException dre)
577         {
578             // Ignore expected exception
579         }
580 
581         try
582         {
583             Bezier.cubic(Bezier.DEFAULT_BEZIER_SIZE, ray1, ray3, Double.NaN);
584             fail("Illegal shape value should have thrown a DrawRuntimeException");
585         }
586         catch (DrawRuntimeException dre)
587         {
588             // Ignore expected exception
589         }
590 
591         try
592         {
593             Bezier.cubic(Bezier.DEFAULT_BEZIER_SIZE, ray1, ray3, Double.POSITIVE_INFINITY);
594             fail("Illegal shape value should have thrown a DrawRuntimeException");
595         }
596         catch (DrawRuntimeException dre)
597         {
598             // Ignore expected exception
599         }
600 
601         PolyLine3d result = Bezier.bezier(2, ray1, ray3); // Should succeed
602         assertEquals("size should be 2", 2, result.size());
603         assertEquals("start of result is at start", 0, ray1.distanceSquared(result.getFirst()), 0);
604         assertEquals("end of result is at start", 0, ray3.distanceSquared(result.getLast()), 0);
605         result = Bezier.bezier(ray1, ray3); // Should succeed
606         assertEquals("size should be default", Bezier.DEFAULT_BEZIER_SIZE, result.size());
607         assertEquals("start of result is at start", 0, ray1.distanceSquared(result.getFirst()), 0);
608         assertEquals("end of result is at start", 0, ray3.distanceSquared(result.getLast()), 0);
609         try
610         {
611             Bezier.bezier(1, ray1, ray3);
612             fail("size smaller than 2 should have thrown a DrawRuntimeException");
613         }
614         catch (DrawRuntimeException dre)
615         {
616             // Ignore expected exception
617         }
618 
619         try
620         {
621             Bezier.bezier(ray1);
622             fail("cannot make a Bezier from only one point; should have thrown a DrawRuntimeException");
623         }
624         catch (DrawRuntimeException dre)
625         {
626             // Ignore expected exception
627         }
628 
629         result = Bezier.cubic(2, ray1, cp1, cp2, ray3);
630         assertEquals("size should be 2", 2, result.size());
631         assertEquals("start of result is at start", 0, ray1.distanceSquared(result.getFirst()), 0);
632         assertEquals("end of result is at start", 0, ray3.distanceSquared(result.getLast()), 0);
633         result = Bezier.cubic(4, ray1, cp1, cp2, ray3);
634         assertEquals("size should be 4", 4, result.size());
635         assertEquals("start of result is at start", 0, ray1.distanceSquared(result.getFirst()), 0);
636         assertEquals("end of result is at start", 0, ray3.distanceSquared(result.getLast()), 0);
637 
638         try
639         {
640             Bezier.cubic(1, ray1, cp1, cp2, ray3);
641             fail("Cannot construct a Bezier approximation that has only one point");
642         }
643         catch (DrawRuntimeException dre)
644         {
645             // Ignore expected exception
646         }
647 
648     }
649 
650 }