1 package org.opentrafficsim.core.geometry;
2
3 import org.djunits.unit.AngleUnit;
4 import org.djunits.unit.LengthUnit;
5 import org.djunits.unit.LinearDensityUnit;
6 import org.djunits.value.vdouble.scalar.Angle;
7 import org.djunits.value.vdouble.scalar.Length;
8 import org.djunits.value.vdouble.scalar.LinearDensity;
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 public final class Clothoid
37 {
38
39 private Clothoid()
40 {
41
42 }
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 static final double[] SN = {
83 -2.99181919401019853726E3,
84 7.08840045257738576863E5,
85 -6.29741486205862506537E7,
86 2.54890880573376359104E9,
87 -4.42979518059697779103E10,
88 3.18016297876567817986E11
89 };
90
91
92 static final double[] SD = {
93 2.81376268889994315696E2,
94 4.55847810806532581675E4,
95 5.17343888770096400730E6,
96 4.19320245898111231129E8,
97 2.24411795645340920940E10,
98 6.07366389490084639049E11
99 };
100
101
102 static final double[] CN = {
103 -4.98843114573573548651E-8,
104 9.50428062829859605134E-6,
105 -6.45191435683965050962E-4,
106 1.88843319396703850064E-2,
107 -2.05525900955013891793E-1,
108 9.99999999999999998822E-1
109 };
110
111
112 static final double[] CD = {
113 3.99982968972495980367E-12,
114 9.15439215774657478799E-10,
115 1.25001862479598821474E-7,
116 1.22262789024179030997E-5,
117 8.68029542941784300606E-4,
118 4.12142090722199792936E-2,
119 1.00000000000000000118E0
120 };
121
122
123 static final double[] FN = {
124 4.21543555043677546506E-1,
125 1.43407919780758885261E-1,
126 1.15220955073585758835E-2,
127 3.45017939782574027900E-4,
128 4.63613749287867322088E-6,
129 3.05568983790257605827E-8,
130 1.02304514164907233465E-10,
131 1.72010743268161828879E-13,
132 1.34283276233062758925E-16,
133 3.76329711269987889006E-20
134 };
135
136
137 static final double[] FD = {
138 7.51586398353378947175E-1,
139 1.16888925859191382142E-1,
140 6.44051526508858611005E-3,
141 1.55934409164153020873E-4,
142 1.84627567348930545870E-6,
143 1.12699224763999035261E-8,
144 3.60140029589371370404E-11,
145 5.88754533621578410010E-14,
146 4.52001434074129701496E-17,
147 1.25443237090011264384E-20
148 };
149
150
151 static final double[] GN = {
152 5.04442073643383265887E-1,
153 1.97102833525523411709E-1,
154 1.87648584092575249293E-2,
155 6.84079380915393090172E-4,
156 1.15138826111884280931E-5,
157 9.82852443688422223854E-8,
158 4.45344415861750144738E-10,
159 1.08268041139020870318E-12,
160 1.37555460633261799868E-15,
161 8.36354435630677421531E-19,
162 1.86958710162783235106E-22
163 };
164
165
166 static final double[] GD = {
167 1.47495759925128324529E0,
168 3.37748989120019970451E-1,
169 2.53603741420338795122E-2,
170 8.14679107184306179049E-4,
171 1.27545075667729118702E-5,
172 1.04314589657571990585E-7,
173 4.60680728146520428211E-10,
174 1.10273215066240270757E-12,
175 1.38796531259578871258E-15,
176 8.39158816283118707363E-19,
177 1.86958710162783236342E-22
178 };
179
180
181
182
183
184
185
186
187 private static double polevl(final double x, final double[] coef)
188 {
189 double result = coef[0];
190 for (int i = 0; i < coef.length; i++)
191 {
192 result = result * x + coef[i];
193 }
194 return result;
195 }
196
197
198
199
200
201
202
203 private static double p1evl(final double x, final double[] coef)
204 {
205 double result = x + coef[0];
206 for (int i = 0; i < coef.length; i++)
207 {
208 result = result * x + coef[i];
209 }
210 return result;
211 }
212
213
214
215
216
217
218 private static double[] fresnel(final double xxa)
219 {
220 final double x = Math.abs(xxa);
221 final double x2 = x * x;
222 double cc, ss;
223
224 if (x2 < 2.5625)
225 {
226 final double t = x2 * x2;
227 ss = x * x2 * polevl(t, SN) / p1evl(t, SD);
228 cc = x * polevl(t, CN) / polevl(t, CD);
229 }
230 else if (x > 36974.0)
231 {
232 cc = 0.5;
233 ss = 0.5;
234 }
235 else
236 {
237 double t = Math.PI * x2;
238 final double u = 1.0 / (t * t);
239 t = 1.0 / t;
240 final double f = 1.0 - u * polevl(u, FN) / p1evl(u, FD);
241 final double g = t * polevl(u, GN) / p1evl(u, GD);
242
243 t = Math.PI * 0.5 * x2;
244 final double c = Math.cos(t);
245 final double s = Math.sin(t);
246 t = Math.PI * x;
247 cc = 0.5 + (f * s - g * c) / t;
248 ss = 0.5 - (f * c + g * s) / t;
249 }
250
251 if (xxa < 0.0)
252 {
253 cc = -cc;
254 ss = -ss;
255 }
256 return new double[]{cc, ss};
257 }
258
259
260
261
262
263
264
265
266 private static double[] odrSpiral(final double s, final double cDot, final double initialCurvature)
267 {
268 double a = Math.sqrt(Math.PI / Math.abs(cDot));
269
270 double[] xy = fresnel(initialCurvature + s / a);
271 return new double[]{xy[0] * a, xy[1] * a * Math.signum(cDot), s * s * cDot * 0.5};
272 }
273
274
275
276
277
278
279
280
281
282
283 private static OTSLine3D clothoid(final double initialCurvature, final double curvatureDerivative,
284 final double length, final int numSegments) throws OTSGeometryException
285 {
286 OTSPoint3D[] points = new OTSPoint3D[numSegments + 1];
287 double[] offset = odrSpiral(initialCurvature / curvatureDerivative, curvatureDerivative, initialCurvature);
288 double sinRot = Math.sin(offset[2]);
289 double cosRot = Math.cos(offset[2]);
290 for (int i = 0; i <= numSegments; i++)
291 {
292 double[] xyd =
293 odrSpiral(i * length / numSegments + initialCurvature / curvatureDerivative, curvatureDerivative,
294 initialCurvature);
295 double dx = xyd[0] - offset[0];
296 double dy = xyd[1] - offset[1];
297 points[i] = new OTSPoint3D(dx * cosRot + dy * sinRot, dy * cosRot - dx * sinRot, 0);
298 }
299 return new OTSLine3D(points);
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317 @SuppressWarnings("checkstyle:parameternumber")
318 private static OTSLine3D clothoid(final double x1, final double y1, final double startElevation,
319 final double startDirection, final double startCurvature, final double endCurvature, final double length,
320 final double endElevation, final int numSegments) throws OTSGeometryException
321 {
322 OTSLine3D result = clothoid(startCurvature, (endCurvature - startCurvature) / length, length, numSegments);
323 double sinRot = Math.sin(startDirection);
324 double cosRot = Math.cos(startDirection);
325 OTSPoint3D[] list = new OTSPoint3D[result.size()];
326 double elevationPerStep = (endElevation - startElevation) / (result.size() - 1);
327 for (int i = 0; i < result.size(); i++)
328 {
329 try
330 {
331 OTSPoint3D p = result.get(i);
332 list[i] =
333 new OTSPoint3D(x1 + cosRot * p.x + sinRot * p.y, y1 + cosRot * p.y - sinRot * p.x, startElevation
334 + i * elevationPerStep);
335 }
336 catch (OTSGeometryException ge)
337 {
338
339 System.err.println("CANNOT HAPPEN; if you see this; let us know what you did.");
340 }
341 }
342 return new OTSLine3D(list);
343 }
344
345
346
347
348
349
350
351
352
353
354
355
356 public static OTSLine3D clothoid(final OTSPoint3D start, final Angle.Abs startDirection, final double endCurvature,
357 final Length.Rel length, final Length.Rel endElevation, final int numSegments) throws OTSGeometryException
358 {
359 return clothoid(start.x, start.y, start.z, startDirection.si, 0, endCurvature, length.si, endElevation.si,
360 numSegments);
361 }
362
363
364
365
366
367
368
369
370
371
372
373
374
375 public static OTSLine3D clothoid(final OTSPoint3D start, final Angle.Abs startDirection,
376 final double startCurvature, final double endCurvature, final Length.Rel length, final Length.Rel endElevation,
377 final int numSegments) throws OTSGeometryException
378 {
379 return clothoid(start.x, start.y, start.x, startDirection.si, startCurvature, endCurvature, length.si,
380 endElevation.si, numSegments);
381 }
382
383
384
385
386
387
388
389
390
391
392
393
394 public static OTSLine3D
395 clothoid(final OTSPoint3D start, final Angle.Abs startDirection, final LinearDensity endCurvature,
396 final Length.Rel length, final Length.Rel endElevation, final int numSegments) throws OTSGeometryException
397 {
398 return clothoid(start, startDirection, 0, endCurvature.si, length, endElevation, numSegments);
399 }
400
401
402
403
404
405
406
407
408
409
410
411
412
413 public static OTSLine3D clothoid(final OTSPoint3D start, final Angle.Abs startDirection,
414 final LinearDensity startCurvature, final LinearDensity endCurvature, final Length.Rel length,
415 final Length.Rel endElevation, final int numSegments) throws OTSGeometryException
416 {
417 return clothoid(start, startDirection, startCurvature.si, endCurvature.si, length, endElevation, numSegments);
418 }
419
420
421
422
423
424
425 public static void main(final String[] args) throws OTSGeometryException
426 {
427 OTSLine3D line;
428
429
430
431 line =
432 clothoid(new OTSPoint3D(10, 10, 5), new Angle.Abs(Math.PI / 8, AngleUnit.RADIAN), new LinearDensity(
433 0 * -0.03, LinearDensityUnit.PER_METER), new LinearDensity(0.04, LinearDensityUnit.PER_METER),
434 new Length.Rel(100, LengthUnit.METER), new Length.Rel(15, LengthUnit.METER), 100);
435 System.out.println(OTSGeometryUtil.printCoordinates("#", line, "\n"));
436 }
437
438 }