From db430ccc4e5ead157b5a4fdfff54a3fc5adb62b4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 7 Apr 2022 14:15:28 -0400 Subject: [PATCH] curve: Add some curve utilities Internal apis: - gsk_curve_get_curvature_points - gsk_curve_get_cusps These apis will be used in the stroker and in path ops. --- gsk/gskcurve.c | 161 ++++++++++++++++++++++++++++++++++++++++++ gsk/gskcurveprivate.h | 6 ++ 2 files changed, 167 insertions(+) diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c index b1b82acad5..5703002d48 100644 --- a/gsk/gskcurve.c +++ b/gsk/gskcurve.c @@ -1472,6 +1472,167 @@ gsk_curve_reverse (const GskCurve *curve, get_class (curve->op)->reverse (curve, reverse); } +static inline void +_sincosf (float angle, + float *out_s, + float *out_c) +{ +#ifdef HAVE_SINCOSF + sincosf (angle, out_s, out_c); +#else + *out_s = sinf (angle); + *out_c = cosf (angle); +#endif +} + +static void +align_points (const graphene_point_t *p, + const graphene_point_t *a, + const graphene_point_t *b, + graphene_point_t *q, + int n) +{ + graphene_vec2_t n1; + float angle; + float s, c; + + get_tangent (a, b, &n1); + angle = - atan2 (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1)); + _sincosf (angle, &s, &c); + + for (int i = 0; i < n; i++) + { + q[i].x = (p[i].x - a->x) * c - (p[i].y - a->y) * s; + q[i].y = (p[i].x - a->x) * s + (p[i].y - a->y) * c; + } +} + +/* find solutions for at^2 + bt + c = 0 */ +static int +solve_quadratic (float a, float b, float c, float t[2]) +{ + float d; + int n = 0; + + if (fabs (a) > 0.0001) + { + if (b*b > 4*a*c) + { + d = sqrt (b*b - 4*a*c); + t[n++] = (-b + d)/(2*a); + t[n++] = (-b - d)/(2*a); + } + else + { + t[n++] = -b / (2*a); + } + } + else if (fabs (b) > 0.0001) + { + t[n++] = -c / b; + } + + return n; +} + +static int +filter_allowable (float t[3], + int n) +{ + float g[3]; + int j = 0; + + for (int i = 0; i < n; i++) + if (0 < t[i] && t[i] < 1) + g[j++] = t[i]; + for (int i = 0; i < j; i++) + t[i] = g[i]; + return j; +} + +/* Get the points where the curvature of curve is + * zero, or a maximum or minimum, inside the open + * interval from 0 to 1. + */ +int +gsk_curve_get_curvature_points (const GskCurve *curve, + float t[3]) +{ + const graphene_point_t *pts = curve->cubic.points; + graphene_point_t p[4]; + float a, b, c, d; + float x, y, z; + int n; + + if (curve->op != GSK_PATH_CUBIC) + return 0; + + align_points (pts, &pts[0], &pts[3], p, 4); + + a = p[2].x * p[1].y; + b = p[3].x * p[1].y; + c = p[1].x * p[2].y; + d = p[3].x * p[2].y; + + x = - 3*a + 2*b + 3*c - d; + y = 3*a - b - 3*c; + z = c - a; + + n = solve_quadratic (x, y, z, t); + return filter_allowable (t, n); +} + +/* Find cusps inside the open interval from 0 to 1. + * + * According to Stone & deRose, A Geometric Characterization + * of Parametric Cubic curves, a necessary and sufficient + * condition is that the first derivative vanishes. + */ +int +gsk_curve_get_cusps (const GskCurve *curve, + float t[2]) +{ + const graphene_point_t *pts = curve->cubic.points; + graphene_point_t p[3]; + float ax, bx, cx; + float ay, by, cy; + float tx[3]; + int nx; + int n = 0; + + if (curve->op != GSK_PATH_CUBIC) + return 0; + + p[0].x = 3 * (pts[1].x - pts[0].x); + p[0].y = 3 * (pts[1].y - pts[0].y); + p[1].x = 3 * (pts[2].x - pts[1].x); + p[1].y = 3 * (pts[2].y - pts[1].y); + p[2].x = 3 * (pts[3].x - pts[2].x); + p[2].y = 3 * (pts[3].y - pts[2].y); + + ax = p[0].x - 2 * p[1].x + p[2].x; + bx = - 2 * p[0].x + 2 * p[1].x; + cx = p[0].x; + + nx = solve_quadratic (ax, bx, cx, tx); + nx = filter_allowable (tx, nx); + + ay = p[0].y - 2 * p[1].y + p[2].y; + by = - 2 * p[0].y + 2 * p[1].y; + cy = p[0].y; + + for (int i = 0; i < nx; i++) + { + float ti = tx[i]; + + if (0 < ti && ti < 1 && + fabs (ay * ti * ti + by * ti + cy) < 0.001) + t[n++] = ti; + } + + return n; +} + /* Replace a line by an equivalent quad, * and a quad by an equivalent cubic. */ diff --git a/gsk/gskcurveprivate.h b/gsk/gskcurveprivate.h index 0302b89a9f..dbf25200a8 100644 --- a/gsk/gskcurveprivate.h +++ b/gsk/gskcurveprivate.h @@ -153,6 +153,12 @@ void gsk_curve_raise (const GskCurve GskCurve *raised); +int gsk_curve_get_curvature_points (const GskCurve *curve, + float t[3]); + +int gsk_curve_get_cusps (const GskCurve *curve, + float t[2]); + G_END_DECLS #endif /* __GSK_CURVE_PRIVATE_H__ */