1 package org.djutils.draw.curve;
2
3 import org.djutils.draw.function.ContinuousPiecewiseLinearFunction;
4 import org.djutils.draw.line.PolyLine2d;
5 import org.djutils.draw.point.DirectedPoint2d;
6 import org.djutils.draw.point.Point2d;
7 import org.djutils.exceptions.Throw;
8 import org.djutils.exceptions.Try;
9 import org.djutils.math.AngleUtil;
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
37 public class Clothoid2d implements Curve2d, OffsetCurve2d
38 {
39
40
41 private static final double ANGLE_TOLERANCE = 2.0 * Math.PI / 3600.0;
42
43
44 private static final double SECANT_TOLERANCE = 1e-8;
45
46
47 private final DirectedPoint2d startPoint;
48
49
50 private final DirectedPoint2d endPoint;
51
52
53 private final double startCurvature;
54
55
56 private final double endCurvature;
57
58
59 private final double length;
60
61
62
63
64
65 private final double a;
66
67
68 private final double alphaMin;
69
70
71 private final double alphaMax;
72
73
74 private final double[] t0;
75
76
77 private final double[] n0;
78
79
80 private final boolean opposite;
81
82
83 private final boolean reflected;
84
85
86 private final Straight2d straight;
87
88
89 private final Arc2d arc;
90
91
92 private boolean shiftDetermined;
93
94
95 private double shiftX;
96
97
98 private double shiftY;
99
100
101 private double dShiftX;
102
103
104 private double dShiftY;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 public Clothoid2d(final DirectedPoint2d startPoint, final DirectedPoint2d endPoint)
124 {
125 Throw.whenNull(startPoint, "startPoint");
126 Throw.whenNull(endPoint, "endPoint");
127 this.startPoint = startPoint;
128 this.endPoint = endPoint;
129
130 double dx = endPoint.x - startPoint.x;
131 double dy = endPoint.y - startPoint.y;
132 double d2 = Math.hypot(dx, dy);
133 double d = Math.atan2(dy, dx);
134
135 double phi1 = AngleUtil.normalizeAroundZero(d - startPoint.dirZ);
136 double phi2 = AngleUtil.normalizeAroundZero(endPoint.dirZ - d);
137 double phi1Abs = Math.abs(phi1);
138 double phi2Abs = Math.abs(phi2);
139
140 if (phi1Abs < ANGLE_TOLERANCE && phi2Abs < ANGLE_TOLERANCE)
141 {
142
143 this.length = Math.hypot(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
144 this.a = Double.POSITIVE_INFINITY;
145 this.startCurvature = 0.0;
146 this.endCurvature = 0.0;
147 this.straight = new Straight2d(startPoint, this.length);
148 this.arc = null;
149 this.alphaMin = 0.0;
150 this.alphaMax = 0.0;
151 this.t0 = null;
152 this.n0 = null;
153 this.opposite = false;
154 this.reflected = false;
155 return;
156 }
157 else if (Math.abs(phi2 - phi1) < ANGLE_TOLERANCE)
158 {
159
160 double r = .5 * d2 / Math.sin(phi1);
161 double cosStartDirection = Math.cos(startPoint.dirZ);
162 double sinStartDirection = Math.sin(startPoint.dirZ);
163 double ang = Math.PI / 2.0;
164 double cosAng = Math.cos(ang);
165 double sinAng = Math.sin(ang);
166 double x0 = startPoint.x - r * (cosStartDirection * cosAng + sinStartDirection * sinAng);
167 double y0 = startPoint.y - r * (cosStartDirection * -sinAng + sinStartDirection * cosAng);
168 double from = Math.atan2(startPoint.y - y0, startPoint.x - x0);
169 double to = Math.atan2(endPoint.y - y0, endPoint.x - x0);
170 if (r < 0 && to > from)
171 {
172 to = to - 2.0 * Math.PI;
173 }
174 else if (r > 0 && to < from)
175 {
176 to = to + 2.0 * Math.PI;
177 }
178 double angle = Math.abs(to - from);
179 this.length = angle * Math.abs(r);
180 this.a = 0.0;
181 this.startCurvature = 1.0 / r;
182 this.endCurvature = 1.0 / r;
183 this.straight = null;
184 this.arc = new Arc2d(startPoint, Math.abs(r), r > 0.0, angle);
185 this.alphaMin = 0.0;
186 this.alphaMax = 0.0;
187 this.t0 = null;
188 this.n0 = null;
189 this.opposite = false;
190 this.reflected = false;
191 return;
192 }
193 this.straight = null;
194 this.arc = null;
195
196
197
198 if (phi2Abs < phi1Abs)
199 {
200 this.opposite = true;
201 double phi3 = phi1;
202 phi1 = -phi2;
203 phi2 = -phi3;
204 dx = -dx;
205 dy = -dy;
206 }
207 else
208 {
209 this.opposite = false;
210 }
211
212
213 this.reflected = phi2 < 0 || phi2 > Math.PI;
214 if (this.reflected)
215 {
216 phi1 = -phi1;
217 phi2 = -phi2;
218 }
219
220
221 double[] cs = Fresnel.fresnel(alphaToT(phi1 + phi2));
222 double h = cs[1] * Math.cos(phi1) - cs[0] * Math.sin(phi1);
223 boolean cShape = 0 < phi1 && phi1 < phi2 && phi2 < Math.PI && h < 0;
224 double theta = getTheta(phi1, phi2, cShape);
225 double aSign = cShape ? -1.0 : 1.0;
226 double thetaSign = -aSign;
227
228 double v1 = theta + phi1 + phi2;
229 double v2 = theta + phi1;
230 double[] cs0 = Fresnel.fresnel(alphaToT(theta));
231 double[] cs1 = Fresnel.fresnel(alphaToT(v1));
232 this.a = d2 / ((cs1[1] + aSign * cs0[1]) * Math.sin(v2) + (cs1[0] + aSign * cs0[0]) * Math.cos(v2));
233
234 dx /= d2;
235 dy /= d2;
236 if (this.reflected)
237 {
238
239 this.t0 = new double[] {Math.cos(-v2) * dx + Math.sin(-v2) * dy, -Math.sin(-v2) * dx + Math.cos(-v2) * dy};
240 this.n0 = new double[] {-this.t0[1], this.t0[0]};
241 }
242 else
243 {
244 this.t0 = new double[] {Math.cos(v2) * dx + Math.sin(v2) * dy, -Math.sin(v2) * dx + Math.cos(v2) * dy};
245 this.n0 = new double[] {this.t0[1], -this.t0[0]};
246 }
247
248 this.alphaMin = thetaSign * theta;
249 this.alphaMax = v1;
250 double sign = (this.reflected ? -1.0 : 1.0);
251 double curveMin = Math.PI * alphaToT(this.alphaMin) / this.a;
252 double curveMax = Math.PI * alphaToT(v1) / this.a;
253 this.startCurvature = sign * (this.opposite ? -curveMax : curveMin);
254 this.endCurvature = sign * (this.opposite ? -curveMin : curveMax);
255 this.length = this.a * (alphaToT(v1) - alphaToT(this.alphaMin));
256 }
257
258
259
260
261
262
263
264
265
266
267 public Clothoid2d(final DirectedPoint2d startPoint, final double a, final double startCurvature, final double endCurvature)
268 {
269 Throw.whenNull(startPoint, "startPoint");
270 Throw.when(a <= 0.0, IllegalArgumentException.class, "A value must be above 0.");
271 this.startPoint = startPoint;
272
273 this.a = a * Math.sqrt(Math.PI);
274 this.length = a * a * Math.abs(endCurvature - startCurvature);
275 this.startCurvature = startCurvature;
276 this.endCurvature = endCurvature;
277
278 double l1 = a * a * startCurvature;
279 double l2 = a * a * endCurvature;
280 this.alphaMin = Math.abs(l1) * startCurvature / 2.0;
281 this.alphaMax = Math.abs(l2) * endCurvature / 2.0;
282
283 double ang = AngleUtil.normalizeAroundZero(startPoint.dirZ) - Math.abs(this.alphaMin);
284 this.t0 = new double[] {Math.cos(ang), Math.sin(ang)};
285 this.n0 = new double[] {this.t0[1], -this.t0[0]};
286 double endDirection = ang + Math.abs(this.alphaMax);
287 if (startCurvature > endCurvature)
288 {
289
290
291 double m = Math.tan(startPoint.dirZ + Math.PI / 2.0);
292
293
294 double onePlusMm = 1.0 + m * m;
295 double oneMinusMm = 1.0 - m * m;
296 double mmMinusOne = m * m - 1.0;
297 double twoM = 2.0 * m;
298 double t00 = this.t0[0];
299 double t01 = this.t0[1];
300 double n00 = this.n0[0];
301 double n01 = this.n0[1];
302 this.t0[0] = (oneMinusMm * t00 + 2 * m * t01) / onePlusMm;
303 this.t0[1] = (twoM * t00 + mmMinusOne * t01) / onePlusMm;
304 this.n0[0] = (oneMinusMm * n00 + 2 * m * n01) / onePlusMm;
305 this.n0[1] = (twoM * n00 + mmMinusOne * n01) / onePlusMm;
306
307 double ang2 = Math.atan2(this.t0[1], this.t0[0]);
308 endDirection = ang2 - Math.abs(this.alphaMax) + Math.PI;
309 }
310 PolyLine2d line = toPolyLine(new Flattener2d.NumSegments(1));
311 Point2d end = Try.assign(() -> line.get(line.size() - 1), "Line does not have an end point.");
312 this.endPoint = new DirectedPoint2d(end.x, end.y, endDirection);
313
314
315 this.straight = null;
316 this.arc = null;
317 this.opposite = false;
318 this.reflected = false;
319 }
320
321
322
323
324
325
326
327
328
329
330
331
332
333 public static Clothoid2d withLength(final DirectedPoint2d startPoint, final double length, final double startCurvature,
334 final double endCurvature)
335 {
336 Throw.when(length <= 0.0, IllegalArgumentException.class, "Length must be above 0.");
337 double a = Math.sqrt(length / Math.abs(endCurvature - startCurvature));
338 return new Clothoid2d(startPoint, a, startCurvature, endCurvature);
339 }
340
341
342
343
344
345
346 private static double alphaToT(final double alpha)
347 {
348 return alpha >= 0 ? Math.sqrt(alpha * 2.0 / Math.PI) : -Math.sqrt(-alpha * 2.0 / Math.PI);
349 }
350
351
352
353
354
355
356
357
358 private static double getTheta(final double phi1, final double phi2, final boolean cShape)
359 {
360 double sign, phiMin, phiMax;
361 if (cShape)
362 {
363 double lambda = (1 - Math.cos(phi1)) / (1 - Math.cos(phi2));
364 phiMin = 0.0;
365 phiMax = (lambda * lambda * (phi1 + phi2)) / (1 - (lambda * lambda));
366 sign = -1.0;
367 }
368 else
369 {
370 phiMin = Math.max(0, -phi1);
371 phiMax = Math.PI / 2 - phi1;
372 sign = 1;
373 }
374
375 double fMin = fTheta(phiMin, phi1, phi2, sign);
376 double fMax = fTheta(phiMax, phi1, phi2, sign);
377 if (fMin * fMax > 0)
378 {
379 throw new IllegalArgumentException(
380 "f(phiMin) and f(phiMax) have the same sign, we cant find f(theta) = 0 between them.");
381 }
382
383
384 double x0 = phiMin;
385 double x1 = phiMax;
386 double x2 = 0;
387 for (int i = 0; i < 100; i++)
388 {
389 double f1 = fTheta(x1, phi1, phi2, sign);
390 x2 = x1 - f1 * (x1 - x0) / (f1 - fTheta(x0, phi1, phi2, sign));
391 x2 = Math.max(Math.min(x2, phiMax), phiMin);
392 x0 = x1;
393 x1 = x2;
394 if (Math.abs(x0 - x1) < SECANT_TOLERANCE || Math.abs(x0 / x1 - 1) < SECANT_TOLERANCE
395 || Math.abs(f1) < SECANT_TOLERANCE)
396 {
397 return x2;
398 }
399 }
400
401 return x2;
402 }
403
404
405
406
407
408
409
410
411
412
413
414 private static double fTheta(final double theta, final double phi1, final double phi2, final double sign)
415 {
416 double thetaPhi1 = theta + phi1;
417 double[] cs0 = Fresnel.fresnel(alphaToT(theta));
418 double[] cs1 = Fresnel.fresnel(alphaToT(thetaPhi1 + phi2));
419 return (cs1[1] + sign * cs0[1]) * Math.cos(thetaPhi1) - (cs1[0] + sign * cs0[0]) * Math.sin(thetaPhi1);
420 }
421
422 @Override
423 public DirectedPoint2d getStartPoint()
424 {
425 return this.startPoint;
426 }
427
428 @Override
429 public DirectedPoint2d getEndPoint()
430 {
431 return this.endPoint;
432 }
433
434
435
436
437
438 public double getStartCurvature()
439 {
440 return this.startCurvature;
441 }
442
443
444
445
446
447 public double getEndCurvature()
448 {
449 return this.endCurvature;
450 }
451
452
453
454
455
456 public double getStartRadius()
457 {
458 return 1.0 / this.startCurvature;
459 }
460
461
462
463
464
465 public double getEndRadius()
466 {
467 return 1.0 / this.endCurvature;
468 }
469
470
471
472
473
474 public double getA()
475 {
476
477
478 return this.a / Math.sqrt(Math.PI);
479 }
480
481
482
483
484 private void assureShift()
485 {
486 if (this.shiftDetermined)
487 {
488 return;
489 }
490
491 DirectedPoint2d p1 = this.opposite ? this.endPoint : this.startPoint;
492 DirectedPoint2d p2 = this.opposite ? this.startPoint : this.endPoint;
493
494
495 double[] csMin = Fresnel.fresnel(alphaToT(this.alphaMin));
496 double xMin = this.a * (csMin[0] * this.t0[0] - csMin[1] * this.n0[0]);
497 double yMin = this.a * (csMin[0] * this.t0[1] - csMin[1] * this.n0[1]);
498 this.shiftX = p1.x - xMin;
499 this.shiftY = p1.y - yMin;
500
501
502 if (p2 != null)
503 {
504 double[] csMax = Fresnel.fresnel(alphaToT(this.alphaMax));
505 double xMax = this.a * (csMax[0] * this.t0[0] - csMax[1] * this.n0[0]);
506 double yMax = this.a * (csMax[0] * this.t0[1] - csMax[1] * this.n0[1]);
507 this.dShiftX = p2.x - (xMax + this.shiftX);
508 this.dShiftY = p2.y - (yMax + this.shiftY);
509 }
510 else
511 {
512 this.dShiftX = 0.0;
513 this.dShiftY = 0.0;
514 }
515
516 this.shiftDetermined = true;
517 }
518
519
520
521
522
523
524
525 private Point2d getPoint(final double fraction, final double offset)
526 {
527 double f = this.opposite ? 1.0 - fraction : fraction;
528 double alpha = this.alphaMin + f * (this.alphaMax - this.alphaMin);
529 double[] cs = Fresnel.fresnel(alphaToT(alpha));
530 double x = this.shiftX + this.a * (cs[0] * this.t0[0] - cs[1] * this.n0[0]) + f * this.dShiftX;
531 double y = this.shiftY + this.a * (cs[0] * this.t0[1] - cs[1] * this.n0[1]) + f * this.dShiftY;
532 double d = getDirectionForAlpha(alpha) + Math.PI / 2;
533 return new Point2d(x + Math.cos(d) * offset, y + Math.sin(d) * offset);
534 }
535
536 @Override
537 public Point2d getPoint(final double fraction)
538 {
539 if (this.arc != null)
540 {
541 return this.arc.getPoint(fraction);
542 }
543 else if (this.straight != null)
544 {
545 return this.straight.getPoint(fraction);
546 }
547 return getPoint(fraction, 0);
548 }
549
550 @Override
551 public Point2d getPoint(final double fraction, final ContinuousPiecewiseLinearFunction of)
552 {
553 if (this.arc != null)
554 {
555 return this.arc.getPoint(fraction, of);
556 }
557 else if (this.straight != null)
558 {
559 return this.straight.getPoint(fraction, of);
560 }
561 return getPoint(fraction, of.get(fraction));
562 }
563
564 @Override
565 public Double getDirection(final double fraction)
566 {
567 if (this.arc != null)
568 {
569 return this.arc.getDirection(fraction);
570 }
571 else if (this.straight != null)
572 {
573 return this.straight.getDirection(fraction);
574 }
575 return getDirectionForAlpha(this.alphaMin + fraction * (this.alphaMax - this.alphaMin));
576 }
577
578 @Override
579 public double getDirection(final double fraction, final ContinuousPiecewiseLinearFunction of)
580 {
581 double derivativeOffset = of.getDerivative(fraction) / this.length;
582 return getDirection(this.alphaMin + fraction * (this.alphaMax - this.alphaMin)) + Math.atan(derivativeOffset);
583 }
584
585
586
587
588
589
590 private double getDirectionForAlpha(final double alpha)
591 {
592 double rot = Math.atan2(this.t0[1], this.t0[0]);
593
594 rot += this.reflected ? -Math.abs(alpha) : Math.abs(alpha);
595 if (this.opposite)
596 {
597 rot += Math.PI;
598 }
599 return AngleUtil.normalizeAroundZero(rot);
600 }
601
602 @Override
603 public PolyLine2d toPolyLine(final Flattener2d flattener)
604 {
605 if (this.straight != null)
606 {
607 return this.straight.toPolyLine(flattener);
608 }
609 if (this.arc != null)
610 {
611 return this.arc.toPolyLine(flattener);
612 }
613 assureShift();
614 return flattener.flatten(this);
615 }
616
617 @Override
618 public PolyLine2d toPolyLine(final OffsetFlattener2d flattener, final ContinuousPiecewiseLinearFunction offsets)
619 {
620 Throw.whenNull(offsets, "offsets");
621 if (this.straight != null)
622 {
623 return this.straight.toPolyLine(flattener, offsets);
624 }
625 if (this.arc != null)
626 {
627 return this.arc.toPolyLine(flattener, offsets);
628 }
629 assureShift();
630 return flattener.flatten(this, offsets);
631 }
632
633 @Override
634 public double getLength()
635 {
636 return this.length;
637 }
638
639
640
641
642
643
644 public String getAppliedShape()
645 {
646 return this.straight == null ? (this.arc == null ? "Clothoid" : "Arc") : "Straight";
647 }
648
649 @Override
650 public String toString()
651 {
652 return "Clothoid [startPoint=" + this.startPoint + ", endPoint=" + this.endPoint + ", startCurvature="
653 + this.startCurvature + ", endCurvature=" + this.endCurvature + ", length=" + this.length + "]";
654 }
655
656 }
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674 final class Fresnel
675 {
676
677
678
679 private static final double[] CN1 = new double[] {
680 9.999999999999999421E-01,
681 -1.994608988261842706E-01,
682 1.761939525434914045E-02,
683 -5.280796513726226960E-04,
684 5.477113856826871660E-06
685 };
686
687
688 private static final double[] CD1 = new double[] {
689 1.000000000000000000E+00,
690 4.727921120104532689E-02,
691 1.099572150256418851E-03,
692 1.552378852769941331E-05,
693 1.189389014228757184E-07
694 };
695
696
697 private static final double[] CN2 = new double[] {
698 1.00000000000111043640E+00,
699 -2.07073360335323894245E-01,
700 1.91870279431746926505E-02,
701 -6.71376034694922109230E-04,
702 1.02365435056105864908E-05,
703 -5.68293310121870728343E-08
704 };
705
706
707 private static final double[] CD2 = new double[] {
708 1.00000000000000000000E+00,
709 3.96667496952323433510E-02,
710 7.88905245052359907842E-04,
711 1.01344630866749406081E-05,
712 8.77945377892369265356E-08,
713 4.41701374065009620393E-10
714 };
715
716
717 private static final double[] SN1 = new double[] {
718 5.2359877559829887021E-01,
719 -7.0748991514452302596E-02,
720 3.8778212346368287939E-03,
721 -8.4555728435277680591E-05,
722 6.7174846662514086196E-07
723 };
724
725
726 private static final double[] SD1 = new double[] {
727 1.0000000000000000000E+00,
728 4.1122315114238422205E-02,
729 8.1709194215213447204E-04,
730 9.6269087593903403370E-06,
731 5.9528122767840998345E-08
732 };
733
734
735 private static final double[] SN2 = new double[] {
736 5.23598775598344165913E-01,
737 -7.37766914010191323867E-02,
738 4.30730526504366510217E-03,
739 -1.09540023911434994566E-04,
740 1.28531043742724820610E-06,
741 -5.76765815593088804567E-09
742 };
743
744
745 private static final double[] SD2 = new double[] {
746 1.00000000000000000000E+00,
747 3.53398342167472162540E-02,
748 6.18224620195473216538E-04,
749 6.87086265718620117905E-06,
750 5.03090581246612375866E-08,
751 2.05539124458579596075E-10
752 };
753
754
755 private static final double[] FN3 = new double[] {
756 3.1830975293580985290E-01,
757 1.2226000551672961219E+01,
758 1.2924886131901657025E+02,
759 4.3886367156695547655E+02,
760 4.1466722177958961672E+02,
761 5.6771463664185116454E+01
762 };
763
764
765 private static final double[] FD3 = new double[] {
766 1.0000000000000000000E+00,
767 3.8713003365583442831E+01,
768 4.1674359830705629745E+02,
769 1.4740030733966610568E+03,
770 1.5371675584895759916E+03,
771 2.9113088788847831515E+02
772 };
773
774
775 private static final double[] FN4 = new double[] {
776 3.183098818220169217E-01,
777 1.958839410219691002E+01,
778 3.398371349269842400E+02,
779 1.930076407867157531E+03,
780 3.091451615744296552E+03,
781 7.177032493651399590E+02
782 };
783
784
785 private static final double[] FD4 = new double[] {
786 1.000000000000000000E+00,
787 6.184271381728873709E+01,
788 1.085350675006501251E+03,
789 6.337471558511437898E+03,
790 1.093342489888087888E+04,
791 3.361216991805511494E+03
792 };
793
794
795 private static final double[] FN5 = new double[] {
796 -9.675460329952532343E-02,
797 -2.431275407194161683E+01,
798 -1.947621998306889176E+03,
799 -6.059852197160773639E+04,
800 -7.076806952837779823E+05,
801 -2.417656749061154155E+06,
802 -7.834914590078311336E+05
803 };
804
805
806 private static final double[] FD5 = new double[] {
807 1.000000000000000000E+00,
808 2.548289012949732752E+02,
809 2.099761536857815105E+04,
810 6.924122509827708985E+05,
811 9.178823229918143780E+06,
812 4.292733255630186679E+07,
813 4.803294184260528342E+07
814 };
815
816
817 private static final double[] GN3 = new double[] {
818 1.013206188102747985E-01,
819 4.445338275505123778E+00,
820 5.311228134809894481E+01,
821 1.991828186789025318E+02,
822 1.962320379716626191E+02,
823 2.054214324985006303E+01
824 };
825
826
827 private static final double[] GD3 = new double[] {
828 1.000000000000000000E+00,
829 4.539250196736893605E+01,
830 5.835905757164290666E+02,
831 2.544731331818221034E+03,
832 3.481121478565452837E+03,
833 1.013794833960028555E+03
834 };
835
836
837 private static final double[] GN4 = new double[] {
838 1.01321161761804586E-01,
839 7.11205001789782823E+00,
840 1.40959617911315524E+02,
841 9.08311749529593938E+02,
842 1.59268006085353864E+03,
843 3.13330163068755950E+02
844 };
845
846
847 private static final double[] GD4 = new double[] {
848 1.00000000000000000E+00,
849 7.17128596939302198E+01,
850 1.49051922797329229E+03,
851 1.06729678030583897E+04,
852 2.41315567213369742E+04,
853 1.15149832376260604E+04
854 };
855
856
857 private static final double[] GN5 = new double[] {
858 -1.53989733819769316E-01,
859 -4.31710157823357568E+01,
860 -3.87754141746378493E+03,
861 -1.35678867813756347E+05,
862 -1.77758950838029676E+06,
863 -6.66907061668636416E+06,
864 -1.72590224654836845E+06
865 };
866
867
868 private static final double[] GD5 = new double[] {
869 1.00000000000000000E+00,
870 2.86733194975899483E+02,
871 2.69183180396242536E+04,
872 1.02878693056687506E+06,
873 1.62095600500231646E+07,
874 9.38695862531635179E+07,
875 1.40622441123580005E+08
876 };
877
878
879
880 private Fresnel()
881 {
882
883 }
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898 public static double[] fresnel(final double x)
899 {
900 final double t = Math.abs(x);
901 double cc, ss;
902 if (t < 1.2)
903 {
904 cc = t * ratioEval(t, CN1, +1) / ratioEval(t, CD1, +1);
905 ss = t * t * t * ratioEval(t, SN1, +1) / ratioEval(t, SD1, +1);
906 }
907 else if (t < 1.6)
908 {
909 cc = t * ratioEval(t, CN2, +1) / ratioEval(t, CD2, +1);
910 ss = t * t * t * ratioEval(t, SN2, +1) / ratioEval(t, SD2, +1);
911 }
912 else if (t < 1.9)
913 {
914 double pitt2 = Math.PI * t * t / 2;
915 double sinpitt2 = Math.sin(pitt2);
916 double cospitt2 = Math.cos(pitt2);
917 double ft = (1 / t) * ratioEval(t, FN3, -1) / ratioEval(t, FD3, -1);
918 double gt = (1 / (t * t * t)) * ratioEval(t, GN3, -1) / ratioEval(t, GD3, -1);
919 cc = .5 + ft * sinpitt2 - gt * cospitt2;
920 ss = .5 - ft * cospitt2 - gt * sinpitt2;
921 }
922 else if (t < 2.4)
923 {
924 double pitt2 = Math.PI * t * t / 2;
925 double sinpitt2 = Math.sin(pitt2);
926 double cospitt2 = Math.cos(pitt2);
927 double tinv = 1 / t;
928 double tttinv = tinv * tinv * tinv;
929 double ft = tinv * ratioEval(t, FN4, -1) / ratioEval(t, FD4, -1);
930 double gt = tttinv * ratioEval(t, GN4, -1) / ratioEval(t, GD4, -1);
931 cc = .5 + ft * sinpitt2 - gt * cospitt2;
932 ss = .5 - ft * cospitt2 - gt * sinpitt2;
933 }
934 else
935 {
936 double pitt2 = Math.PI * t * t / 2;
937 double sinpitt2 = Math.sin(pitt2);
938 double cospitt2 = Math.cos(pitt2);
939 double piinv = 1 / Math.PI;
940 double tinv = 1 / t;
941 double tttinv = tinv * tinv * tinv;
942 double ttttinv = tttinv * tinv;
943 double ft = tinv * (piinv + (ttttinv * ratioEval(t, FN5, -1) / ratioEval(t, FD5, -1)));
944 double gt = tttinv * ((piinv * piinv) + (ttttinv * ratioEval(t, GN5, -1) / ratioEval(t, GD5, -1)));
945 cc = .5 + ft * sinpitt2 - gt * cospitt2;
946 ss = .5 - ft * cospitt2 - gt * sinpitt2;
947 }
948 if (x < 0)
949 {
950 cc = -cc;
951 ss = -ss;
952 }
953
954 return new double[] {cc, ss};
955 }
956
957
958
959
960
961
962
963
964 private static double ratioEval(final double t, final double[] coef, final double sign)
965 {
966 double value = 0;
967 for (int s = 0; s < coef.length; s++)
968 {
969 value += coef[s] * Math.pow(t, sign * 4 * s);
970 }
971 return value;
972 }
973
974 }