123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 |
- /*
- 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
- }
|