diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c index 922c4dd6d5..d37bb5bc71 100644 --- a/gsk/gskcurve.c +++ b/gsk/gskcurve.c @@ -77,8 +77,9 @@ struct _GskCurveClass GskBoundingBox *bounds); void (* get_tight_bounds) (const GskCurve *curve, GskBoundingBox *bounds); - void (* get_derivative) (const GskCurve *curve, - GskCurve *derivative); + void (* get_derivative_at) (const GskCurve *curve, + float t, + graphene_point_t *value); int (* get_crossing) (const GskCurve *curve, const graphene_point_t *point); }; @@ -366,16 +367,14 @@ gsk_line_curve_get_bounds (const GskCurve *curve, } static void -gsk_line_curve_get_derivative (const GskCurve *curve, - GskCurve *deriv) +gsk_line_curve_get_derivative_at (const GskCurve *curve, + float t, + graphene_point_t *value) { const GskLineCurve *self = &curve->line; - graphene_point_t p; - p.x = self->points[1].x - self->points[0].x; - p.y = self->points[1].y - self->points[0].y; - - gsk_line_curve_init_from_points (&deriv->line, GSK_PATH_LINE, &p, &p); + value->x = self->points[1].x - self->points[0].x; + value->y = self->points[1].y - self->points[0].y; } static int @@ -404,7 +403,7 @@ static const GskCurveClass GSK_LINE_CURVE_CLASS = { gsk_line_curve_decompose_curve, gsk_line_curve_get_bounds, gsk_line_curve_get_bounds, - gsk_line_curve_get_derivative, + gsk_line_curve_get_derivative_at, gsk_line_curve_get_crossing, }; @@ -762,6 +761,16 @@ gsk_quad_curve_get_derivative (const GskCurve *curve, gsk_line_curve_init_from_points (&deriv->line, GSK_PATH_LINE, &p[0], &p[1]); } +static void +gsk_quad_curve_get_derivative_at (const GskCurve *curve, + float t, + graphene_point_t *value) +{ + GskCurve d; + gsk_quad_curve_get_derivative (curve, &d); + gsk_curve_get_point (&d, t, value); +} + static int gsk_quad_curve_get_crossing (const GskCurve *curve, const graphene_point_t *point) @@ -788,7 +797,7 @@ static const GskCurveClass GSK_QUAD_CURVE_CLASS = { gsk_quad_curve_decompose_curve, gsk_quad_curve_get_bounds, gsk_quad_curve_get_tight_bounds, - gsk_quad_curve_get_derivative, + gsk_quad_curve_get_derivative_at, gsk_quad_curve_get_crossing, }; @@ -975,8 +984,22 @@ pow3 (float w) return w * w * w; } -static void gsk_cubic_curve_get_derivative (const GskCurve *curve, - GskCurve *deriv); +static void +gsk_cubic_curve_get_derivative (const GskCurve *curve, + GskCurve *deriv) +{ + const GskCubicCurve *self = &curve->cubic; + graphene_point_t p[3]; + + p[0].x = 3.f * (self->points[1].x - self->points[0].x); + p[0].y = 3.f * (self->points[1].y - self->points[0].y); + p[1].x = 3.f * (self->points[2].x - self->points[1].x); + p[1].y = 3.f * (self->points[2].y - self->points[1].y); + p[2].x = 3.f * (self->points[3].x - self->points[2].x); + p[2].y = 3.f * (self->points[3].y - self->points[2].y); + + gsk_quad_curve_init_from_points (&deriv->quad, p); +} static float gsk_cubic_curve_get_curvature (const GskCurve *curve, @@ -1201,20 +1224,13 @@ gsk_cubic_curve_get_tight_bounds (const GskCurve *curve, } static void -gsk_cubic_curve_get_derivative (const GskCurve *curve, - GskCurve *deriv) +gsk_cubic_curve_get_derivative_at (const GskCurve *curve, + float t, + graphene_point_t *value) { - const GskCubicCurve *self = &curve->cubic; - graphene_point_t p[3]; - - p[0].x = 3.f * (self->points[1].x - self->points[0].x); - p[0].y = 3.f * (self->points[1].y - self->points[0].y); - p[1].x = 3.f * (self->points[2].x - self->points[1].x); - p[1].y = 3.f * (self->points[2].y - self->points[1].y); - p[2].x = 3.f * (self->points[3].x - self->points[2].x); - p[2].y = 3.f * (self->points[3].y - self->points[2].y); - - gsk_quad_curve_init_from_points (&deriv->quad, p); + GskCurve d; + gsk_cubic_curve_get_derivative (curve, &d); + gsk_curve_get_point (&d, t, value); } static int @@ -1243,7 +1259,7 @@ static const GskCurveClass GSK_CUBIC_CURVE_CLASS = { gsk_cubic_curve_decompose_curve, gsk_cubic_curve_get_bounds, gsk_cubic_curve_get_tight_bounds, - gsk_cubic_curve_get_derivative, + gsk_cubic_curve_get_derivative_at, gsk_cubic_curve_get_crossing, }; @@ -1407,35 +1423,53 @@ gsk_conic_curve_get_point (const GskCurve *curve, gsk_conic_curve_eval_point (self, t, pos); } +/* See M. Floater, Derivatives of rational Bezier curves */ +static void +gsk_conic_curve_get_derivative_at (const GskCurve *curve, + float t, + graphene_point_t *value) +{ + const GskConicCurve *self = &curve->conic; + float w = gsk_conic_curve_get_weight (self); + const graphene_point_t *pts = self->points; + graphene_point_t p[3], p1[2]; + float w1[2], w2; + + /* The tangent will be 0 in these corner cases, just + * treat it like a line here. + */ + if ((t <= 0.f && graphene_point_equal (&pts[0], &pts[1])) || + (t >= 1.f && graphene_point_equal (&pts[1], &pts[3]))) + { + graphene_point_init (value, pts[3].x - pts[0].x, pts[3].y - pts[0].y); + return; + } + + p[0] = pts[0]; + p[1] = pts[1]; + p[2] = pts[3]; + + w1[0] = (1 - t) + t*w; + w1[1] = (1 - t)*w + t; + + w2 = (1 - t) * w1[0] + t * w1[1]; + + p1[0].x = ((1 - t)*p[0].x + t*w*p[1].x)/w1[0]; + p1[0].y = ((1 - t)*p[0].y + t*w*p[1].y)/w1[0]; + p1[1].x = ((1 - t)*w*p[1].x + t*p[2].x)/w1[1]; + p1[1].y = ((1 - t)*w*p[1].y + t*p[2].y)/w1[1]; + + value->x = 2 * (w1[0] * w1[1])/(w2*w2) * (p1[1].x - p1[0].x); + value->y = 2 * (w1[0] * w1[1])/(w2*w2) * (p1[1].y - p1[0].y); +} + static void gsk_conic_curve_get_tangent (const GskCurve *curve, float t, graphene_vec2_t *tangent) { - const GskConicCurve *self = &curve->conic; graphene_point_t tmp; - float w = gsk_conic_curve_get_weight (self); - const graphene_point_t *pts = self->points; - - /* The tangent will be 0 in these corner cases, just - * treat it like a line here. */ - if ((t <= 0.f && graphene_point_equal (&pts[0], &pts[1])) || - (t >= 1.f && graphene_point_equal (&pts[1], &pts[3]))) - { - graphene_vec2_init (tangent, pts[3].x - pts[0].x, pts[3].y - pts[0].y); - return; - } - - gsk_curve_eval_quad ((graphene_point_t[3]) { - GRAPHENE_POINT_INIT ((w - 1) * (pts[3].x - pts[0].x), - (w - 1) * (pts[3].y - pts[0].y)), - GRAPHENE_POINT_INIT (pts[3].x - pts[0].x - 2 * w * (pts[1].x - pts[0].x), - pts[3].y - pts[0].y - 2 * w * (pts[1].y - pts[0].y)), - GRAPHENE_POINT_INIT (w * (pts[1].x - pts[0].x), - w * (pts[1].y - pts[0].y)) - }, - t, - &tmp); + gsk_conic_curve_get_derivative_at (curve, t, &tmp); graphene_vec2_init (tangent, tmp.x, tmp.y); graphene_vec2_normalize (tangent, tangent); } @@ -1911,7 +1945,7 @@ static const GskCurveClass GSK_CONIC_CURVE_CLASS = { gsk_conic_curve_decompose_curve, gsk_conic_curve_get_bounds, gsk_conic_curve_get_tight_bounds, - NULL, /* FIXME */ + gsk_conic_curve_get_derivative_at, gsk_conic_curve_get_crossing, }; @@ -2106,10 +2140,11 @@ gsk_curve_get_tight_bounds (const GskCurve *curve, } void -gsk_curve_get_derivative (const GskCurve *curve, - GskCurve *deriv) +gsk_curve_get_derivative_at (const GskCurve *curve, + float t, + graphene_point_t *value) { - get_class (curve->op)->get_derivative (curve, deriv); + get_class (curve->op)->get_derivative_at (curve, t, value); } int diff --git a/gsk/gskcurveprivate.h b/gsk/gskcurveprivate.h index 170c92133f..6655ec7864 100644 --- a/gsk/gskcurveprivate.h +++ b/gsk/gskcurveprivate.h @@ -164,8 +164,9 @@ void gsk_curve_get_bounds (const GskCurve void gsk_curve_get_tight_bounds (const GskCurve *curve, GskBoundingBox *bounds); -void gsk_curve_get_derivative (const GskCurve *curve, - GskCurve *derivative); +void gsk_curve_get_derivative_at (const GskCurve *curve, + float t, + graphene_point_t *value); int gsk_curve_get_crossing (const GskCurve *curve, const graphene_point_t *point); gboolean gsk_curve_get_closest_point (const GskCurve *curve, diff --git a/testsuite/gsk/curve.c b/testsuite/gsk/curve.c index f3d31f74f5..610041b77b 100644 --- a/testsuite/gsk/curve.c +++ b/testsuite/gsk/curve.c @@ -342,26 +342,22 @@ test_curve_split (void) static void test_curve_derivative (void) { - GskCurve c, d; + GskCurve c; float t; graphene_vec2_t t1, t2; graphene_point_t p; for (int i = 0; i < 100; i++) { - /* No derivatives for conics */ - init_random_curve_with_op (&c, GSK_PATH_LINE, GSK_PATH_CUBIC); - - gsk_curve_get_derivative (&c, &d); + init_random_curve (&c); for (int j = 0; j < 100; j++) { t = g_test_rand_double_range (0, 1); + gsk_curve_get_derivative_at (&c, t, &p); gsk_curve_get_tangent (&c, t, &t1); - gsk_curve_get_point (&d, t, &p); graphene_vec2_init (&t2, p.x, p.y); graphene_vec2_normalize (&t2, &t2); - g_assert_true (graphene_vec2_near (&t1, &t2, 0.1)); } }