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 xArray double[]; ?
* @param yArray double[]; ?
*/
private static void evalXYaLarge(final int nk, final double a, final double b, final double[] xArray, final double[] yArray)
{
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];
xArray[0] = cg * dC0 - s * sg * dS0;
yArray[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;
xArray[1] = cg * DC - s * sg * DS;
yArray[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;
xArray[2] = cg * DC - s * sg * DS;
yArray[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 zArray double[]; ?
* @param yArray double[]; ?
*/
private static void evalXYazero(final int nk, final double b, final double[] zArray, final double[] yArray)
{
double sb = Math.sin(b);
double cb = Math.cos(b);
double b2 = b * b;
if (Math.abs(b) < 1e-3)
{
zArray[0] = 1 - (b2 / 6) * (1 - (b2 / 20) * (1 - (b2 / 42)));
yArray[0] = (b / 2) * (1 - (b2 / 12) * (1 - (b2 / 30)));
}
else
{
zArray[0] = sb / b;
yArray[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)
{
zArray[k] = (sb - k * yArray[k - 1]) / b;
yArray[k] = (k * zArray[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);
zArray[k] = (k * A * rLa + B * rLb + cb) / (1 + k);
yArray[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