View Javadoc
1   package org.opentrafficsim.core.geometry;
2   
3   /**
4    * Utility class to create clothoid lines, in particular the Fresnel integral based on:
5    * <ul>
6    * <li>W.J. Cody (1968) Chebyshev approximations for the Fresnel integrals. Mathematics of Computation, Vol. 22, Issue 102, pp.
7    * 450–453.</li>
8    * </ul>
9    * <p>
10   * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
11   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
12   * </p>
13   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
14   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
15   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
16   * @see <a href="https://www.ams.org/journals/mcom/1968-22-102/S0025-5718-68-99871-2/S0025-5718-68-99871-2.pdf">Cody (1968)</a>
17   *      (note: tables with values not included in pdf)
18   * @param c C value of Fresnel integral
19   * @param s S value of Fresnel integral
20   */
21  public record Fresnel(double c, double s)
22  {
23  
24      // {@formatter:off}
25      /** Numerator coefficients to calculate C(t) in region 1. */
26      private static final double[] CN1 = new double[] {
27              9.999999999999999421E-01,
28              -1.994608988261842706E-01, 
29              1.761939525434914045E-02,
30              -5.280796513726226960E-04, 
31              5.477113856826871660E-06
32      };
33  
34      /** Denominator coefficients to calculate C(t) in region 1. */
35      private static final double[] CD1 = new double[] {
36              1.000000000000000000E+00,
37              4.727921120104532689E-02,
38              1.099572150256418851E-03,
39              1.552378852769941331E-05,
40              1.189389014228757184E-07
41      };
42  
43      /** Numerator coefficients to calculate C(t) in region 2. */
44      private static final double[] CN2 = new double[] {
45              1.00000000000111043640E+00,
46              -2.07073360335323894245E-01,
47              1.91870279431746926505E-02,
48              -6.71376034694922109230E-04,
49              1.02365435056105864908E-05,
50              -5.68293310121870728343E-08
51      };
52  
53      /** Denominator coefficients to calculate C(t) in region 3. */
54      private static final double[] CD2 = new double[] {
55              1.00000000000000000000E+00,
56              3.96667496952323433510E-02,
57              7.88905245052359907842E-04,
58              1.01344630866749406081E-05,
59              8.77945377892369265356E-08,
60              4.41701374065009620393E-10
61      };
62  
63      /** Numerator coefficients to calculate S(t) in region 1. */
64      private static final double[] SN1 = new double[] {
65              5.2359877559829887021E-01,
66              -7.0748991514452302596E-02,
67              3.8778212346368287939E-03,
68              -8.4555728435277680591E-05,
69              6.7174846662514086196E-07
70      };
71  
72      /** Denominator coefficients to calculate S(t) in region 1. */
73      private static final double[] SD1 = new double[] {
74              1.0000000000000000000E+00,
75              4.1122315114238422205E-02,
76              8.1709194215213447204E-04,
77              9.6269087593903403370E-06,
78              5.9528122767840998345E-08
79      };
80  
81      /** Numerator coefficients to calculate S(t) in region 2. */
82      private static final double[] SN2 = new double[] {
83              5.23598775598344165913E-01,
84              -7.37766914010191323867E-02,
85              4.30730526504366510217E-03,
86              -1.09540023911434994566E-04,
87              1.28531043742724820610E-06,
88              -5.76765815593088804567E-09
89      };
90  
91      /** Denominator coefficients to calculate S(t) in region 2. */
92      private static final double[] SD2 = new double[] {
93              1.00000000000000000000E+00,
94              3.53398342167472162540E-02,
95              6.18224620195473216538E-04,
96              6.87086265718620117905E-06,
97              5.03090581246612375866E-08,
98              2.05539124458579596075E-10
99      };
100 
101     /** Numerator coefficients to calculate f(t) in region 3. */
102     private static final double[] FN3 = new double[] {
103             3.1830975293580985290E-01,
104             1.2226000551672961219E+01,
105             1.2924886131901657025E+02,
106             4.3886367156695547655E+02,
107             4.1466722177958961672E+02,
108             5.6771463664185116454E+01
109     };
110 
111     /** Denominator coefficients to calculate f(t) in region 3. */
112     private static final double[] FD3 = new double[] {
113             1.0000000000000000000E+00,
114             3.8713003365583442831E+01,
115             4.1674359830705629745E+02,
116             1.4740030733966610568E+03,
117             1.5371675584895759916E+03,
118             2.9113088788847831515E+02
119     };
120 
121     /** Numerator coefficients to calculate f(t) in region 4. */
122     private static final double[] FN4 = new double[] {
123             3.183098818220169217E-01,
124             1.958839410219691002E+01,
125             3.398371349269842400E+02,
126             1.930076407867157531E+03,
127             3.091451615744296552E+03,
128             7.177032493651399590E+02
129     };
130 
131     /** Denominator coefficients to calculate f(t) in region 4. */
132     private static final double[] FD4 = new double[] {
133             1.000000000000000000E+00,
134             6.184271381728873709E+01,
135             1.085350675006501251E+03,
136             6.337471558511437898E+03,
137             1.093342489888087888E+04,
138             3.361216991805511494E+03
139     };
140 
141     /** Numerator coefficients to calculate f(t) in region 5. */
142     private static final double[] FN5 = new double[] {
143             -9.675460329952532343E-02,
144             -2.431275407194161683E+01,
145             -1.947621998306889176E+03,
146             -6.059852197160773639E+04,
147             -7.076806952837779823E+05,
148             -2.417656749061154155E+06,
149             -7.834914590078311336E+05
150     };
151 
152     /** Denominator coefficients to calculate f(t) in region 5. */
153     private static final double[] FD5 = new double[] {
154             1.000000000000000000E+00,
155             2.548289012949732752E+02,
156             2.099761536857815105E+04,
157             6.924122509827708985E+05,
158             9.178823229918143780E+06,
159             4.292733255630186679E+07,
160             4.803294184260528342E+07
161     };
162 
163     /** Numerator coefficients to calculate g(t) in region 3. */
164     private static final double[] GN3 = new double[] {
165             1.013206188102747985E-01,
166             4.445338275505123778E+00,
167             5.311228134809894481E+01,
168             1.991828186789025318E+02,
169             1.962320379716626191E+02,
170             2.054214324985006303E+01
171     };
172 
173     /** Denominator coefficients to calculate g(t) in region 3. */
174     private static final double[] GD3 = new double[] {
175             1.000000000000000000E+00,
176             4.539250196736893605E+01,
177             5.835905757164290666E+02,
178             2.544731331818221034E+03,
179             3.481121478565452837E+03,
180             1.013794833960028555E+03
181     };
182 
183     /** Numerator coefficients to calculate g(t) in region 4. */
184     private static final double[] GN4 = new double[] {
185             1.01321161761804586E-01,
186             7.11205001789782823E+00,
187             1.40959617911315524E+02,
188             9.08311749529593938E+02,
189             1.59268006085353864E+03,
190             3.13330163068755950E+02
191     };
192 
193     /** Denominator coefficients to calculate g(t) in region 4. */
194     private static final double[] GD4 = new double[] {
195             1.00000000000000000E+00,
196             7.17128596939302198E+01,
197             1.49051922797329229E+03,
198             1.06729678030583897E+04,
199             2.41315567213369742E+04,
200             1.15149832376260604E+04
201     };
202 
203     /** Numerator coefficients to calculate g(t) in region 5. */
204     private static final double[] GN5 = new double[] {
205             -1.53989733819769316E-01,
206             -4.31710157823357568E+01,
207             -3.87754141746378493E+03,
208             -1.35678867813756347E+05,
209             -1.77758950838029676E+06,
210             -6.66907061668636416E+06,
211             -1.72590224654836845E+06
212     };
213     
214     /** Denominator coefficients to calculate g(t) in region 5. */
215     private static final double[] GD5 = new double[] {
216             1.00000000000000000E+00,
217             2.86733194975899483E+02,
218             2.69183180396242536E+04,
219             1.02878693056687506E+06,
220             1.62095600500231646E+07,
221             9.38695862531635179E+07,
222             1.40622441123580005E+08
223     };
224     // {@formatter:on}
225 
226     /**
227      * Approximate the Fresnel integral. The method used is based on Cody (1968). This method applies rational approximation to
228      * approximate the clothoid. For clothoid rotation beyond 1.6 rad, this occurs in polar form. The polar form is robust for
229      * arbitrary large numbers, unlike polynomial expansion, and will at a large threshold converge to (0.5, 0.5). There are 5
230      * regions with different fitted values for the rational approximations, in Cartesian or polar form.<br>
231      * <br>
232      * W.J. Cody (1968) Chebyshev approximations for the Fresnel integrals. Mathematics of Computation, Vol. 22, Issue 102, pp.
233      * 450–453.
234      * @param x length along the standard Fresnel integral (no scaling).
235      * @return array with two double values c and s
236      * @see <a href="https://www.ams.org/journals/mcom/1968-22-102/S0025-5718-68-99871-2/S0025-5718-68-99871-2.pdf">Cody
237      *      (1968)</a>
238      */
239     public static Fresnel integral(final double x)
240     {
241         final double t = Math.abs(x);
242         double cc, ss;
243         if (t < 1.2)
244         {
245             cc = t * ratioEval(t, CN1, +1) / ratioEval(t, CD1, +1);
246             ss = t * t * t * ratioEval(t, SN1, +1) / ratioEval(t, SD1, +1);
247         }
248         else if (t < 1.6)
249         {
250             cc = t * ratioEval(t, CN2, +1) / ratioEval(t, CD2, +1);
251             ss = t * t * t * ratioEval(t, SN2, +1) / ratioEval(t, SD2, +1);
252         }
253         else if (t < 1.9)
254         {
255             double pitt2 = Math.PI * t * t / 2;
256             double sinpitt2 = Math.sin(pitt2);
257             double cospitt2 = Math.cos(pitt2);
258             double ft = (1 / t) * ratioEval(t, FN3, -1) / ratioEval(t, FD3, -1);
259             double gt = (1 / (t * t * t)) * ratioEval(t, GN3, -1) / ratioEval(t, GD3, -1);
260             cc = .5 + ft * sinpitt2 - gt * cospitt2;
261             ss = .5 - ft * cospitt2 - gt * sinpitt2;
262         }
263         else if (t < 2.4)
264         {
265             double pitt2 = Math.PI * t * t / 2;
266             double sinpitt2 = Math.sin(pitt2);
267             double cospitt2 = Math.cos(pitt2);
268             double tinv = 1 / t;
269             double tttinv = tinv * tinv * tinv;
270             double ft = tinv * ratioEval(t, FN4, -1) / ratioEval(t, FD4, -1);
271             double gt = tttinv * ratioEval(t, GN4, -1) / ratioEval(t, GD4, -1);
272             cc = .5 + ft * sinpitt2 - gt * cospitt2;
273             ss = .5 - ft * cospitt2 - gt * sinpitt2;
274         }
275         else
276         {
277             double pitt2 = Math.PI * t * t / 2;
278             double sinpitt2 = Math.sin(pitt2);
279             double cospitt2 = Math.cos(pitt2);
280             double piinv = 1 / Math.PI;
281             double tinv = 1 / t;
282             double tttinv = tinv * tinv * tinv;
283             double ttttinv = tttinv * tinv;
284             double ft = tinv * (piinv + (ttttinv * ratioEval(t, FN5, -1) / ratioEval(t, FD5, -1)));
285             double gt = tttinv * ((piinv * piinv) + (ttttinv * ratioEval(t, GN5, -1) / ratioEval(t, GD5, -1)));
286             cc = .5 + ft * sinpitt2 - gt * cospitt2;
287             ss = .5 - ft * cospitt2 - gt * sinpitt2;
288         }
289         if (x < 0)
290         {
291             cc = -cc;
292             ss = -ss;
293         }
294 
295         return new Fresnel(cc, ss);
296     }
297 
298     /**
299      * Evaluate numerator or denominator of rational approximation.
300      * @param t value along the clothoid.
301      * @param coef rational approximation coefficients.
302      * @param sign sign of exponent, +1 for Cartesian rational approximation, -1 for polar approximation.
303      * @return numerator or denominator of rational approximation.
304      */
305     private static double ratioEval(final double t, final double[] coef, final double sign)
306     {
307         double value = 0;
308         for (int s = 0; s < coef.length; s++)
309         {
310             value += coef[s] * Math.pow(t, sign * 4 * s);
311         }
312         return value;
313     }
314 
315 }