#define __NR_PATH_C__ /* * Pixel buffer rendering library * * Authors: * Lauris Kaplinski * * This code is in public domain */ #include #include "n-art-bpath.h" #include "nr-rect.h" #include "nr-path.h" static void nr_curve_bbox (NR::Coord x000, NR::Coord y000, NR::Coord x001, NR::Coord y001, NR::Coord x011, NR::Coord y011, NR::Coord x111, NR::Coord y111, NRRect *bbox); static void nr_curve_bbox(NR::Point const p000, NR::Point const p001, NR::Point const p011, NR::Point const p111, NRRect *bbox); NRBPath *nr_path_duplicate_transform(NRBPath *d, NRBPath *s, NRMatrix const *transform) { int i; if (!s->path) { d->path = NULL; return d; } i = 0; while (s->path[i].code != NR_END) i += 1; d->path = g_new (NArtBpath, i + 1); i = 0; while (s->path[i].code != NR_END) { d->path[i].code = s->path[i].code; if (s->path[i].code == NR_CURVETO) { d->path[i].x1 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x1, s->path[i].y1); d->path[i].y1 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x1, s->path[i].y1); d->path[i].x2 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x2, s->path[i].y2); d->path[i].y2 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x2, s->path[i].y2); } d->path[i].x3 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x3, s->path[i].y3); d->path[i].y3 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x3, s->path[i].y3); i += 1; } d->path[i].code = NR_END; return d; } NRBPath *nr_path_duplicate_transform(NRBPath *d, NRBPath *s, NR::Matrix const transform) { NRMatrix tr = transform; return nr_path_duplicate_transform(d, s, &tr); } NArtBpath* nr_artpath_affine(NArtBpath *s, NR::Matrix const &aff) { NRBPath bp, abp; bp.path = s; nr_path_duplicate_transform(&abp, &bp, aff); return abp.path; } static void nr_line_wind_distance (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, NR::Point &pt, int *wind, NR::Coord *best) { NR::Coord Ax, Ay, Bx, By, Dx, Dy, s; NR::Coord dist2; /* Find distance */ Ax = x0; Ay = y0; Bx = x1; By = y1; Dx = x1 - x0; Dy = y1 - y0; const NR::Coord Px = pt[NR::X]; const NR::Coord Py = pt[NR::Y]; if (best) { s = ((Px - Ax) * Dx + (Py - Ay) * Dy) / (Dx * Dx + Dy * Dy); if (s <= 0.0) { dist2 = (Px - Ax) * (Px - Ax) + (Py - Ay) * (Py - Ay); } else if (s >= 1.0) { dist2 = (Px - Bx) * (Px - Bx) + (Py - By) * (Py - By); } else { NR::Coord Qx, Qy; Qx = Ax + s * Dx; Qy = Ay + s * Dy; dist2 = (Px - Qx) * (Px - Qx) + (Py - Qy) * (Py - Qy); } if (dist2 < (*best * *best)) *best = sqrt (dist2); } if (wind) { /* Find wind */ if ((Ax >= Px) && (Bx >= Px)) return; if ((Ay >= Py) && (By >= Py)) return; if ((Ay < Py) && (By < Py)) return; if (Ay == By) return; /* Ctach upper y bound */ if (Ay == Py) { if (Ax < Px) *wind -= 1; return; } else if (By == Py) { if (Bx < Px) *wind += 1; return; } else { NR::Coord Qx; /* Have to calculate intersection */ Qx = Ax + Dx * (Py - Ay) / Dy; if (Qx < Px) { *wind += (Dy > 0.0) ? 1 : -1; } } } } static void nr_curve_bbox_wind_distance (NR::Coord x000, NR::Coord y000, NR::Coord x001, NR::Coord y001, NR::Coord x011, NR::Coord y011, NR::Coord x111, NR::Coord y111, NR::Point &pt, NRRect *bbox, int *wind, NR::Coord *best, NR::Coord tolerance) { NR::Coord x0, y0, x1, y1, len2; int needdist, needwind, needline; const NR::Coord Px = pt[NR::X]; const NR::Coord Py = pt[NR::Y]; needdist = 0; needwind = 0; needline = 0; if (bbox) nr_curve_bbox (x000, y000, x001, y001, x011, y011, x111, y111, bbox); x0 = MIN (x000, x001); x0 = MIN (x0, x011); x0 = MIN (x0, x111); y0 = MIN (y000, y001); y0 = MIN (y0, y011); y0 = MIN (y0, y111); x1 = MAX (x000, x001); x1 = MAX (x1, x011); x1 = MAX (x1, x111); y1 = MAX (y000, y001); y1 = MAX (y1, y011); y1 = MAX (y1, y111); if (best) { /* Quicly adjust to endpoints */ len2 = (x000 - Px) * (x000 - Px) + (y000 - Py) * (y000 - Py); if (len2 < (*best * *best)) *best = (NR::Coord) sqrt (len2); len2 = (x111 - Px) * (x111 - Px) + (y111 - Py) * (y111 - Py); if (len2 < (*best * *best)) *best = (NR::Coord) sqrt (len2); if (((x0 - Px) < *best) && ((y0 - Py) < *best) && ((Px - x1) < *best) && ((Py - y1) < *best)) { /* Point is inside sloppy bbox */ /* Now we have to decide, whether subdivide */ /* fixme: (Lauris) */ if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) { needdist = 1; } else { needline = 1; } } } if (!needdist && wind) { if ((y1 >= Py) && (y0 < Py) && (x0 < Px)) { /* Possible intersection at the left */ /* Now we have to decide, whether subdivide */ /* fixme: (Lauris) */ if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) { needwind = 1; } else { needline = 1; } } } if (needdist || needwind) { NR::Coord x00t, x0tt, xttt, x1tt, x11t, x01t; NR::Coord y00t, y0tt, yttt, y1tt, y11t, y01t; NR::Coord s, t; t = 0.5; s = 1 - t; x00t = s * x000 + t * x001; x01t = s * x001 + t * x011; x11t = s * x011 + t * x111; x0tt = s * x00t + t * x01t; x1tt = s * x01t + t * x11t; xttt = s * x0tt + t * x1tt; y00t = s * y000 + t * y001; y01t = s * y001 + t * y011; y11t = s * y011 + t * y111; y0tt = s * y00t + t * y01t; y1tt = s * y01t + t * y11t; yttt = s * y0tt + t * y1tt; nr_curve_bbox_wind_distance (x000, y000, x00t, y00t, x0tt, y0tt, xttt, yttt, pt, NULL, wind, best, tolerance); nr_curve_bbox_wind_distance (xttt, yttt, x1tt, y1tt, x11t, y11t, x111, y111, pt, NULL, wind, best, tolerance); } else if (1 || needline) { nr_line_wind_distance (x000, y000, x111, y111, pt, wind, best); } } void nr_path_matrix_point_bbox_wind_distance (NRBPath *bpath, NR::Matrix const &m, NR::Point &pt, NRRect *bbox, int *wind, NR::Coord *dist, NR::Coord tolerance) { NR::Coord x0, y0, x3, y3; const NArtBpath *p; if (!bpath->path) { if (wind) *wind = 0; if (dist) *dist = NR_HUGE; return; } x0 = y0 = 0.0; x3 = y3 = 0.0; for (p = bpath->path; p->code != NR_END; p+= 1) { switch (p->code) { case NR_MOVETO_OPEN: case NR_MOVETO: x0 = m[0] * p->x3 + m[2] * p->y3 + m[4]; y0 = m[1] * p->x3 + m[3] * p->y3 + m[5]; if (bbox) { bbox->x0 = (NR::Coord) MIN (bbox->x0, x0); bbox->y0 = (NR::Coord) MIN (bbox->y0, y0); bbox->x1 = (NR::Coord) MAX (bbox->x1, x0); bbox->y1 = (NR::Coord) MAX (bbox->y1, y0); } break; case NR_LINETO: x3 = m[0] * p->x3 + m[2] * p->y3 + m[4]; y3 = m[1] * p->x3 + m[3] * p->y3 + m[5]; if (bbox) { bbox->x0 = (NR::Coord) MIN (bbox->x0, x3); bbox->y0 = (NR::Coord) MIN (bbox->y0, y3); bbox->x1 = (NR::Coord) MAX (bbox->x1, x3); bbox->y1 = (NR::Coord) MAX (bbox->y1, y3); } if (dist || wind) { nr_line_wind_distance (x0, y0, x3, y3, pt, wind, dist); } x0 = x3; y0 = y3; break; case NR_CURVETO: x3 = m[0] * p->x3 + m[2] * p->y3 + m[4]; y3 = m[1] * p->x3 + m[3] * p->y3 + m[5]; nr_curve_bbox_wind_distance (x0, y0, m[0] * p->x1 + m[2] * p->y1 + m[4], m[1] * p->x1 + m[3] * p->y1 + m[5], m[0] * p->x2 + m[2] * p->y2 + m[4], m[1] * p->x2 + m[3] * p->y2 + m[5], x3, y3, pt, bbox, wind, dist, tolerance); x0 = x3; y0 = y3; break; default: break; } } } static void nr_curve_bbox(NR::Point const p000, NR::Point const p001, NR::Point const p011, NR::Point const p111, NRRect *bbox) { using NR::X; using NR::Y; nr_curve_bbox(p000[X], p000[Y], p001[X], p001[Y], p011[X], p011[Y], p111[X], p111[Y], bbox); } /* Fast bbox calculation */ /* Thanks to Nathan Hurst for suggesting it */ static void nr_curve_bbox (NR::Coord x000, NR::Coord y000, NR::Coord x001, NR::Coord y001, NR::Coord x011, NR::Coord y011, NR::Coord x111, NR::Coord y111, NRRect *bbox) { NR::Coord a, b, c, D; bbox->x0 = (NR::Coord) MIN (bbox->x0, x111); bbox->y0 = (NR::Coord) MIN (bbox->y0, y111); bbox->x1 = (NR::Coord) MAX (bbox->x1, x111); bbox->y1 = (NR::Coord) MAX (bbox->y1, y111); /* * xttt = s * (s * (s * x000 + t * x001) + t * (s * x001 + t * x011)) + t * (s * (s * x001 + t * x011) + t * (s * x011 + t * x111)) * xttt = s * (s2 * x000 + s * t * x001 + t * s * x001 + t2 * x011) + t * (s2 * x001 + s * t * x011 + t * s * x011 + t2 * x111) * xttt = s * (s2 * x000 + 2 * st * x001 + t2 * x011) + t * (s2 * x001 + 2 * st * x011 + t2 * x111) * xttt = s3 * x000 + 2 * s2t * x001 + st2 * x011 + s2t * x001 + 2st2 * x011 + t3 * x111 * xttt = s3 * x000 + 3s2t * x001 + 3st2 * x011 + t3 * x111 * xttt = s3 * x000 + (1 - s) 3s2 * x001 + (1 - s) * (1 - s) * 3s * x011 + (1 - s) * (1 - s) * (1 - s) * x111 * xttt = s3 * x000 + (3s2 - 3s3) * x001 + (3s - 6s2 + 3s3) * x011 + (1 - 2s + s2 - s + 2s2 - s3) * x111 * xttt = (x000 - 3 * x001 + 3 * x011 - x111) * s3 + * ( 3 * x001 - 6 * x011 + 3 * x111) * s2 + * ( 3 * x011 - 3 * x111) * s + * ( x111) * xttt' = (3 * x000 - 9 * x001 + 9 * x011 - 3 * x111) * s2 + * ( 6 * x001 - 12 * x011 + 6 * x111) * s + * ( 3 * x011 - 3 * x111) */ a = 3 * x000 - 9 * x001 + 9 * x011 - 3 * x111; b = 6 * x001 - 12 * x011 + 6 * x111; c = 3 * x011 - 3 * x111; /* * s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */ if (fabs (a) < NR_EPSILON) { /* s = -c / b */ if (fabs (b) > NR_EPSILON) { double s, t, xttt; s = -c / b; if ((s > 0.0) && (s < 1.0)) { t = 1.0 - s; xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111; bbox->x0 = (float) MIN (bbox->x0, xttt); bbox->x1 = (float) MAX (bbox->x1, xttt); } } } else { /* s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */ D = b * b - 4 * a * c; if (D >= 0.0) { NR::Coord d, s, t, xttt; /* Have solution */ d = sqrt (D); s = (-b + d) / (2 * a); if ((s > 0.0) && (s < 1.0)) { t = 1.0 - s; xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111; bbox->x0 = (NR::Coord) MIN (bbox->x0, xttt); bbox->x1 = (NR::Coord) MAX (bbox->x1, xttt); } s = (-b - d) / (2 * a); if ((s > 0.0) && (s < 1.0)) { t = 1.0 - s; xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111; bbox->x0 = (NR::Coord) MIN (bbox->x0, xttt); bbox->x1 = (NR::Coord) MAX (bbox->x1, xttt); } } } a = 3 * y000 - 9 * y001 + 9 * y011 - 3 * y111; b = 6 * y001 - 12 * y011 + 6 * y111; c = 3 * y011 - 3 * y111; if (fabs (a) < NR_EPSILON) { /* s = -c / b */ if (fabs (b) > NR_EPSILON) { double s, t, yttt; s = -c / b; if ((s > 0.0) && (s < 1.0)) { t = 1.0 - s; yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111; bbox->y0 = (float) MIN (bbox->y0, yttt); bbox->y1 = (float) MAX (bbox->y1, yttt); } } } else { /* s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */ D = b * b - 4 * a * c; if (D >= 0.0) { NR::Coord d, s, t, yttt; /* Have solution */ d = sqrt (D); s = (-b + d) / (2 * a); if ((s > 0.0) && (s < 1.0)) { t = 1.0 - s; yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111; bbox->y0 = (NR::Coord) MIN (bbox->y0, yttt); bbox->y1 = (NR::Coord) MAX (bbox->y1, yttt); } s = (-b - d) / (2 * a); if ((s > 0.0) && (s < 1.0)) { t = 1.0 - s; yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111; bbox->y0 = (NR::Coord) MIN (bbox->y0, yttt); bbox->y1 = (NR::Coord) MAX (bbox->y1, yttt); } } } } void nr_path_matrix_bbox_union(NRBPath const *bpath, NR::Matrix const &m, NRRect *bbox) { using NR::X; using NR::Y; if (!bpath->path) { return; } NR::Point prev0(0, 0); for (NArtBpath const *p = bpath->path; p->code != NR_END; ++p) { switch (p->code) { case NR_MOVETO_OPEN: case NR_MOVETO: { NR::Point const c3(p->x3, p->y3); prev0 = c3 * m; nr_rect_union_pt(bbox, prev0); break; } case NR_LINETO: { NR::Point const c3(p->x3, p->y3); NR::Point const endPt( c3 * m ); nr_rect_union_pt(bbox, endPt); prev0 = endPt; break; } case NR_CURVETO: { NR::Point const c1(p->x1, p->y1); NR::Point const c2(p->x2, p->y2); NR::Point const c3(p->x3, p->y3); NR::Point const endPt( c3 * m ); nr_curve_bbox(prev0, c1 * m, c2 * m, endPt, bbox); prev0 = endPt; break; } default: break; } } }