View Javadoc
1   package org.opentrafficsim.core.geometry;
2   
3   import java.util.Arrays;
4   
5   import org.djutils.draw.point.Point2d;
6   import org.djutils.exceptions.Throw;
7   
8   /**
9    * Continuous definition of a Bezier. Note that this class does not implement {@code ContinuousLine}. This class is simply a
10   * helper class for (and a super of) {@code ContinuousBezierCubic}, which uses this class to determine curvature, offset lines,
11   * etc.
12   * <p>
13   * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
14   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
15   * </p>
16   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
17   * @see <a href="https://pomax.github.io/bezierinfo/">Bezier info</a>
18   */
19  public class ContinuousBezier
20  {
21  
22      /** T values of numerical approach of Legendre-Gauss to determine Bezier length. */
23      private static final double[] T =
24              new double[] {-0.0640568928626056260850430826247450385909, 0.0640568928626056260850430826247450385909,
25                      -0.1911188674736163091586398207570696318404, 0.1911188674736163091586398207570696318404,
26                      -0.3150426796961633743867932913198102407864, 0.3150426796961633743867932913198102407864,
27                      -0.4337935076260451384870842319133497124524, 0.4337935076260451384870842319133497124524,
28                      -0.5454214713888395356583756172183723700107, 0.5454214713888395356583756172183723700107,
29                      -0.6480936519369755692524957869107476266696, 0.6480936519369755692524957869107476266696,
30                      -0.7401241915785543642438281030999784255232, 0.7401241915785543642438281030999784255232,
31                      -0.8200019859739029219539498726697452080761, 0.8200019859739029219539498726697452080761,
32                      -0.8864155270044010342131543419821967550873, 0.8864155270044010342131543419821967550873,
33                      -0.9382745520027327585236490017087214496548, 0.9382745520027327585236490017087214496548,
34                      -0.9747285559713094981983919930081690617411, 0.9747285559713094981983919930081690617411,
35                      -0.9951872199970213601799974097007368118745, 0.9951872199970213601799974097007368118745};
36  
37      /** C values of numerical approach of Legendre-Gauss to determine Bezier length. */
38      private static final double[] C =
39              new double[] {0.1279381953467521569740561652246953718517, 0.1279381953467521569740561652246953718517,
40                      0.1258374563468282961213753825111836887264, 0.1258374563468282961213753825111836887264,
41                      0.121670472927803391204463153476262425607, 0.121670472927803391204463153476262425607,
42                      0.1155056680537256013533444839067835598622, 0.1155056680537256013533444839067835598622,
43                      0.1074442701159656347825773424466062227946, 0.1074442701159656347825773424466062227946,
44                      0.0976186521041138882698806644642471544279, 0.0976186521041138882698806644642471544279,
45                      0.086190161531953275917185202983742667185, 0.086190161531953275917185202983742667185,
46                      0.0733464814110803057340336152531165181193, 0.0733464814110803057340336152531165181193,
47                      0.0592985849154367807463677585001085845412, 0.0592985849154367807463677585001085845412,
48                      0.0442774388174198061686027482113382288593, 0.0442774388174198061686027482113382288593,
49                      0.0285313886289336631813078159518782864491, 0.0285313886289336631813078159518782864491,
50                      0.0123412297999871995468056670700372915759, 0.0123412297999871995468056670700372915759};
51  
52      /** The shape points. */
53      protected Point2d[] points;
54  
55      /**
56       * Create a Bezier of any order.
57       * @param points Point2d... shape points.
58       */
59      public ContinuousBezier(final Point2d... points)
60      {
61          Throw.whenNull(points, "Points may not be null.");
62          Throw.when(points.length < 2, IllegalArgumentException.class, "Minimum number of points is 2.");
63          for (Point2d point : points)
64          {
65              Throw.whenNull(point, "One of the points is null.");
66          }
67          this.points = points;
68      }
69  
70      /**
71       * Returns the derivative for a Bezier, which is a Bezier of 1 order lower.
72       * @return ContinuousBezier; derivative Bezier.
73       */
74      public ContinuousBezier derivative()
75      {
76          Throw.when(this.points.length < 2, IllegalStateException.class,
77                  "Requesting derivative on Bezier with less than 2 points");
78          int n = this.points.length - 1;
79          Point2d[] derivativePoints = new Point2d[n];
80          for (int i = 0; i < n; i++)
81          {
82              derivativePoints[i] =
83                      new Point2d(n * (this.points[i + 1].x - this.points[i].x), n * (this.points[i + 1].y - this.points[i].y));
84          }
85          return new ContinuousBezier(derivativePoints);
86      }
87  
88      /**
89       * Returns the estimated length using the method of numerical approach of Legendre-Gauss, which is quite accurate.
90       * @return double; estimated length.
91       */
92      public double length()
93      {
94          double len = 0.0;
95          for (int i = 0; i < T.length; i++)
96          {
97              double t = 0.5 * T[i] + 0.5;
98              Point2d p = derivative().at(t);
99              len += C[i] * Math.hypot(p.x, p.y);
100         }
101         len *= 0.5;
102         return len;
103     }
104 
105     /**
106      * Return the point for the given t value.
107      * @param t double; t value, moving from 0 to 1 along the Bezier.
108      * @return Point2d; point of the Bezier at t.
109      */
110     public Point2d at(final double t)
111     {
112         double[] x = new double[this.points.length];
113         double[] y = new double[this.points.length];
114         for (int j = 0; j < this.points.length; j++)
115         {
116             x[j] = this.points[j].x;
117             y[j] = this.points[j].y;
118         }
119         return new Point2d(Bezier.Bn(t, x), Bezier.Bn(t, y));
120     }
121 
122     /**
123      * Returns the curvature at the given t value.
124      * @param t double; t value, moving from 0 to 1 along the Bezier.
125      * @return double curvature at the given t value.
126      */
127     public double curvature(final double t)
128     {
129         ContinuousBezier der = derivative();
130         Point2d d = der.at(t);
131         double denom = Math.pow(d.x * d.x + d.y * d.y, 3.0 / 2.0);
132         if (denom == 0.0)
133         {
134             return Double.POSITIVE_INFINITY;
135         }
136         Point2d dd = der.derivative().at(t);
137         double numer = d.x * dd.y - dd.x * d.y;
138         return numer / denom;
139     }
140 
141     /** {@inheritDoc} */
142     @Override
143     public String toString()
144     {
145         return "ContinuousBezier [points=" + Arrays.toString(this.points) + "]";
146     }
147 
148 }