diff options
Diffstat (limited to 'third_party/spiro/ppedit/cornu.c')
-rw-r--r-- | third_party/spiro/ppedit/cornu.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/third_party/spiro/ppedit/cornu.c b/third_party/spiro/ppedit/cornu.c new file mode 100644 index 0000000..b0eac68 --- /dev/null +++ b/third_party/spiro/ppedit/cornu.c @@ -0,0 +1,615 @@ +/* +ppedit - A pattern plate editor for Spiro splines. +Copyright (C) 2007 Raph Levien + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +*/ +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include "bezctx_intf.h" + +/* The computation of fresnel integrals is adapted from: */ + +/* +Cephes Math Library Release 2.1: December, 1988 +Copyright 1984, 1987, 1988 by Stephen L. Moshier +Direct inquiries to 30 Frost Street, Cambridge, MA 02140 +*/ + +double polevl( double x, double coef[], int N ) +{ +double ans; +int i; +double *p; + +p = coef; +ans = *p++; +i = N; + +do + ans = ans * x + *p++; +while( --i ); + +return( ans ); +} + +/* p1evl() */ +/* N + * Evaluate polynomial when coefficient of x is 1.0. + * Otherwise same as polevl. + */ + +double p1evl( double x, double coef[], int N ) +{ +double ans; +double *p; +int i; + +p = coef; +ans = x + *p++; +i = N-1; + +do + ans = ans * x + *p++; +while( --i ); + +return( ans ); +} + +static double sn[6] = { +-2.99181919401019853726E3, + 7.08840045257738576863E5, +-6.29741486205862506537E7, + 2.54890880573376359104E9, +-4.42979518059697779103E10, + 3.18016297876567817986E11, +}; +static double sd[6] = { +/* 1.00000000000000000000E0,*/ + 2.81376268889994315696E2, + 4.55847810806532581675E4, + 5.17343888770096400730E6, + 4.19320245898111231129E8, + 2.24411795645340920940E10, + 6.07366389490084639049E11, +}; + +static double cn[6] = { +-4.98843114573573548651E-8, + 9.50428062829859605134E-6, +-6.45191435683965050962E-4, + 1.88843319396703850064E-2, +-2.05525900955013891793E-1, + 9.99999999999999998822E-1, +}; +static double cd[7] = { + 3.99982968972495980367E-12, + 9.15439215774657478799E-10, + 1.25001862479598821474E-7, + 1.22262789024179030997E-5, + 8.68029542941784300606E-4, + 4.12142090722199792936E-2, + 1.00000000000000000118E0, +}; +static double fn[10] = { + 4.21543555043677546506E-1, + 1.43407919780758885261E-1, + 1.15220955073585758835E-2, + 3.45017939782574027900E-4, + 4.63613749287867322088E-6, + 3.05568983790257605827E-8, + 1.02304514164907233465E-10, + 1.72010743268161828879E-13, + 1.34283276233062758925E-16, + 3.76329711269987889006E-20, +}; +static double fd[10] = { +/* 1.00000000000000000000E0,*/ + 7.51586398353378947175E-1, + 1.16888925859191382142E-1, + 6.44051526508858611005E-3, + 1.55934409164153020873E-4, + 1.84627567348930545870E-6, + 1.12699224763999035261E-8, + 3.60140029589371370404E-11, + 5.88754533621578410010E-14, + 4.52001434074129701496E-17, + 1.25443237090011264384E-20, +}; +static double gn[11] = { + 5.04442073643383265887E-1, + 1.97102833525523411709E-1, + 1.87648584092575249293E-2, + 6.84079380915393090172E-4, + 1.15138826111884280931E-5, + 9.82852443688422223854E-8, + 4.45344415861750144738E-10, + 1.08268041139020870318E-12, + 1.37555460633261799868E-15, + 8.36354435630677421531E-19, + 1.86958710162783235106E-22, +}; +static double gd[11] = { +/* 1.00000000000000000000E0,*/ + 1.47495759925128324529E0, + 3.37748989120019970451E-1, + 2.53603741420338795122E-2, + 8.14679107184306179049E-4, + 1.27545075667729118702E-5, + 1.04314589657571990585E-7, + 4.60680728146520428211E-10, + 1.10273215066240270757E-12, + 1.38796531259578871258E-15, + 8.39158816283118707363E-19, + 1.86958710162783236342E-22, +}; + +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif + +int fresnl( xxa, ssa, cca ) +double xxa, *ssa, *cca; +{ +double f, g, cc, ss, c, s, t, u; +double x, x2; + +x = fabs(xxa); +x2 = x * x; +if( x2 < 2.5625 ) + { + t = x2 * x2; + ss = x * x2 * polevl( t, sn, 5)/p1evl( t, sd, 6 ); + cc = x * polevl( t, cn, 5)/polevl(t, cd, 6 ); + goto done; + } + + + + + +#if 0 +/* Note by RLL: the cutoff here seems low to me; perhaps it should be + eliminated altogether. */ +if( x > 36974.0 ) + { + cc = 0.5; + ss = 0.5; + goto done; + } +#endif + +/* Asymptotic power series auxiliary functions + * for large argument + */ + x2 = x * x; + t = M_PI * x2; + u = 1.0/(t * t); + t = 1.0/t; + f = 1.0 - u * polevl( u, fn, 9)/p1evl(u, fd, 10); + g = t * polevl( u, gn, 10)/p1evl(u, gd, 11); + + t = M_PI_2 * x2; + c = cos(t); + s = sin(t); + t = M_PI * x; + cc = 0.5 + (f * s - g * c)/t; + ss = 0.5 - (f * c + g * s)/t; + +done: +if( xxa < 0.0 ) + { + cc = -cc; + ss = -ss; + } + +*cca = cc; +*ssa = ss; +return(0); +} + +/* + End section adapted from Cephes math library. The following code is + by Raph Levien. +*/ + +void eval_cornu(double t, double *ps, double *pc) +{ + double s, c; + double rspio2 = 0.7978845608028653; /* 1 / sqrt(pi/2) */ + double spio2 = 1.2533141373155; /* sqrt(pi/2) */ + + fresnl(t * rspio2, &s, &c); + *ps = s * spio2; + *pc = c * spio2; +} + +double mod_2pi(double th) { + double u = th * (1 / (2 * M_PI)); + return 2 * M_PI * (u - floor(u + 0.5)); +} + +void fit_cornu_half(double th0, double th1, + double *pt0, double *pt1, + double *pk0, double *pk1) +{ + int i; + const int max_iter = 21; + double t0, t1, t_m; + double tl, tr, t_est; + double s0, c0, s1, c1; + /* This implementation uses bisection, which is simple but almost + certainly not the fastest converging. If time is of the essence, + use something like Newton-Raphson. */ + + if (fabs(th0 + th1) < 1e-6) { + th0 += 1e-6; + th1 += 1e-6; + } + t_est = 0.29112 * (th1 + th0) / sqrt(th1 - th0); + tl = t_est * .9; + tr = t_est * 2; + for (i = 0; i < max_iter; i++) { + double dt; + double chord_th; + + t_m = .5 * (tl + tr); + dt = (th0 + th1) / (4 * t_m); + t0 = t_m - dt; + t1 = t_m + dt; + eval_cornu(t0, &s0, &c0); + eval_cornu(t1, &s1, &c1); + chord_th = atan2(s1 - s0, c1 - c0); + if (mod_2pi(chord_th - t0 * t0 - th0) < 0) + tl = t_m; + else + tr = t_m; + } + *pt0 = t0; + *pt1 = t1; + if (pk0 || pk1) { + double chordlen = hypot(s1 - s0, c1 - c0); + if (pk0) *pk0 = t0 * chordlen; + if (pk1) *pk1 = t1 * chordlen; + } +} + +/* Most of the time, this should give a fairly tight, yet conservative, + (meaning it won't underestimate) approximation of the maximum error + between a Cornu spiral segment and its quadratic Bezier fit. +*/ +double +est_cornu_error(double t0, double t1) +{ + double t, u, est; + + if (t0 < 0 || t1 < 0) { + t0 = -t0; + t1 = -t1; + } + if (t1 < 0) { + fprintf(stderr, "unexpected t1 sign\n"); + } + if (t1 < t0) { + double tmp = t0; + t0 = t1; + t1 = tmp; + } + if (fabs(t0) < 1e-9) { + est = t1 * t1 * t1; + est *= .017256 - .0059 - est * t1; + } else { + t = t1 - t0; + t *= t; + t *= t; + est = t * fabs(t0 + t1 - 1.22084) / (t0 + t1); + u = t0 + t1 + .6; + u = u * u * u; + est *= .014 * (.6 * u + 1); + est += t * (t1 - t0) * .004; + } + return est; +} + +void +affine_pt(const double aff[6], double x, double y, double *px, double *py) { + *px = x * aff[0] + y * aff[2] + aff[4]; + *py = x * aff[1] + y * aff[3] + aff[5]; +} + +void +affine_multiply(double dst[6], const double src1[6], const double src2[6]) +{ + double d0, d1, d2, d3, d4, d5; + d0 = src1[0] * src2[0] + src1[1] * src2[2]; + d1 = src1[0] * src2[1] + src1[1] * src2[3]; + d2 = src1[2] * src2[0] + src1[3] * src2[2]; + d3 = src1[2] * src2[1] + src1[3] * src2[3]; + d4 = src1[4] * src2[0] + src1[5] * src2[2] + src2[4]; + d5 = src1[4] * src2[1] + src1[5] * src2[3] + src2[5]; + dst[0] = d0; + dst[1] = d1; + dst[2] = d2; + dst[3] = d3; + dst[4] = d4; + dst[5] = d5; +} + +void +fit_quadratic(double x0, double y0, double th0, + double x1, double y1, double th1, + double quad[6]) { + double th; + double s0, c0, s1, c1; + double det, s, c; + + th = atan2(y1 - y0, x1 - x0); + s0 = sin(th0 - th); + c0 = cos(th0 - th); + s1 = sin(th - th1); + c1 = cos(th - th1); + det = 1 / (s0 * c1 + s1 * c0); + s = s0 * s1 * det; + c = c0 * s1 * det; + quad[0] = x0; + quad[1] = y0; + quad[2] = x0 + (x1 - x0) * c - (y1 - y0) * s; + quad[3] = y0 + (y1 - y0) * c + (x1 - x0) * s; + quad[4] = x1; + quad[5] = y1; +} + +void +cornu_seg_to_quad(double t0, double t1, const double aff[6], bezctx *bc) +{ + double x0, y0; + double x1, y1; + double th0 = t0 * t0; + double th1 = t1 * t1; + double quad[6]; + double qx1, qy1, qx2, qy2; + + eval_cornu(t0, &y0, &x0); + eval_cornu(t1, &y1, &x1); + fit_quadratic(x0, y0, th0, x1, y1, th1, quad); + affine_pt(aff, quad[2], quad[3], &qx1, &qy1); + affine_pt(aff, quad[4], quad[5], &qx2, &qy2); + bezctx_quadto(bc, qx1, qy1, qx2, qy2); +} + +void +cornu_seg_to_bpath(double t0, double t1, const double aff[6], + bezctx *bc, double tol) +{ + double tm; + + if ((t0 < 0 && t1 > 0) || (t1 < 0 && t0 > 0)) + tm = 0; + else { + if (fabs(t0 * t0 - t1 * t1) < 1.5 && + est_cornu_error(t0, t1) < tol) { + cornu_seg_to_quad(t0, t1, aff, bc); + return; + } +#if 0 + if (fabs(t0 - t1) < 1e-6) { + printf("DIVERGENCE!\007\n"); + return; + + } +#endif + tm = (t0 + t1) * .5; + } + + cornu_seg_to_bpath(t0, tm, aff, bc, tol); + cornu_seg_to_bpath(tm, t1, aff, bc, tol); +} + +void +cornu_to_bpath(const double xs[], const double ys[], const double ths[], int n, + bezctx *bc, double tol, int closed, int kt0, int n_kt) +{ + int i; + + for (i = 0; i < n - 1 + closed; i++) { + double x0 = xs[i], y0 = ys[i]; + int ip1 = (i + 1) % n; + double x1 = xs[ip1], y1 = ys[ip1]; + double th = atan2(y1 - y0, x1 - x0); + double th0 = mod_2pi(ths[i] - th); + double th1 = mod_2pi(th - ths[ip1]); + double t0, t1; + double s0, c0, s1, c1; + double chord_th, chordlen, rot, scale; + double aff[6], aff2[6]; + double flip = -1; + + th1 += 1e-6; + if (th1 < th0) { + double tmp = th0; + th0 = th1; + th1 = tmp; + flip = 1; + } + fit_cornu_half(th0, th1, &t0, &t1, NULL, NULL); + if (flip == 1) { + double tmp = t0; + t0 = t1; + t1 = tmp; + } + eval_cornu(t0, &s0, &c0); + s0 *= flip; + eval_cornu(t1, &s1, &c1); + s1 *= flip; + chord_th = atan2(s1 - s0, c1 - c0); + chordlen = hypot(s1 - s0, c1 - c0); + rot = th - chord_th; + scale = hypot(y1 - y0, x1 - x0) / chordlen; + aff[0] = 1; + aff[1] = 0; + aff[2] = 0; + aff[3] = flip; + aff[4] = -c0; + aff[5] = -s0; + aff2[0] = scale * cos(rot); + aff2[1] = scale * sin(rot); + aff2[2] = -aff2[1]; + aff2[3] = aff2[0]; + aff2[4] = x0; + aff2[5] = y0; + affine_multiply(aff, aff, aff2); + bezctx_mark_knot(bc, (kt0 + i) % n_kt); + cornu_seg_to_bpath(t0, t1, aff, bc, tol / scale); + } +} + +/* fit arc to pts (0, 0), (x, y), and (1, 0), return th tangent to + arc at (x, y) */ +double fit_arc(double x, double y) { + return atan2(y - 2 * x * y, y * y + x - x * x); +} + +void +local_ths(const double xs[], const double ys[], double ths[], int n, + int closed) +{ + int i; + + for (i = 1 - closed; i < n - 1 + closed; i++) { + int im1 = (i + n - 1) % n; + double x0 = xs[im1]; + double y0 = ys[im1]; + double x1 = xs[i]; + double y1 = ys[i]; + int ip1 = (i + 1) % n; + double x2 = xs[ip1]; + double y2 = ys[ip1]; + double dx = x2 - x0; + double dy = y2 - y0; + double ir2 = dx * dx + dy * dy; + double x = ((x1 - x0) * dx + (y1 - y0) * dy) / ir2; + double y = ((y1 - y0) * dx - (x1 - x0) * dy) / ir2; + if (dx == 0.0 && dy == 0.0) + ths[i] = 0.0; + else + ths[i] = fit_arc(x, y) + atan2(dy, dx); + } +} + +void +endpoint_ths(const double xs[], const double ys[], double ths[], int n) +{ + ths[0] = 2 * atan2(ys[1] - ys[0], xs[1] - xs[0]) - ths[1]; + ths[n - 1] = 2 * atan2(ys[n - 1] - ys[n - 2], xs[n - 1] - xs[n-2]) - ths[n - 2]; +} + +void +tweak_ths(const double xs[], const double ys[], double ths[], int n, + double delt, int closed) +{ + double *dks = (double *)malloc(sizeof(double) * n); + int i; + double first_k0, last_k1; + + for (i = 0; i < n - 1 + closed; i++) { + double x0 = xs[i]; + double y0 = ys[i]; + int ip1 = (i + 1) % n; + double x1 = xs[ip1]; + double y1 = ys[ip1]; + double th, th0, th1; + double t0, t1, k0, k1; + double s0, c0, s1, c1; + double scale; + double flip = -1; + + if (x0 == x1 && y0 == y1) { +#ifdef VERBOSE + printf("Overlapping points (i=%d)\n", i); +#endif + /* Very naughty, casting off the constness like this. */ + ((double*) xs)[i] = x1 = x1 + 1e-6; + } + + th = atan2(y1 - y0, x1 - x0); + th0 = mod_2pi(ths[i] - th); + th1 = mod_2pi(th - ths[ip1]); + + th1 += 1e-6; + if (th1 < th0) { + double tmp = th0; + th0 = th1; + th1 = tmp; + flip = 1; + } + fit_cornu_half(th0, th1, &t0, &t1, &k0, &k1); + if (flip == 1) { + double tmp = t0; + t0 = t1; + t1 = tmp; + + tmp = k0; + k0 = k1; + k1 = tmp; + } + eval_cornu(t0, &s0, &c0); + eval_cornu(t1, &s1, &c1); + scale = 1 / hypot(y1 - y0, x1 - x0); + k0 *= scale; + k1 *= scale; + if (i > 0) dks[i] = k0 - last_k1; + else first_k0 = k0; + last_k1 = k1; + } + if (closed) + dks[0] = first_k0 - last_k1; + for (i = 1 - closed; i < n - 1 + closed; i++) { + int im1 = (i + n - 1) % n; + double x0 = xs[im1]; + double y0 = ys[im1]; + double x1 = xs[i]; + double y1 = ys[i]; + int ip1 = (i + 1) % n; + double x2 = xs[ip1]; + double y2 = ys[ip1]; + double chord1 = hypot(x1 - x0, y1 - y0); + double chord2 = hypot(x2 - x1, y2 - y1); + ths[i] -= delt * dks[i] * chord1 * chord2 / (chord1 + chord2); + } + free(dks); +} + +void test_cornu(void) +{ +#if 0 + int i; + for (i = -10; i < 100; i++) { + double t = 36974 * 1.2533141373155 + i * .1; + double s, c; + eval_cornu(t, &s, &c); + printf("%g %g %g\n", t, s, c); + } +#else + double t0, t1; + fit_cornu_half(0, 1, &t0, &t1, 0, 0); + printf("%g %g\n", t0, t1); +#endif +} |