Bezier.java

  1. package org.opentrafficsim.core.geometry;

  2. import nl.tudelft.simulation.language.d3.DirectedPoint;

  3. /**
  4.  * Generation of B&eacute;zier curves. <br>
  5.  * The class implements the cubic(...) method to generate a cubic B&eacute;zier curve using the following formula: B(t) = (1 -
  6.  * t)<sup>3</sup>P<sub>0</sub> + 3t(1 - t)<sup>2</sup> P<sub>1</sub> + 3t<sup>2</sup> (1 - t) P<sub>2</sub> + t<sup>3</sup>
  7.  * P<sub>3</sub> where P<sub>0</sub> and P<sub>3</sub> are the end points, and P<sub>1</sub> and P<sub>2</sub> the control
  8.  * points. <br>
  9.  * For a smooth movement, one of the standard implementations if the cubic(...) function offered is the case where P<sub>1</sub>
  10.  * is positioned halfway between P<sub>0</sub> and P<sub>3</sub> starting from P<sub>0</sub> in the direction of P<sub>3</sub>,
  11.  * and P<sub>2</sub> is positioned halfway between P<sub>3</sub> and P<sub>0</sub> starting from P<sub>3</sub> in the direction
  12.  * of P<sub>0</sub>.<br>
  13.  * Finally, an n-point generalization of the B&eacute;zier curve is implemented with the bezier(...) function.
  14.  * <p>
  15.  * Copyright (c) 2013-2016 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
  16.  * BSD-style license. See <a href="http://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
  17.  * </p>
  18.  * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
  19.  * initial version Nov 14, 2015 <br>
  20.  * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
  21.  * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
  22.  */
  23. public final class Bezier
  24. {
  25.     /** The default number of points to use to construct a B&eacute;zier curve. */
  26.     private static final int DEFAULT_NUM_POINTS = 64;

  27.     /** Cached factorial values. */
  28.     private static long[] fact = new long[]{1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L,
  29.         39916800L, 479001600L, 6227020800L, 87178291200L, 1307674368000L, 20922789888000L, 355687428096000L,
  30.         6402373705728000L, 121645100408832000L, 2432902008176640000L};

  31.     /** Utility class. */
  32.     private Bezier()
  33.     {
  34.         // do not instantiate
  35.     }

  36.     /**
  37.      * Construct a cubic B&eacute;zier curve from start to end with two control points.
  38.      * @param numPoints the number of points for the B&eacute;zier curve
  39.      * @param start the start point of the B&eacute;zier curve
  40.      * @param control1 the first control point
  41.      * @param control2 the second control point
  42.      * @param end the end point of the B&eacute;zier curve
  43.      * @return a cubic B&eacute;zier curve between start and end, with the two provided control points
  44.      * @throws OTSGeometryException in case the number of points is less than 2 or the B&eacute;zier curve could not be
  45.      *             constructed
  46.      */
  47.     public static OTSLine3D cubic(final int numPoints, final OTSPoint3D start, final OTSPoint3D control1,
  48.         final OTSPoint3D control2, final OTSPoint3D end) throws OTSGeometryException
  49.     {
  50.         OTSPoint3D[] points = new OTSPoint3D[numPoints];
  51.         for (int n = 0; n < numPoints; n++)
  52.         {
  53.             double t = n / (numPoints - 1.0);
  54.             double x = B3(t, start.x, control1.x, control2.x, end.x);
  55.             double y = B3(t, start.y, control1.y, control2.y, end.y);
  56.             double z = B3(t, start.z, control1.z, control2.z, end.z);
  57.             points[n] = new OTSPoint3D(x, y, z);
  58.         }
  59.         return new OTSLine3D(points);
  60.     }

  61.     /**
  62.      * Construct a cubic B&eacute;zier curve from start to end with two generated control points at half the distance between
  63.      * start and end. The z-value is interpolated in a linear way.
  64.      * @param numPoints the number of points for the B&eacute;zier curve
  65.      * @param start the directed start point of the B&eacute;zier curve
  66.      * @param end the directed end point of the B&eacute;zier curve
  67.      * @return a cubic B&eacute;zier curve between start and end, with the two provided control points
  68.      * @throws OTSGeometryException in case the number of points is less than 2 or the B&eacute;zier curve could not be
  69.      *             constructed
  70.      */
  71.     public static OTSLine3D cubic(final int numPoints, final DirectedPoint start, final DirectedPoint end)
  72.         throws OTSGeometryException
  73.     {
  74.         double distance2 =
  75.             Math.sqrt((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / 2.0;
  76.         OTSPoint3D control1 =
  77.             new OTSPoint3D(start.x + distance2 * Math.cos(start.getRotZ()), start.y + distance2
  78.                 * Math.sin(start.getRotZ()), start.z);
  79.         OTSPoint3D control2 =
  80.             new OTSPoint3D(end.x - distance2 * Math.cos(end.getRotZ()), end.y - distance2 * Math.sin(end.getRotZ()),
  81.                 end.z);
  82.         // return cubic(numPoints, new OTSPoint3D(start), control1, control2, new OTSPoint3D(end));
  83.         return bezier(numPoints, new OTSPoint3D(start), control1, control2, new OTSPoint3D(end));
  84.     }

  85.     /**
  86.      * Construct a cubic B&eacute;zier curve from start to end with two generated control points at half the distance between
  87.      * start and end. The z-value is interpolated in a linear way.
  88.      * @param start the directed start point of the B&eacute;zier curve
  89.      * @param end the directed end point of the B&eacute;zier curve
  90.      * @return a cubic B&eacute;zier curve between start and end, with the two provided control points
  91.      * @throws OTSGeometryException in case the number of points is less than 2 or the B&eacute;zier curve could not be
  92.      *             constructed
  93.      */
  94.     public static OTSLine3D cubic(final DirectedPoint start, final DirectedPoint end) throws OTSGeometryException
  95.     {
  96.         return cubic(DEFAULT_NUM_POINTS, start, end);
  97.     }

  98.     /**
  99.      * Calculate the cubic B&eacute;zier point with B(t) = (1 - t)<sup>3</sup>P<sub>0</sub> + 3t(1 - t)<sup>2</sup>
  100.      * P<sub>1</sub> + 3t<sup>2</sup> (1 - t) P<sub>2</sub> + t<sup>3</sup> P<sub>3</sub>.
  101.      * @param t the fraction
  102.      * @param p0 the first point of the curve
  103.      * @param p1 the first control point
  104.      * @param p2 the second control point
  105.      * @param p3 the end point of the curve
  106.      * @return the cubic bezier value B(t)
  107.      */
  108.     @SuppressWarnings("checkstyle:methodname")
  109.     private static double B3(final double t, final double p0, final double p1, final double p2, final double p3)
  110.     {
  111.         double t2 = t * t;
  112.         double t3 = t2 * t;
  113.         double m = (1.0 - t);
  114.         double m2 = m * m;
  115.         double m3 = m2 * m;
  116.         return m3 * p0 + 3.0 * t * m2 * p1 + 3.0 * t2 * m * p2 + t3 * p3;
  117.     }

  118.     /**
  119.      * Construct a B&eacute;zier curve of degree n.
  120.      * @param numPoints the number of points for the B&eacute;zier curve to be constructed
  121.      * @param points the points of the curve, where the first and last are begin and end point, and the intermediate ones are
  122.      *            control points. There should be at least two points.
  123.      * @return the B&eacute;zier value B(t) of degree n, where n is the number of points in the array
  124.      * @throws OTSGeometryException in case the number of points is less than 2 or the B&eacute;zier curve could not be
  125.      *             constructed
  126.      */
  127.     public static OTSLine3D bezier(final int numPoints, final OTSPoint3D... points) throws OTSGeometryException
  128.     {
  129.         OTSPoint3D[] result = new OTSPoint3D[numPoints];
  130.         double[] px = new double[points.length];
  131.         double[] py = new double[points.length];
  132.         double[] pz = new double[points.length];
  133.         for (int i = 0; i < points.length; i++)
  134.         {
  135.             px[i] = points[i].x;
  136.             py[i] = points[i].y;
  137.             pz[i] = points[i].z;
  138.         }
  139.         for (int n = 0; n < numPoints; n++)
  140.         {
  141.             double t = n / (numPoints - 1.0);
  142.             double x = Bn(t, px);
  143.             double y = Bn(t, py);
  144.             double z = Bn(t, pz);
  145.             result[n] = new OTSPoint3D(x, y, z);
  146.         }
  147.         return new OTSLine3D(result);
  148.     }

  149.     /**
  150.      * Construct a B&eacute;zier curve of degree n.
  151.      * @param points the points of the curve, where the first and last are begin and end point, and the intermediate ones are
  152.      *            control points. There should be at least two points.
  153.      * @return the B&eacute;zier value B(t) of degree n, where n is the number of points in the array
  154.      * @throws OTSGeometryException in case the number of points is less than 2 or the B&eacute;zier curve could not be
  155.      *             constructed
  156.      */
  157.     public static OTSLine3D bezier(final OTSPoint3D... points) throws OTSGeometryException
  158.     {
  159.         return bezier(DEFAULT_NUM_POINTS, points);
  160.     }

  161.     /**
  162.      * Calculate the B&eacute;zier point of degree n, with B(t) = Sum(i = 0..n) [C(n, i) * (1 - t)<sup>n-i</sup> t<sup>i</sup>
  163.      * P<sub>i</sub>], where C(n, k) is the binomial coefficient defined by n! / ( k! (n-k)! ), ! being the factorial operator.
  164.      * @param t the fraction
  165.      * @param p the points of the curve, where the first and last are begin and end point, and the intermediate ones are control
  166.      *            points
  167.      * @return the B&eacute;zier value B(t) of degree n, where n is the number of points in the array
  168.      */
  169.     @SuppressWarnings("checkstyle:methodname")
  170.     private static double Bn(final double t, final double... p)
  171.     {
  172.         double b = 0.0;
  173.         double m = (1.0 - t);
  174.         int n = p.length - 1;
  175.         double fn = factorial(n);
  176.         for (int i = 0; i <= n; i++)
  177.         {
  178.             double c = fn / (factorial(i) * (factorial(n - i)));
  179.             b += c * Math.pow(m, n - i) * Math.pow(t, i) * p[i];
  180.         }
  181.         return b;
  182.     }

  183.     /**
  184.      * Calculate factorial(k), which is k * (k-1) * (k-2) * ... * 1. For factorials up to 20, a lookup table is used.
  185.      * @param k the parameter
  186.      * @return factorial(k)
  187.      */
  188.     private static double factorial(final int k)
  189.     {
  190.         if (k < fact.length)
  191.         {
  192.             return fact[k];
  193.         }
  194.         double f = 1;
  195.         for (int i = 2; i <= k; i++)
  196.         {
  197.             f = f * i;
  198.         }
  199.         return f;
  200.     }

  201.     /**
  202.      * @param args args
  203.      * @throws OTSGeometryException ne
  204.      */
  205.     public static void main(final String[] args) throws OTSGeometryException
  206.     {
  207.         // DirectedPoint s = new DirectedPoint(0, 0, 0, 0, 0, -Math.PI/2.0);
  208.         // DirectedPoint e = new DirectedPoint(10, 10, 20, 0, 0, Math.PI);
  209.         // OTSLine3D b1 = Bezier.cubic(s, e);
  210.         // for (OTSPoint3D p : b1.getPoints())
  211.         // {
  212.         // System.out.println(p.x + "\t" + p.y + "\t" + p.z);
  213.         // }

  214.         OTSPoint3D s = new OTSPoint3D(0, 0, 0);
  215.         OTSPoint3D s1 = new OTSPoint3D(10, 0, 0);
  216.         OTSPoint3D m1 = new OTSPoint3D(25, 5, 0);
  217.         OTSPoint3D m2 = new OTSPoint3D(-15, 5, 0);
  218.         OTSPoint3D e0 = new OTSPoint3D(0, 10, 20);
  219.         OTSPoint3D e = new OTSPoint3D(10, 10, 20);
  220.         OTSLine3D b1 = Bezier.bezier(s, s1, m1, m2, e0, e);
  221.         for (OTSPoint3D p : b1.getPoints())
  222.         {
  223.             System.out.println(p.x + "\t" + p.y + "\t" + p.z);
  224.         }

  225.     }
  226. }