void opticurve (int m) { int i; for (i = 0; i < m; i++) { dpara(mod (i - 1, m)); } }
/* Always succeeds and returns 0 */ static int smooth(privcurve_t *curve, int sign, double alphamax) { int m = curve->n; int i, j, k; double dd, denom, alpha; dpoint_t p2, p3, p4; if (sign == '-') { /* reverse orientation of negative paths */ for (i=0, j=m-1; i<j; i++, j--) { dpoint_t tmp; tmp = curve->vertex[i]; curve->vertex[i] = curve->vertex[j]; curve->vertex[j] = tmp; } } /* examine each vertex and find its best fit */ for (i=0; i<m; i++) { j = mod(i+1, m); k = mod(i+2, m); p4 = interval(1/2.0, curve->vertex[k], curve->vertex[j]); denom = ddenom(curve->vertex[i], curve->vertex[k]); if (denom != 0.0) { dd = dpara(curve->vertex[i], curve->vertex[j], curve->vertex[k]) / denom; dd = fabs(dd); alpha = dd>1 ? (1 - 1.0/dd) : 0; alpha = alpha / 0.75; } else { alpha = 4/3.0; } curve->alpha0[j] = alpha; /* remember "original" value of alpha */ if (alpha > alphamax) { /* pointed corner */ curve->tag[j] = POTRACE_CORNER; curve->c[j][1] = curve->vertex[j]; curve->c[j][2] = p4; } else { if (alpha < 0.55) { alpha = 0.55; } else if (alpha > 1) { alpha = 1; } p2 = interval(.5+.5*alpha, curve->vertex[i], curve->vertex[j]); p3 = interval(.5+.5*alpha, curve->vertex[k], curve->vertex[j]); curve->tag[j] = POTRACE_CURVETO; curve->c[j][0] = p2; curve->c[j][1] = p3; curve->c[j][2] = p4; } curve->alpha[j] = alpha; /* store the "cropped" value of alpha */ curve->beta[j] = 0.5; } curve->alphacurve = 1; return 0; }
/* calculate best fit from i+.5 to j+.5. Assume i<j (cyclically). Return 0 and set badness and parameters (alpha, beta), if possible. Return 1 if impossible. */ static int opti_penalty(privpath_t *pp, int i, int j, opti_t *res, double opttolerance, int *convc, double *areac) { int m = pp->curve.n; int k, k1, k2, conv, i1; double area, alpha, d, d1, d2; dpoint_t p0, p1, p2, p3, pt; double A, R, A1, A2, A3, A4; double s, t; /* check convexity, corner-freeness, and maximum bend < 179 degrees */ if (i==j) { /* sanity - a full loop can never be an opticurve */ return 1; } k = i; i1 = mod(i+1, m); k1 = mod(k+1, m); conv = convc[k1]; if (conv == 0) { return 1; } d = ddist(pp->curve.vertex[i], pp->curve.vertex[i1]); for (k=k1; k!=j; k=k1) { k1 = mod(k+1, m); k2 = mod(k+2, m); if (convc[k1] != conv) { return 1; } if (sign(cprod(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2])) != conv) { return 1; } if (iprod1(pp->curve.vertex[i], pp->curve.vertex[i1], pp->curve.vertex[k1], pp->curve.vertex[k2]) < d * ddist(pp->curve.vertex[k1], pp->curve.vertex[k2]) * COS179) { return 1; } } /* the curve we're working in: */ p0 = pp->curve.c[mod(i,m)][2]; p1 = pp->curve.vertex[mod(i+1,m)]; p2 = pp->curve.vertex[mod(j,m)]; p3 = pp->curve.c[mod(j,m)][2]; /* determine its area */ area = areac[j] - areac[i]; area -= dpara(pp->curve.vertex[0], pp->curve.c[i][2], pp->curve.c[j][2])/2; if (i>=j) { area += areac[m]; } /* find intersection o of p0p1 and p2p3. Let t,s such that o = interval(t,p0,p1) = interval(s,p3,p2). Let A be the area of the triangle (p0,o,p3). */ A1 = dpara(p0, p1, p2); A2 = dpara(p0, p1, p3); A3 = dpara(p0, p2, p3); /* A4 = dpara(p1, p2, p3); */ A4 = A1+A3-A2; if (A2 == A1) { /* this should never happen */ return 1; } t = A3/(A3-A4); s = A2/(A2-A1); A = A2 * t / 2.0; if (A == 0.0) { /* this should never happen */ return 1; } R = area / A; /* relative area */ alpha = 2 - sqrt(4 - R / 0.3); /* overall alpha for p0-o-p3 curve */ res->c[0] = interval(t * alpha, p0, p1); res->c[1] = interval(s * alpha, p3, p2); res->alpha = alpha; res->t = t; res->s = s; p1 = res->c[0]; p2 = res->c[1]; /* the proposed curve is now (p0,p1,p2,p3) */ res->pen = 0; /* calculate penalty */ /* check tangency with edges */ for (k=mod(i+1,m); k!=j; k=k1) { k1 = mod(k+1,m); t = tangent(p0, p1, p2, p3, pp->curve.vertex[k], pp->curve.vertex[k1]); if (t<-.5) { return 1; } pt = bezier(t, p0, p1, p2, p3); d = ddist(pp->curve.vertex[k], pp->curve.vertex[k1]); if (d == 0.0) { /* this should never happen */ return 1; } d1 = dpara(pp->curve.vertex[k], pp->curve.vertex[k1], pt) / d; if (fabs(d1) > opttolerance) { return 1; } if (iprod(pp->curve.vertex[k], pp->curve.vertex[k1], pt) < 0 || iprod(pp->curve.vertex[k1], pp->curve.vertex[k], pt) < 0) { return 1; } res->pen += sq(d1); } /* check corners */ for (k=i; k!=j; k=k1) { k1 = mod(k+1,m); t = tangent(p0, p1, p2, p3, pp->curve.c[k][2], pp->curve.c[k1][2]); if (t<-.5) { return 1; } pt = bezier(t, p0, p1, p2, p3); d = ddist(pp->curve.c[k][2], pp->curve.c[k1][2]); if (d == 0.0) { /* this should never happen */ return 1; } d1 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pt) / d; d2 = dpara(pp->curve.c[k][2], pp->curve.c[k1][2], pp->curve.vertex[k1]) / d; d2 *= 0.75 * pp->curve.alpha[k1]; if (d2 < 0) { d1 = -d1; d2 = -d2; } if (d1 < d2 - opttolerance) { return 1; } if (d1 < d2) { res->pen += sq(d1 - d2); } } return 0; }
/* optimize the path p, replacing sequences of Bezier segments by a single segment when possible. Return 0 on success, 1 with errno set on failure. */ static int opticurve(privpath_t *pp, double opttolerance) { int m = pp->curve.n; int *pt = NULL; /* pt[m+1] */ double *pen = NULL; /* pen[m+1] */ int *len = NULL; /* len[m+1] */ opti_t *opt = NULL; /* opt[m+1] */ int om; int i,j,r; opti_t o; dpoint_t p0; int i1; double area; double alpha; double *s = NULL; double *t = NULL; int *convc = NULL; /* conv[m]: pre-computed convexities */ double *areac = NULL; /* cumarea[m+1]: cache for fast area computation */ SAFE_MALLOC(pt, m+1, int); SAFE_MALLOC(pen, m+1, double); SAFE_MALLOC(len, m+1, int); SAFE_MALLOC(opt, m+1, opti_t); SAFE_MALLOC(convc, m, int); SAFE_MALLOC(areac, m+1, double); /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ for (i=0; i<m; i++) { if (pp->curve.tag[i] == POTRACE_CURVETO) { convc[i] = sign(dpara(pp->curve.vertex[mod(i-1,m)], pp->curve.vertex[i], pp->curve.vertex[mod(i+1,m)])); } else { convc[i] = 0; } } /* pre-calculate areas */ area = 0.0; areac[0] = 0.0; p0 = pp->curve.vertex[0]; for (i=0; i<m; i++) { i1 = mod(i+1, m); if (pp->curve.tag[i1] == POTRACE_CURVETO) { alpha = pp->curve.alpha[i1]; area += 0.3*alpha*(4-alpha)*dpara(pp->curve.c[i][2], pp->curve.vertex[i1], pp->curve.c[i1][2])/2; area += dpara(p0, pp->curve.c[i][2], pp->curve.c[i1][2])/2; } areac[i+1] = area; } pt[0] = -1; pen[0] = 0; len[0] = 0; /* Fixme: we always start from a fixed point -- should find the best curve cyclically ### */ for (j=1; j<=m; j++) { /* calculate best path from 0 to j */ pt[j] = j-1; pen[j] = pen[j-1]; len[j] = len[j-1]+1; for (i=j-2; i>=0; i--) { r = opti_penalty(pp, i, mod(j,m), &o, opttolerance, convc, areac); if (r) { break; } if (len[j] > len[i]+1 || (len[j] == len[i]+1 && pen[j] > pen[i] + o.pen)) { pt[j] = i; pen[j] = pen[i] + o.pen; len[j] = len[i] + 1; opt[j] = o; } } } om = len[m]; r = privcurve_init(&pp->ocurve, om); if (r) { goto malloc_error; } SAFE_MALLOC(s, om, double); SAFE_MALLOC(t, om, double); j = m; for (i=om-1; i>=0; i--) { if (pt[j]==j-1) { pp->ocurve.tag[i] = pp->curve.tag[mod(j,m)]; pp->ocurve.c[i][0] = pp->curve.c[mod(j,m)][0]; pp->ocurve.c[i][1] = pp->curve.c[mod(j,m)][1]; pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; pp->ocurve.vertex[i] = pp->curve.vertex[mod(j,m)]; pp->ocurve.alpha[i] = pp->curve.alpha[mod(j,m)]; pp->ocurve.alpha0[i] = pp->curve.alpha0[mod(j,m)]; pp->ocurve.beta[i] = pp->curve.beta[mod(j,m)]; s[i] = t[i] = 1.0; } else { pp->ocurve.tag[i] = POTRACE_CURVETO; pp->ocurve.c[i][0] = opt[j].c[0]; pp->ocurve.c[i][1] = opt[j].c[1]; pp->ocurve.c[i][2] = pp->curve.c[mod(j,m)][2]; pp->ocurve.vertex[i] = interval(opt[j].s, pp->curve.c[mod(j,m)][2], pp->curve.vertex[mod(j,m)]); pp->ocurve.alpha[i] = opt[j].alpha; pp->ocurve.alpha0[i] = opt[j].alpha; s[i] = opt[j].s; t[i] = opt[j].t; } j = pt[j]; } /* calculate beta parameters */ for (i=0; i<om; i++) { i1 = mod(i+1,om); pp->ocurve.beta[i] = s[i] / (s[i] + t[i1]); } pp->ocurve.alphacurve = 1; free(pt); free(pen); free(len); free(opt); free(s); free(t); free(convc); free(areac); return 0; malloc_error: free(pt); free(pen); free(len); free(opt); free(s); free(t); free(convc); free(areac); return 1; }