static void spiro_seg_to_bpath(const double ks[4], double x0, double y0, double x1, double y1, bezctx *bc, int depth) { double bend = fabs(ks[0]) + fabs(.5 * ks[1]) + fabs(.125 * ks[2]) + fabs((1./48) * ks[3]); if (!bend > 1e-8) { bezctx_lineto(bc, x1, y1); } else { double seg_ch = hypot(x1 - x0, y1 - y0); double seg_th = atan2(y1 - y0, x1 - x0); double xy[2]; double ch, th; double scale, rot; double th_even, th_odd; double ul, vl; double ur, vr; integrate_spiro(ks, xy); ch = hypot(xy[0], xy[1]); th = atan2(xy[1], xy[0]); scale = seg_ch / ch; rot = seg_th - th; if (depth > 5 || bend < 1.) { th_even = (1./384) * ks[3] + (1./8) * ks[1] + rot; th_odd = (1./48) * ks[2] + .5 * ks[0]; ul = (scale * (1./3)) * cos(th_even - th_odd); vl = (scale * (1./3)) * sin(th_even - th_odd); ur = (scale * (1./3)) * cos(th_even + th_odd); vr = (scale * (1./3)) * sin(th_even + th_odd); bezctx_curveto(bc, x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1); } else { /* subdivide */ double ksub[4]; double thsub; double xysub[2]; double xmid, ymid; double cth, sth; ksub[0] = .5 * ks[0] - .125 * ks[1] + (1./64) * ks[2] - (1./768) * ks[3]; ksub[1] = .25 * ks[1] - (1./16) * ks[2] + (1./128) * ks[3]; ksub[2] = .125 * ks[2] - (1./32) * ks[3]; ksub[3] = (1./16) * ks[3]; thsub = rot - .25 * ks[0] + (1./32) * ks[1] - (1./384) * ks[2] + (1./6144) * ks[3]; cth = .5 * scale * cos(thsub); sth = .5 * scale * sin(thsub); integrate_spiro(ksub, xysub); xmid = x0 + cth * xysub[0] - sth * xysub[1]; ymid = y0 + cth * xysub[1] + sth * xysub[0]; spiro_seg_to_bpath(ksub, x0, y0, xmid, ymid, bc, depth + 1); ksub[0] += .25 * ks[1] + (1./384) * ks[3]; ksub[1] += .125 * ks[2]; ksub[2] += (1./16) * ks[3]; spiro_seg_to_bpath(ksub, xmid, ymid, x1, y1, bc, depth + 1); } } }
int test_integ(void) { double ks[] = {1, 2, 3, 4}; double xy[2]; double xynom[2]; double ch, th; int i, j; int nsubdiv; n = ORDER < 6 ? 4096 : 1024; integrate_spiro(ks, xynom); nsubdiv = ORDER < 12 ? 8 : 7; for (i = 0; i < nsubdiv; i++) { double st, en; double err; int n_iter = (1 << (20 - i)); n = 1 << i; st = get_time(); for (j = 0; j < n_iter; j++) integrate_spiro(ks, xy); en = get_time(); err = hypot(xy[0] - xynom[0], xy[1] - xynom[1]); #ifdef VERBOSE printf("%d %d %g %g\n", ORDER, n, (en - st) / n_iter, err); #endif ch = hypot(xy[0], xy[1]); th = atan2(xy[1], xy[0]); #if 0 printf("n = %d: integ(%g %g %g %g) = %g %g, ch = %g, th = %g\n", n, ks[0], ks[1], ks[2], ks[3], xy[0], xy[1], ch, th); printf("%d: %g %g\n", n, xy[0] - xynom[0], xy[1] - xynom[1]); #endif } return 0; }
static double compute_ends(const double ks[4], double ends[2][4], double seg_ch) { double xy[2]; double ch, th; double l, l2, l3; double th_even, th_odd; double k0_even, k0_odd; double k1_even, k1_odd; double k2_even, k2_odd; integrate_spiro(ks, xy, N_IS); ch = hypot(xy[0], xy[1]); th = atan2(xy[1], xy[0]); l = ch / seg_ch; th_even = .5 * ks[0] + (1./48) * ks[2]; th_odd = .125 * ks[1] + (1./384) * ks[3] - th; ends[0][0] = th_even - th_odd; ends[1][0] = th_even + th_odd; k0_even = l * (ks[0] + .125 * ks[2]); k0_odd = l * (.5 * ks[1] + (1./48) * ks[3]); ends[0][1] = k0_even - k0_odd; ends[1][1] = k0_even + k0_odd; l2 = l * l; k1_even = l2 * (ks[1] + .125 * ks[3]); k1_odd = l2 * .5 * ks[2]; ends[0][2] = k1_even - k1_odd; ends[1][2] = k1_even + k1_odd; l3 = l2 * l; k2_even = l3 * ks[2]; k2_odd = l3 * .5 * ks[3]; ends[0][3] = k2_even - k2_odd; ends[1][3] = k2_even + k2_odd; return l; }
static void spiro_seg_to_bpath0(const double ks[4], double *dm, double x0, double y0, double x1, double y1, bezctx *bc, int ncq, int depth) { double bend, seg_ch, seg_th, ch, th, scale, rot; double th_even, th_odd, ul, vl, ur, vr; double thsub, xmid, ymid, cth, sth; double ksub[4], xysub[2], xy[2]; bend = fabs(ks[0]) + fabs(.5 * ks[1]) + fabs(.125 * ks[2]) + fabs((1./48) * ks[3]); if (bend <= 1e-8) { if (depth >= 0 || depth < -4) { #ifdef VERBOSE printf("...to next knot point...\n"); #endif bezctx_lineto(bc, dm[3], dm[4]); } else { bezctx_lineto(bc, (x1 * dm[0] + dm[1]), (y1 * dm[0] + dm[2])); } } else { seg_ch = hypot(x1 - x0, y1 - y0); seg_th = atan2(y1 - y0, x1 - x0); integrate_spiro(ks, xy, N_IS); ch = hypot(xy[0], xy[1]); th = atan2(xy[1], xy[0]); scale = seg_ch / ch; rot = seg_th - th; if (abs(depth) > 5 || bend < 1.) { th_even = (1./384) * ks[3] + (1./8) * ks[1] + rot; th_odd = (1./48) * ks[2] + .5 * ks[0]; ul = (scale * (1./3)) * cos(th_even - th_odd); vl = (scale * (1./3)) * sin(th_even - th_odd); ur = (scale * (1./3)) * cos(th_even + th_odd); vr = (scale * (1./3)) * sin(th_even + th_odd); if (depth >= 0 || depth < -4) { #ifdef VERBOSE printf("...to next knot point...\n"); #endif bezctx_curveto(bc, ((x0 + ul) * dm[0] + dm[1]), ((y0 + vl) * dm[0] + dm[2]), ((x1 - ur) * dm[0] + dm[1]), ((y1 - vr) * dm[0] + dm[2]), dm[3], dm[4]); } else { bezctx_curveto(bc, ((x0 + ul) * dm[0] + dm[1]), ((y0 + vl) * dm[0] + dm[2]), ((x1 - ur) * dm[0] + dm[1]), ((y1 - vr) * dm[0] + dm[2]), (x1 * dm[0] + dm[1]), (y1 * dm[0] + dm[2])); } } else { /* subdivide */ #ifdef VERBOSE printf("...subdivide curve...\n"); #endif ksub[0] = .5 * ks[0] - .125 * ks[1] + (1./64) * ks[2] - (1./768) * ks[3]; ksub[1] = .25 * ks[1] - (1./16) * ks[2] + (1./128) * ks[3]; ksub[2] = .125 * ks[2] - (1./32) * ks[3]; ksub[3] = (1./16) * ks[3]; thsub = rot - .25 * ks[0] + (1./32) * ks[1] - (1./384) * ks[2] + (1./6144) * ks[3]; cth = .5 * scale * cos(thsub); sth = .5 * scale * sin(thsub); integrate_spiro(ksub, xysub, N_IS); xmid = x0 + cth * xysub[0] - sth * xysub[1]; ymid = y0 + cth * xysub[1] + sth * xysub[0]; spiro_seg_to_bpath0(ksub, dm, x0, y0, xmid, ymid, bc, ncq, -(abs(depth) + 1)); ksub[0] += .25 * ks[1] + (1./384) * ks[3]; ksub[1] += .125 * ks[2]; ksub[2] += (1./16) * ks[3]; spiro_seg_to_bpath0(ksub, dm, xmid, ymid, x1, y1, bc, ncq, (depth >= 0 ? ++depth : --depth)); } } }
void print_seg(const double ks[4], double x0, double y0, double x1, double y1) { double bend = fabs(ks[0]) + fabs(.5 * ks[1]) + fabs(.125 * ks[2]) + fabs((1./48) * ks[3]); if (bend < 1e-8) { #ifdef VERBOSE printf("%g %g lineto\n", x1, y1); #endif } else { double seg_ch = hypot(x1 - x0, y1 - y0); double seg_th = atan2(y1 - y0, x1 - x0); double xy[2]; double ch, th; double scale, rot; double th_even, th_odd; double ul, vl; double ur, vr; integrate_spiro(ks, xy); ch = hypot(xy[0], xy[1]); th = atan2(xy[1], xy[0]); scale = seg_ch / ch; rot = seg_th - th; if (bend < 1.) { th_even = (1./384) * ks[3] + (1./8) * ks[1] + rot; th_odd = (1./48) * ks[2] + .5 * ks[0]; ul = (scale * (1./3)) * cos(th_even - th_odd); vl = (scale * (1./3)) * sin(th_even - th_odd); ur = (scale * (1./3)) * cos(th_even + th_odd); vr = (scale * (1./3)) * sin(th_even + th_odd); #ifdef VERBOSE printf("%g %g %g %g %g %g curveto\n", x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1); #endif } else { /* subdivide */ double ksub[4]; double thsub; double xysub[2]; double xmid, ymid; double cth, sth; ksub[0] = .5 * ks[0] - .125 * ks[1] + (1./64) * ks[2] - (1./768) * ks[3]; ksub[1] = .25 * ks[1] - (1./16) * ks[2] + (1./128) * ks[3]; ksub[2] = .125 * ks[2] - (1./32) * ks[3]; ksub[3] = (1./16) * ks[3]; thsub = rot - .25 * ks[0] + (1./32) * ks[1] - (1./384) * ks[2] + (1./6144) * ks[3]; cth = .5 * scale * cos(thsub); sth = .5 * scale * sin(thsub); integrate_spiro(ksub, xysub); xmid = x0 + cth * xysub[0] - sth * xysub[1]; ymid = y0 + cth * xysub[1] + sth * xysub[0]; print_seg(ksub, x0, y0, xmid, ymid); ksub[0] += .25 * ks[1] + (1./384) * ks[3]; ksub[1] += .125 * ks[2]; ksub[2] += (1./16) * ks[3]; print_seg(ksub, xmid, ymid, x1, y1); } } }