/* ---------------------------------------------------------------------- */ /* Stage 4: smoothing and corner analysis (Sec. 2.3.3) */ static void reverse(Path path) { privcurve curve = path.curve; int m = curve.n; dPoint[] v = curve.vertex; int i, j; dPoint tmp; for (i = 0, j = m - 1; i < j; i++, j--) { tmp = v[i]; v[i] = v[j]; v[j] = tmp; } }
/* ---------------------------------------------------------------------- */ /* Stage 5: Curve optimization (Sec. 2.4) */ /* 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(Path path, int i, int j, Opti res, double opttolerance, int[] convc, double[] areac) { int m = path.curve.n; privcurve curve = path.curve; dPoint[] vertex = curve.vertex; int k, k1, k2, conv, i1; double area, alpha, d, d1, d2; dPoint p0, p1, p2, p3, pt; double A, R, A1, A2, A3, A4, 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(vertex[i], 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(vertex[i], vertex[i1], vertex[k1], vertex[k2])) != conv) { return(1); } if (iprod1(vertex[i], vertex[i1], vertex[k1], vertex[k2]) < d * ddist(vertex[k1], vertex[k2]) * -0.999847695156) { return(1); } } /* the curve we're working in: */ p0 = curve.c[mod(i, m) * 3 + 2].copy(); p1 = vertex[mod(i + 1, m)].copy(); p2 = vertex[mod(j, m)].copy(); p3 = curve.c[mod(j, m) * 3 + 2].copy(); /* determine its area */ area = areac[j] - areac[i]; area -= dpara(vertex[0], curve.c[i * 3 + 2], curve.c[j * 3 + 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 = 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 - Math.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].copy(); p2 = res.c[1].copy(); /* 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, vertex[k], vertex[k1]); if (t < -0.5) { return(1); } pt = bezier(t, p0, p1, p2, p3); d = ddist(vertex[k], vertex[k1]); if (d == 0.0) { /* this should never happen */ return(1); } d1 = dpara(vertex[k], vertex[k1], pt) / d; if (Math.Abs(d1) > opttolerance) { return(1); } if (iprod(vertex[k], vertex[k1], pt) < 0 || iprod(vertex[k1], vertex[k], pt) < 0) { return(1); } res.pen += d1 * d1; } /* check corners */ for (k = i; k != j; k = k1) { k1 = mod(k + 1, m); t = tangent(p0, p1, p2, p3, curve.c[k * 3 + 2], curve.c[k1 * 3 + 2]); if (t < -0.5) { return(1); } pt = bezier(t, p0, p1, p2, p3); d = ddist(curve.c[k * 3 + 2], curve.c[k1 * 3 + 2]); if (d == 0.0) { /* this should never happen */ return(1); } d1 = dpara(curve.c[k * 3 + 2], curve.c[k1 * 3 + 2], pt) / d; d2 = dpara(curve.c[k * 3 + 2], curve.c[k1 * 3 + 2], vertex[k1]) / d; d2 *= 0.75 * curve.alpha[k1]; if (d2 < 0) { d1 = -d1; d2 = -d2; } if (d1 < d2 - opttolerance) { return(1); } if (d1 < d2) { res.pen += (d1 - d2) * (d1 - d2); } } return(0); }
/* Always succeeds and returns 0 */ static void smooth(Path path, double alphamax) { int m = path.curve.n; privcurve curve = path.curve; if (path.sign == "-") { reverse(path); } int i, j, k; double dd, denom, alpha; dPoint p2, p3, p4; /* 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 = Math.Abs(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) { curve.tag[j] = POTRACE_CORNER; curve.c[3 * j + 1] = curve.vertex[j]; curve.c[3 * j + 2] = p4; } else { if (alpha < 0.55) { alpha = 0.55; } else if (alpha > 1) { alpha = 1; } p2 = interval(0.5 + 0.5 * alpha, curve.vertex[i], curve.vertex[j]); p3 = interval(0.5 + 0.5 * alpha, curve.vertex[k], curve.vertex[j]); curve.tag[j] = POTRACE_CURVETO; curve.c[3 * j + 0] = p2; curve.c[3 * j + 1] = p3; curve.c[3 * j + 2] = p4; } curve.alpha[j] = alpha; /* store the "cropped" value of alpha */ curve.beta[j] = 0.5; } curve.alphacurve = 1; }
/* 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 void optiCurve(Path path, double opttolerance) { privcurve curve = path.curve; int m = curve.n; dPoint[] vert = curve.vertex; int[] pt = new int[m + 1]; double[] pen = new double[m + 1]; int[] len = new int[m + 1]; Opti[] opt = new Opti[m + 1]; Opti o = new Opti(); int om, i, j, r; dPoint p0; int i1; double area, alpha; privcurve ocurve; int[] convc = new int[m]; /* conv[m]: pre-computed convexities */ double[] areac = new double[m + 1]; /* pre-calculate convexity: +1 = right turn, -1 = left turn, 0 = corner */ for (i = 0; i < m; i++) { if (curve.tag[i] == POTRACE_CURVETO) { convc[i] = sign(dpara(vert[mod(i - 1, m)], vert[i], vert[mod(i + 1, m)])); } else { convc[i] = 0; } } /* pre-calculate areas */ area = 0.0; areac[0] = 0.0; p0 = curve.vertex[0]; for (i = 0; i < m; i++) { i1 = mod(i + 1, m); if (curve.tag[i1] == POTRACE_CURVETO) { alpha = curve.alpha[i1]; area += 0.3 * alpha * (4 - alpha) * dpara(curve.c[i * 3 + 2], vert[i1], curve.c[i1 * 3 + 2]) / 2; area += dpara(p0, curve.c[i * 3 + 2], curve.c[i1 * 3 + 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(path, i, mod(j, m), o, opttolerance, convc, areac); if (r == 1) { 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; o = new Opti(); } } } om = len[m]; ocurve = new privcurve(om); double[] s = new double[om]; double[] t = new double[om]; j = m; for (i = om - 1; i >= 0; i--) { if (pt[j] == j - 1) { ocurve.tag[i] = curve.tag[mod(j, m)]; ocurve.c[i * 3 + 0] = curve.c[mod(j, m) * 3 + 0]; ocurve.c[i * 3 + 1] = curve.c[mod(j, m) * 3 + 1]; ocurve.c[i * 3 + 2] = curve.c[mod(j, m) * 3 + 2]; ocurve.vertex[i] = curve.vertex[mod(j, m)]; ocurve.alpha[i] = curve.alpha[mod(j, m)]; ocurve.alpha0[i] = curve.alpha0[mod(j, m)]; ocurve.beta[i] = curve.beta[mod(j, m)]; s[i] = t[i] = 1.0; } else { ocurve.tag[i] = POTRACE_CURVETO; ocurve.c[i * 3 + 0] = opt[j].c[0]; ocurve.c[i * 3 + 1] = opt[j].c[1]; ocurve.c[i * 3 + 2] = curve.c[mod(j, m) * 3 + 2]; ocurve.vertex[i] = interval(opt[j].s, curve.c[mod(j, m) * 3 + 2], vert[mod(j, m)]); ocurve.alpha[i] = opt[j].alpha; 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); ocurve.beta[i] = s[i] / (s[i] + t[i1]); } ocurve.alphacurve = 1; path.curve = ocurve; }