package org.djutils.draw.line;
import org.djutils.complex.Complex;
import org.djutils.draw.DrawRuntimeException;
import org.djutils.exceptions.Throw;
import org.djutils.polynomialroots.PolynomialRoots;
/**
* Approximate a clothoid with a PolyLine3d.
* Derived from https://github.com/ebertolazzi/G1fitting/blob/master/src/Clothoid.cc
* @author Alexander Verbraeck
* @author Peter Knoppers
*/
public final class Clothoid
{
/** Utility class. */
private Clothoid()
{
// do not instantiate
}
/** ??? */
static final double A_THRESOLD = 0.01;
/** ??? */
static final int A_SERIE_SIZE = 3;
//@formatter:off
/** Fresnel coefficients FN. */
static final double[] FN =
{
0.49999988085884732562,
1.3511177791210715095,
1.3175407836168659241,
1.1861149300293854992,
0.7709627298888346769,
0.4173874338787963957,
0.19044202705272903923,
0.06655998896627697537,
0.022789258616785717418,
0.0040116689358507943804,
0.0012192036851249883877
};
/** Fresnel coefficients FD. */
static final double[] FD =
{
1.0,
2.7022305772400260215,
4.2059268151438492767,
4.5221882840107715516,
3.7240352281630359588,
2.4589286254678152943,
1.3125491629443702962,
0.5997685720120932908,
0.20907680750378849485,
0.07159621634657901433,
0.012602969513793714191,
0.0038302423512931250065
};
/** Fresnel coefficients GN. */
static final double[] GN =
{
0.50000014392706344801,
0.032346434925349128728,
0.17619325157863254363,
0.038606273170706486252,
0.023693692309257725361,
0.007092018516845033662,
0.0012492123212412087428,
0.00044023040894778468486,
-8.80266827476172521e-6,
-1.4033554916580018648e-8,
2.3509221782155474353e-10
};
/** Fresnel coefficients GD. */
static final double[] GD =
{
1.0,
2.0646987497019598937,
2.9109311766948031235,
2.6561936751333032911,
2.0195563983177268073,
1.1167891129189363902,
0.57267874755973172715,
0.19408481169593070798,
0.07634808341431248904,
0.011573247407207865977,
0.0044099273693067311209,
-0.00009070958410429993314
};
/** Pi. */
static final double m_pi = Math.PI;
/** Half Pi. */
static final double m_pi_2 = Math.PI / 2;
/** Two Pi. */
static final double m_2pi = 2 * Math.PI;
/** One over Pi. */
static final double m_1_pi = 1 / Math.PI;
/** One over square root of Pi. */
static final double m_1_sqrt_pi = 1 / Math.sqrt(Math.PI);
/*
* #######
* # ##### ###### #### # # ###### #
* # # # # # ## # # #
* ##### # # ##### #### # # # ##### #
* # ##### # # # # # # #
* # # # # # # # ## # #
* # # # ###### #### # # ###### ######
*/
//@formatter:on
/**
* Purpose: This program computes the Fresnel integrals.
*
*
* C(x) and S(x) using subroutine FCS * Input : x --- Argument of C(x) and S(x) * Output: C --- C(x) * S --- S(x) * Example: * x C(x) S(x) * ----------------------------------- * 0.0 .00000000 .00000000 * 0.5 .49234423 .06473243 * 1.0 .77989340 .43825915 * 1.5 .44526118 .69750496 * 2.0 .48825341 .34341568 * 2.5 .45741301 .61918176 * Purpose: Compute Fresnel integrals C(x) and S(x) * Input : x --- Argument of C(x) and S(x) * Output: C --- C(x) * S --- S(x) ** * @param y double; * @return double[]; double array with two elements; C is stored in the first, S in the second */ private static double[] fresnelCS(final double y) { final double eps = 1E-15; final double x = y > 0 ? y : -y; double resultC; double resultS; if (x < 1.0) { double twofn, fact, denterm, numterm, sum, term; final double s = m_pi_2 * (x * x); final double t = -s * s; // Cosine integral series twofn = 0.0; fact = 1.0; denterm = 1.0; numterm = 1.0; sum = 1.0; do { twofn += 2.0; fact *= twofn * (twofn - 1.0); denterm += 4.0; numterm *= t; term = numterm / (fact * denterm); sum += term; } while (Math.abs(term) > eps * Math.abs(sum)); resultC = x * sum; // Sine integral series twofn = 1.0; fact = 1.0; denterm = 3.0; numterm = 1.0; sum = 1.0 / 3.0; do { twofn += 2.0; fact *= twofn * (twofn - 1.0); denterm += 4.0; numterm *= t; term = numterm / (fact * denterm); sum += term; } while (Math.abs(term) > eps * Math.abs(sum)); resultS = m_pi_2 * sum * (x * x * x); } else if (x < 6.0) { // Rational approximation for f double sumn = 0.0; double sumd = FD[11]; for (int k = 10; k >= 0; --k) { sumn = FN[k] + x * sumn; sumd = FD[k] + x * sumd; } double f = sumn / sumd; // Rational approximation for g sumn = 0.0; sumd = GD[11]; for (int k = 10; k >= 0; --k) { sumn = GN[k] + x * sumn; sumd = GD[k] + x * sumd; } double g = sumn / sumd; double u = m_pi_2 * (x * x); double sinU = Math.sin(u); double cosU = Math.cos(u); resultC = 0.5 + f * sinU - g * cosU; resultS = 0.5 - f * cosU - g * sinU; } else { double absterm; // x >= 6; asymptotic expansions for f and g final double s = m_pi * x * x; final double t = -1 / (s * s); // Expansion for f double numterm = -1.0; double term = 1.0; double sum = 1.0; double oldterm = 1.0; double eps10 = 0.1 * eps; do { numterm += 4.0; term *= numterm * (numterm - 2.0) * t; sum += term; absterm = Math.abs(term); Throw.when(false, oldterm >= absterm, DrawRuntimeException.class, "In FresnelCS f not converged to eps, x = " + x + " oldterm = " + oldterm + " absterm = " + absterm); oldterm = absterm; } while (absterm > eps10 * Math.abs(sum)); double f = sum / (m_pi * x); // Expansion for g numterm = -1.0; term = 1.0; sum = 1.0; oldterm = 1.0; do { numterm += 4.0; term *= numterm * (numterm + 2.0) * t; sum += term; absterm = Math.abs(term); Throw.when(oldterm >= absterm, DrawRuntimeException.class, "In FresnelCS g does not converge to eps, x = " + x + " oldterm = " + oldterm + " absterm = " + absterm); oldterm = absterm; } while (absterm > eps10 * Math.abs(sum)); double g = m_pi * x; g = sum / (g * g * x); double u = m_pi_2 * (x * x); double sinU = Math.sin(u); double cosU = Math.cos(u); resultC = 0.5 + f * sinU - g * cosU; resultS = 0.5 - f * cosU - g * sinU; } if (y < 0) { resultC = -resultC; resultS = -resultS; } return new double[] { resultC, resultS }; } /** * ??? * @param nk int; size of the provided arrays * @param t double; * @param C double[]; should have length nk * @param S double[]; shouldhave laength nk */ private static void fresnelCS(final int nk, final double t, final double[] C, final double[] S) { double[] cs = fresnelCS(t); C[0] = cs[0]; S[0] = cs[1]; if (nk > 1) { double tt = m_pi_2 * (t * t); double ss = Math.sin(tt); double cc = Math.cos(tt); C[1] = ss * m_1_pi; S[1] = (1 - cc) * m_1_pi; if (nk > 2) { C[2] = (t * ss - S[0]) * m_1_pi; S[2] = (C[0] - t * cc) * m_1_pi; } } } /** * ??? * @param a double; * @param b double; * @return double[] with two elements set to X and Y */ private static double[] evalXYaLarge(final double a, final double b) { double s = a > 0 ? +1 : -1; double absa = Math.abs(a); double z = m_1_sqrt_pi * Math.sqrt(absa); double ell = s * b * m_1_sqrt_pi / Math.sqrt(absa); double g = -0.5 * s * (b * b) / absa; double cg = Math.cos(g) / z; double sg = Math.sin(g) / z; // double Cl, Sl, Cz, Sz; double[] resultL = fresnelCS(ell); double[] resultZ = fresnelCS(ell + z); double dC0 = resultZ[0] - resultL[0]; double dS0 = resultZ[1] - resultL[1]; double X = cg * dC0 - s * sg * dS0; double Y = sg * dC0 + s * cg * dS0; return new double[] { X, Y }; } // ------------------------------------------------------------------------- // nk max 3 /** * ??? * @param nk int; minimum 0; maximum 3 * @param a double; ? * @param b double; ? * @param X double[]; ? * @param Y double[]; ? */ private static void evalXYaLarge(final int nk, final double a, final double b, double[] X, double[] Y) { Throw.when(nk <= 0 || nk >= 4, DrawRuntimeException.class, "In evalXYaLarge first argument nk must be in 1..3, nk " + nk); double s = a > 0 ? +1 : -1; double absa = Math.abs(a); double z = m_1_sqrt_pi * Math.sqrt(absa); double ell = s * b * m_1_sqrt_pi / Math.sqrt(absa); double g = -0.5 * s * (b * b) / absa; double cg = Math.cos(g) / z; double sg = Math.sin(g) / z; double[] Cl = new double[3]; double[] Sl = new double[3]; double[] Cz = new double[3]; double[] Sz = new double[3]; fresnelCS(nk, ell, Cl, Sl); fresnelCS(nk, ell + z, Cz, Sz); double dC0 = Cz[0] - Cl[0]; double dS0 = Sz[0] - Sl[0]; X[0] = cg * dC0 - s * sg * dS0; Y[0] = sg * dC0 + s * cg * dS0; if (nk > 1) { cg /= z; sg /= z; double dC1 = Cz[1] - Cl[1]; double dS1 = Sz[1] - Sl[1]; double DC = dC1 - ell * dC0; double DS = dS1 - ell * dS0; X[1] = cg * DC - s * sg * DS; Y[1] = sg * DC + s * cg * DS; if (nk > 2) { double dC2 = Cz[2] - Cl[2]; double dS2 = Sz[2] - Sl[2]; DC = dC2 + ell * (ell * dC0 - 2 * dC1); DS = dS2 + ell * (ell * dS0 - 2 * dS1); cg = cg / z; sg = sg / z; X[2] = cg * DC - s * sg * DS; Y[2] = sg * DC + s * cg * DS; } } } /** * LommelReduced. * @param mu double; ? * @param nu double; ? * @param b double; ? * @return double; ? */ private static double LommelReduced(final double mu, final double nu, final double b) { double tmp = 1 / ((mu + nu + 1) * (mu - nu + 1)); double res = tmp; for (int n = 1; n <= 100; ++n) { tmp *= (-b / (2 * n + mu - nu + 1)) * (b / (2 * n + mu + nu + 1)); res += tmp; if (Math.abs(tmp) < Math.abs(res) * 1e-50) { break; } } return res; } /** * ??? * @param nk int; ? * @param b double; ? * @param X double[]; ? * @param Y double[]; ? */ private static void evalXYazero(final int nk, final double b, double X[], double Y[]) { double sb = Math.sin(b); double cb = Math.cos(b); double b2 = b * b; if (Math.abs(b) < 1e-3) { X[0] = 1 - (b2 / 6) * (1 - (b2 / 20) * (1 - (b2 / 42))); Y[0] = (b / 2) * (1 - (b2 / 12) * (1 - (b2 / 30))); } else { X[0] = sb / b; Y[0] = (1 - cb) / b; } // use recurrence in the stable part int m = (int) Math.floor(2 * b); if (m >= nk) { m = nk - 1; } if (m < 1) { m = 1; } for (int k = 1; k < m; ++k) { X[k] = (sb - k * Y[k - 1]) / b; Y[k] = (k * X[k - 1] - cb) / b; } // use Lommel for the unstable part if (m < nk) { double A = b * sb; double D = sb - b * cb; double B = b * D; double C = -b2 * sb; double rLa = LommelReduced(m + 0.5, 1.5, b); double rLd = LommelReduced(m + 0.5, 0.5, b); for (int k = m; k < nk; ++k) { double rLb = LommelReduced(k + 1.5, 0.5, b); double rLc = LommelReduced(k + 1.5, 1.5, b); X[k] = (k * A * rLa + B * rLb + cb) / (1 + k); Y[k] = (C * rLc + sb) / (2 + k) + D * rLd; rLa = rLc; rLd = rLb; } } } /** * ??? * @param a double; ? * @param b double; ? * @param p double; ? * @return double[]; containing the two values; X and Y. */ private static double[] evalXYaSmall(final double a, final double b, final int p) { Throw.when(p >= 11 && p <= 0, DrawRuntimeException.class, "In evalXYaSmall p = " + p + " must be in 1..10"); double[] x0 = new double[43]; double[] y0 = new double[43]; int nkk = 4 * p + 3; // max 43 evalXYazero(nkk, b, x0, y0); double x = x0[0] - (a / 2) * y0[2]; double y = y0[0] + (a / 2) * x0[2]; double t = 1; double aa = -a * a / 4; // controllare! for (int n = 1; n <= p; ++n) { t *= aa / (2 * n * (2 * n - 1)); double bf = a / (4 * n + 2); int jj = 4 * n; x += t * (x0[jj] - bf * y0[jj + 2]); y += t * (y0[jj] + bf * x0[jj + 2]); } return new double[] { x, y }; } /** * ??? * @param nk int; ? * @param a double; ? * @param b double; ? * @param p double; ? * @param x double[]; ? * @param y double[]; ? */ private static void evalXYaSmall(final int nk, final double a, final double b, final int p, final double[] x, final double[] y) { int nkk = nk + 4 * p + 2; // max 45 double[] x0 = new double[45]; double[] y0 = new double[45]; Throw.when(nkk >= 46, DrawRuntimeException.class, "In evalXYaSmall (nk,p) = (" + nk + "," + p + ")\n" + "nk + 4*p + 2 = " + nkk + " must be less than 46\n"); evalXYazero(nkk, b, x0, y0); for (int j = 0; j < nk; ++j) { x[j] = x0[j] - (a / 2) * y0[j + 2]; y[j] = y0[j] + (a / 2) * x0[j + 2]; } double t = 1; double aa = -a * a / 4; // controllare! for (int n = 1; n <= p; ++n) { t *= aa / (2 * n * (2 * n - 1)); double bf = a / (4 * n + 2); for (int j = 0; j < nk; ++j) { int jj = 4 * n + j; x[j] += t * (x0[jj] - bf * y0[jj + 2]); y[j] += t * (y0[jj] + bf * x0[jj + 2]); } } } /** * ??? * @param a double; ? * @param b double; ? * @param c double; ? * @return double[]; size two containing C and S */ private static double[] GeneralizedFresnelCS(final double a, final double b, final double c) { double[] xxyy = Math.abs(a) < A_THRESOLD ? evalXYaSmall(a, b, A_SERIE_SIZE) : evalXYaLarge(a, b); double cosC = Math.cos(c); double sinC = Math.sin(c); // FIXME: Confusing names double intC = xxyy[0] * cosC - xxyy[1] * sinC; double intS = xxyy[0] * sinC + xxyy[1] * cosC; return new double[] { intC, intS }; } /** * ??? * @param nk int; ? * @param a double; ? * @param b double; ? * @param c double; ? * @param intC double[]; stores C results * @param intS double[]; stores S results */ static void GeneralizedFresnelCS(final int nk, final double a, final double b, final double c, final double[] intC, final double[] intS) { Throw.when(nk <= 0 || nk >= 4, DrawRuntimeException.class, "nk = " + nk + " must be in 1..3"); if (Math.abs(a) < A_THRESOLD) { evalXYaSmall(nk, a, b, A_SERIE_SIZE, intC, intS); } else { evalXYaLarge(nk, a, b, intC, intS); } double cosC = Math.cos(c); double sinC = Math.sin(c); for (int k = 0; k < nk; ++k) { double xx = intC[k]; double yy = intS[k]; intC[k] = xx * cosC - yy * sinC; intS[k] = xx * sinC + yy * cosC; } } /** CF coefficients. */ private static final double[] CF = { 2.989696028701907, 0.716228953608281, -0.458969738821509, -0.502821153340377, 0.261062141752652, -0.045854475238709 }; /** * Create a clothoid connecting (x0,y0) to (x1,y1) having direction theta0 at the start point and theta1 at the end point. * @param x0 double; x coordinate of the start point * @param y0 double; y coordinate of the start point * @param theta0 double; direction at the start point (in radians) * @param x1 double; x coordinate of the end point * @param y1 double; y coordinate of the end point * @param theta1 double; direction at the end point (in radians) * @return int; the number of iterations */ public static int buildClothoid(final double x0, final double y0, final double theta0, final double x1, final double y1, final double theta1) { double k; double dk; double l; // traslazione in (0,0) double dx = x1 - x0; double dy = y1 - y0; double r = Math.hypot(dx, dy); double phi = Math.atan2(dy, dx); double phi0 = theta0 - phi; double phi1 = theta1 - phi; phi0 -= m_2pi * Math.rint(phi0 / m_2pi); phi1 -= m_2pi * Math.rint(phi1 / m_2pi); if (phi0 > m_pi) { phi0 -= m_2pi; } if (phi0 < -m_pi) { phi0 += m_2pi; } if (phi1 > m_pi) { phi1 -= m_2pi; } if (phi1 < -m_pi) { phi1 += m_2pi; } double delta = phi1 - phi0; // punto iniziale double x = phi0 * m_1_pi; double y = phi1 * m_1_pi; double xy = x * y; y *= y; x *= x; double a = (phi0 + phi1) * (CF[0] + xy * (CF[1] + xy * CF[2]) + (CF[3] + xy * CF[4]) * (x + y) + CF[5] * (x * x + y * y)); // newton double g = 0; double dg; double[] intC = new double[3]; double[] intS = new double[3]; int niter = 0; do { GeneralizedFresnelCS(3, 2 * a, delta - a, phi0, intC, intS); g = intS[0]; dg = intC[2] - intC[1]; a -= g / dg; } while (++niter <= 10 && Math.abs(g) > 1E-12); Throw.when(Math.abs(g) > 1E-8, DrawRuntimeException.class, "Newton did not converge, g = " + g + " niter = " + niter); double[] cs = GeneralizedFresnelCS(2 * a, delta - a, phi0); intC[0] = cs[0]; intS[0] = cs[1]; l = r / intC[0]; Throw.when(l <= 0, DrawRuntimeException.class, "Negative length L = " + l); k = (delta - a) / l; dk = 2 * a / l / l; return niter; } /** * Create a clothoid connecting (x0,y0) to (x1,y1) having direction theta0 at the start point and theta1 at the end point. * @param x0 double; x coordinate of the start point * @param y0 double; y coordinate of the start point * @param theta0 double; direction at the start point (in radians) * @param x1 double; x coordinate of the end point * @param y1 double; y coordinate of the end point * @param theta1 double; direction at the end point (in radians) * @return int; the number of iterations */ public static int buildClothoidMoreResults(final double x0, final double y0, final double theta0, final double x1, final double y1, final double theta1) { double k; double dk; double l; double k_1; double dk_1; double l_1; double k_2; double dk_2; double l_2; // traslazione in (0,0) double dx = x1 - x0; double dy = y1 - y0; double r = Math.hypot(dx, dy); double phi = Math.atan2(dy, dx); double phi0 = theta0 - phi; double phi1 = theta1 - phi; phi0 -= m_2pi * Math.rint(phi0 / m_2pi); phi1 -= m_2pi * Math.rint(phi1 / m_2pi); if (phi0 > m_pi) { phi0 -= m_2pi; } if (phi0 < -m_pi) { phi0 += m_2pi; } if (phi1 > m_pi) { phi1 -= m_2pi; } if (phi1 < -m_pi) { phi1 += m_2pi; } double delta = phi1 - phi0; // punto iniziale double x = phi0 * m_1_pi; double y = phi1 * m_1_pi; double xy = x * y; y *= y; x *= x; double a = (phi0 + phi1) * (CF[0] + xy * (CF[1] + xy * CF[2]) + (CF[3] + xy * CF[4]) * (x + y) + CF[5] * (x * x + y * y)); // newton double g = 0; double dg; double[] intC = new double[3]; double[] intS = new double[3]; int niter = 0; do { GeneralizedFresnelCS(3, 2 * a, delta - a, phi0, intC, intS); g = intS[0]; dg = intC[2] - intC[1]; a -= g / dg; } while (++niter <= 10 && Math.abs(g) > 1E-12); Throw.when(Math.abs(g) > 1E-8, DrawRuntimeException.class, "Newton do not converge, g = " + g + " niter = " + niter); GeneralizedFresnelCS(3, 2 * a, delta - a, phi0, intC, intS); l = r / intC[0]; Throw.when(l <= 0, DrawRuntimeException.class, "Negative length L = " + l); k = (delta - a) / l; dk = 2 * a / l / l; double alpha = intC[0] * intC[1] + intS[0] * intS[1]; double beta = intC[0] * intC[2] + intS[0] * intS[2]; double gamma = intC[0] * intC[0] + intS[0] * intS[0]; double tx = intC[1] - intC[2]; double ty = intS[1] - intS[2]; double txy = l * (intC[1] * intS[2] - intC[2] * intS[1]); double omega = l * (intS[0] * tx - intC[0] * ty) - txy; delta = intC[0] * tx + intS[0] * ty; l_1 = omega / delta; l_2 = txy / delta; delta *= l; k_1 = (beta - gamma - k * omega) / delta; k_2 = -(beta + k * txy) / delta; delta *= l / 2; dk_1 = (gamma - alpha - dk * omega * l) / delta; dk_2 = (alpha - dk * txy * l) / delta; return niter; } // void // eval(final double s, // double & theta, // double & kappa, // double & x, // double & y ) const { // double C, S ; // GeneralizedFresnelCS( dk*s*s, k*s, theta0, C, S ) ; // x = x0 + s*C ; // y = y0 + s*S ; // theta = theta0 + s*(k+s*(dk/2)) ; // kappa = k + s*dk ; // } // // void // ClothoidCurve::eval( double s, double & x, double & y ) const { // double C, S ; // GeneralizedFresnelCS( dk*s*s, k*s, theta0, C, S ) ; // x = x0 + s*C ; // y = y0 + s*S ; // } // // void // ClothoidCurve::eval_D( double s, double & x_D, double & y_D ) const { // double theta = theta0 + s*(k+s*(dk/2)) ; // x_D = cos(theta) ; // y_D = sin(theta) ; // } // // void // ClothoidCurve::eval_DD( double s, double & x_DD, double & y_DD ) const { // double theta = theta0 + s*(k+s*(dk/2)) ; // double theta_D = k+s*dk ; // x_DD = -sin(theta)*theta_D ; // y_DD = cos(theta)*theta_D ; // } // // void // ClothoidCurve::eval_DDD( double s, double & x_DDD, double & y_DDD ) const { // double theta = theta0 + s*(k+s*(dk/2)) ; // double theta_D = k+s*dk ; // double C = cos(theta) ; // double S = sin(theta) ; // double th2 = theta_D*theta_D ; // x_DDD = -C*th2-S*dk ; // y_DDD = -S*th2+C*dk ; // } // //// offset curve // void // ClothoidCurve::eval( double s, double offs, double & x, double & y ) const { // double C, S ; // GeneralizedFresnelCS( dk*s*s, k*s, theta0, C, S ) ; // double theta = theta0 + s*(k+s*(dk/2)) ; // double nx = -sin(theta) ; // double ny = cos(theta) ; // x = x0 + s*C + offs * nx ; // y = y0 + s*S + offs * ny ; // } // // void // ClothoidCurve::eval_D( double s, double offs, double & x_D, double & y_D ) const { // double theta = theta0 + s*(k+s*(dk/2)) ; // double theta_D = k+s*dk ; // double scale = 1-offs*theta_D ; // x_D = cos(theta)*scale ; // y_D = sin(theta)*scale ; // } // // void // ClothoidCurve::eval_DD( double s, double offs, double & x_DD, double & y_DD ) const { // double theta = theta0 + s*(k+s*(dk/2)) ; // double theta_D = k+s*dk ; // double C = cos(theta) ; // double S = sin(theta) ; // double tmp1 = theta_D*(1-theta_D*offs) ; // double tmp2 = offs*dk ; // x_DD = -tmp1*S - C*tmp2 ; // y_DD = tmp1*C - S*tmp2 ; // } // // void // ClothoidCurve::eval_DDD( double s, double offs, double & x_DDD, double & y_DDD ) const { // double theta = theta0 + s*(k+s*(dk/2)) ; // double theta_D = k+s*dk ; // double C = cos(theta) ; // double S = sin(theta) ; // double tmp1 = theta_D*theta_D*(theta_D*offs-1) ; // double tmp2 = dk*(1-3*theta_D*offs) ; // x_DDD = tmp1*C-tmp2*S ; // y_DDD = tmp1*S+tmp2*C ; // } /** * ??? * @param theta0 double; theta0 * @param theta double; theta * @return double; kappa */ private static double kappa(final double theta0, final double theta) { double x = theta0 * theta0; double a = -3.714 + x * 0.178; double b = -1.913 - x * 0.0753; double c = 0.999 + x * 0.03475; double d = 0.191 - x * 0.00703; double e = 0.500 - x * -0.00172; double t = d * theta0 + e * theta; return a * theta0 + b * theta + c * (t * t * t); } /** * theta_guess. *
* FIXME value parameter ok;
* @param theta0 double; theta0
* @param k0 double; k0
* @return double; theta
*/
private static double theta_guess(final double theta0, final double k0)
{
double x = theta0 * theta0;
double a = -3.714 + x * 0.178;
double b = -1.913 - x * 0.0753;
double c = 0.999 + x * 0.03475;
double d = 0.191 - x * 0.00703;
double e = 0.500 - x * -0.00172;
double e2 = e * e;
double dt = d * theta0;
double dt2 = dt * dt;
double qA = c * e * e2;
double qB = 3 * (c * d * e2 * theta0);
double qC = 3 * c * e * dt2 + b;
double qD = c * (dt * dt2) + a * theta0 - k0;
boolean ok;
Complex[] roots = PolynomialRoots.cubicRoots(qA, qB, qC, qD);
// Count the real roots
int nr = 0;
for (Complex root : roots)
{
if (root.isReal())
{
nr++;
}
}
// cerco radice reale piu vicina
double theta;
switch (nr)
{
case 0:
default:
ok = false;
return 0;
case 1:
theta = roots[0].re;
break;
case 2:
if (Math.abs(roots[0].re - theta0) < Math.abs(roots[1].re - theta0))
{
theta = roots[0].re;
}
else
{
theta = roots[1].re;
}
break;
case 3:
theta = roots[0].re;
for (int i = 1; i < 3; ++i)
{
if (Math.abs(theta - theta0) > Math.abs(roots[i].re - theta0))
{
theta = roots[i].re;
}
}
break;
}
ok = Math.abs(theta - theta0) < m_pi;
return theta;
}
// bool
// ClothoidCurve::setup_forward( double _x0,
// double _y0,
// double _theta0,
// double _k,
// double _x1,
// double _y1,
// double tol ) {
//
// x0 = _x0 ;
// y0 = _y0 ;
// theta0 = _theta0 ;
// k = _k ;
// s_min = 0 ;
//
//// Compute guess angles
// double len = hypot( _y1-_y0, _x1-_x0 ) ;
// double arot = atan2( _y1-_y0, _x1-_x0 ) ;
// double th0 = theta0 - arot ;
//// normalize angle
// while ( th0 > m_pi ) th0 -= m_2pi ;
// while ( th0 < -m_pi ) th0 += m_2pi ;
//
//// solve the problem from (0,0) to (1,0)
// double k0 = k*len ;
// double alpha = 2.6 ;
// double thmin = max(-m_pi,-theta0/2-alpha) ;
// double thmax = min( m_pi,-theta0/2+alpha) ;
// double Kmin = kappa( th0, thmax ) ;
// double Kmax = kappa( th0, thmin ) ;
// bool ok ;
// double th = theta_guess( th0, max(min(k0,Kmax),Kmin), ok ) ;
// if ( ok ) {
// for ( int iter = 0 ; iter < 10 ; ++iter ) {
// double dk, L, k_1, dk_1, L_1, k_2, dk_2, L_2 ;
// buildClothoid( 0, 0, th0,
// 1, 0, th,
// k, dk, L, k_1, dk_1, L_1, k_2, dk_2, L_2 ) ;
// double f = k - k0 ;
// double df = k_2 ;
// double dth = f/df ;
// th -= dth ;
// if ( abs(dth) < tol && abs(f) < tol ) {
//// transform solution
// buildClothoid( x0, y0, theta0,
// _x1, _y1, arot + th,
// _k, dk, s_max ) ;
// return true ;
// }
// }
// }
// return false ;
// }
//
// void
// ClothoidCurve::change_origin( double s0 ) {
// double new_theta, new_kappa, new_x0, new_y0 ;
// eval( s0, new_theta, new_kappa, new_x0, new_y0 ) ;
// x0 = new_x0 ;
// y0 = new_y0 ;
// theta0 = new_theta ;
// k = new_kappa ;
// s_min -= s0 ;
// s_max -= s0 ;
// }
//
// bool
// ClothoidCurve::bbTriangle( double offs,
// double p0[2],
// double p1[2],
// double p2[2] ) const {
// double theta_max = theta( s_max ) ;
// double theta_min = theta( s_min ) ;
// double dtheta = Math.abs( theta_max-theta_min ) ;
// if ( dtheta < m_pi_2 ) {
// double alpha, t0[2] ;
// eval( s_min, offs, p0[0], p0[1] ) ;
// eval_D( s_min, t0[0], t0[1] ) ; // no offset
// if ( dtheta > 0.0001 * m_pi_2 ) {
// double t1[2] ;
// eval( s_max, offs, p1[0], p1[1] ) ;
// eval_D( s_max, t1[0], t1[1] ) ; // no offset
//// risolvo il sistema
//// p0 + alpha * t0 = p1 + beta * t1
//// alpha * t0 - beta * t1 = p1 - p0
// double det = t1[0]*t0[1]-t0[0]*t1[1] ;
// alpha = ((p1[1]-p0[1])*t1[0] - (p1[0]-p0[0])*t1[1])/det ;
// } else {
//// se angolo troppo piccolo uso approx piu rozza
// alpha = s_max - s_min ;
// }
// p2[0] = p0[0] + alpha*t0[0] ;
// p2[1] = p0[1] + alpha*t0[1] ;
// return true ;
// } else {
// return false ;
// }
// }
//
// void
// ClothoidCurve::bbSplit( double split_angle,
// double split_size,
// double split_offs,
// vector