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.
This commit is contained in:
Matthias Clasen
2020-12-10 18:19:28 -05:00
parent afd398112b
commit 5f40db5043
2 changed files with 153 additions and 3 deletions

View File

@@ -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);
}

View File

@@ -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__ */