From 0f3b1fa768d2556619be44424987d4f586db4039 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 10 Dec 2020 18:19:28 -0500 Subject: [PATCH] Add gsk_curve_offset This method creates an offset curve from an existing curve by just moving the control points laterally. This will be used in stroking. --- gsk/gskcurve.c | 152 +++++++++++++++++++++++++++++++++++++++++- gsk/gskcurveprivate.h | 4 ++ 2 files changed, 153 insertions(+), 3 deletions(-) diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c index d1f6c235c2..775a3c6da4 100644 --- a/gsk/gskcurve.c +++ b/gsk/gskcurve.c @@ -67,6 +67,9 @@ struct _GskCurveClass graphene_rect_t *bounds); void (* get_tight_bounds) (const GskCurve *curve, graphene_rect_t *bounds); + void (* offset) (const GskCurve *curve, + float distance, + GskCurve *offset_curve); }; static void @@ -78,6 +81,60 @@ get_tangent (const graphene_point_t *p0, graphene_vec2_normalize (t, t); } +static void +get_normal (const graphene_point_t *p0, + const graphene_point_t *p1, + graphene_vec2_t *n) +{ + graphene_vec2_init (n, p0->y - p1->y, p1->x - p0->x); + graphene_vec2_normalize (n, n); +} + +/* Compute q = p + d * n */ +static void +scale_point (const graphene_point_t *p, + const graphene_vec2_t *n, + float d, + graphene_point_t *q) +{ + q->x = p->x + d * graphene_vec2_get_x (n); + q->y = p->y + d * graphene_vec2_get_y (n); +} + +/* Set p to the intersection of the lines through a, b and c, d. + * Return the number of intersections found (0 or 1) + */ +static int +line_intersection (const graphene_point_t *a, + const graphene_point_t *b, + const graphene_point_t *c, + const graphene_point_t *d, + graphene_point_t *p) +{ + float a1 = b->y - a->y; + float b1 = a->x - b->x; + float c1 = a1*a->x + b1*a->y; + + float a2 = d->y - c->y; + float b2 = c->x - d->x; + float c2 = a2*c->x+ b2*c->y; + + float det = a1*b2 - a2*b1; + + if (fabs (det) < 0.001) + { + p->x = NAN; + p->y = NAN; + return 0; + } + else + { + p->x = (b2*c1 - b1*c2) / det; + p->y = (a1*c2 - a2*c1) / det; + return 1; + } +} + /** LINE **/ static void @@ -239,6 +296,23 @@ gsk_line_curve_get_bounds (const GskCurve *curve, graphene_rect_expand (bounds, &pts[1], bounds); } +static void +gsk_line_curve_offset (const GskCurve *curve, + float distance, + GskCurve *offset_curve) +{ + const GskLineCurve *self = &curve->line; + const graphene_point_t *pts = self->points; + graphene_vec2_t n; + graphene_point_t p[4]; + + get_normal (&pts[0], &pts[1], &n); + scale_point (&pts[0], &n, distance, &p[0]); + scale_point (&pts[1], &n, distance, &p[1]); + + gsk_curve_init (offset_curve, gsk_pathop_encode (GSK_PATH_LINE, p)); +} + static const GskCurveClass GSK_LINE_CURVE_CLASS = { gsk_line_curve_init, gsk_line_curve_init_foreach, @@ -254,7 +328,8 @@ static const GskCurveClass GSK_LINE_CURVE_CLASS = { gsk_line_curve_get_start_end_tangent, gsk_line_curve_get_start_end_tangent, gsk_line_curve_get_bounds, - gsk_line_curve_get_bounds + gsk_line_curve_get_bounds, + gsk_line_curve_offset }; /** CURVE **/ @@ -568,6 +643,39 @@ gsk_curve_curve_get_tight_bounds (const GskCurve *curve, } } +static void +gsk_curve_curve_offset (const GskCurve *curve, + float distance, + GskCurve *offset) +{ + const GskCurveCurve *self = &curve->curve; + const graphene_point_t *pts = self->points; + graphene_vec2_t n; + graphene_point_t p[4]; + graphene_point_t m1, m2, m3, m4; + + /* Simply scale control points, a la Tiller and Hanson */ + get_normal (&pts[0], &pts[1], &n); + scale_point (&pts[0], &n, distance, &p[0]); + scale_point (&pts[1], &n, distance, &m1); + + get_normal (&pts[1], &pts[2], &n); + scale_point (&pts[1], &n, distance, &m2); + scale_point (&pts[2], &n, distance, &m3); + + get_normal (&pts[2], &pts[3], &n); + scale_point (&pts[2], &n, distance, &m4); + scale_point (&pts[3], &n, distance, &p[3]); + + if (!line_intersection (&p[0], &m1, &m2, &m3, &p[1])) + p[1] = m1; + + if (!line_intersection (&m2, &m3, &m4, &p[3], &p[2])) + p[2] = m4; + + gsk_curve_curve_init_from_points (&offset->curve, p); +} + static const GskCurveClass GSK_CURVE_CURVE_CLASS = { gsk_curve_curve_init, gsk_curve_curve_init_foreach, @@ -583,7 +691,8 @@ static const GskCurveClass GSK_CURVE_CURVE_CLASS = { gsk_curve_curve_get_start_tangent, gsk_curve_curve_get_end_tangent, gsk_curve_curve_get_bounds, - gsk_curve_curve_get_tight_bounds + gsk_curve_curve_get_tight_bounds, + gsk_curve_curve_offset }; /** CONIC **/ @@ -1148,6 +1257,34 @@ gsk_conic_curve_get_tight_bounds (const GskCurve *curve, } } +static void +gsk_conic_curve_offset (const GskCurve *curve, + float distance, + GskCurve *offset) +{ + const GskConicCurve *self = &curve->conic; + const graphene_point_t *pts = self->points; + graphene_vec2_t n; + graphene_point_t p[4]; + graphene_point_t m1, m2; + + /* Simply scale control points, a la Tiller and Hanson */ + get_normal (&pts[0], &pts[1], &n); + scale_point (&pts[0], &n, distance, &p[0]); + scale_point (&pts[1], &n, distance, &m1); + + get_normal (&pts[1], &pts[3], &n); + scale_point (&pts[1], &n, distance, &m2); + scale_point (&pts[3], &n, distance, &p[3]); + + if (!line_intersection (&p[0], &m1, &m2, &p[3], &p[1])) + p[1] = m1; + + p[2] = pts[2]; + + gsk_conic_curve_init_from_points (&offset->conic, p); +} + static const GskCurveClass GSK_CONIC_CURVE_CLASS = { gsk_conic_curve_init, gsk_conic_curve_init_foreach, @@ -1163,7 +1300,8 @@ static const GskCurveClass GSK_CONIC_CURVE_CLASS = { gsk_conic_curve_get_start_tangent, gsk_conic_curve_get_end_tangent, gsk_conic_curve_get_bounds, - gsk_conic_curve_get_tight_bounds + gsk_conic_curve_get_tight_bounds, + gsk_conic_curve_offset }; /** API **/ @@ -1303,3 +1441,11 @@ gsk_curve_get_tight_bounds (const GskCurve *curve, { get_class (curve->op)->get_tight_bounds (curve, bounds); } + +void +gsk_curve_offset (const GskCurve *curve, + float distance, + GskCurve *offset_curve) +{ + get_class (curve->op)->offset (curve, distance, offset_curve); +} diff --git a/gsk/gskcurveprivate.h b/gsk/gskcurveprivate.h index 1ae20e9e44..e4705f915b 100644 --- a/gsk/gskcurveprivate.h +++ b/gsk/gskcurveprivate.h @@ -132,6 +132,10 @@ int gsk_curve_intersect (const GskCurve graphene_point_t *p, int n); +void gsk_curve_offset (const GskCurve *curve, + float distance, + GskCurve *offset_curve); + G_END_DECLS #endif /* __GSK_CURVE_PRIVATE_H__ */