Change curve derivative api

We don't need to have the derivative as a curve,
it is enough for us to compute values of the
derivative at a given t, which we can also do
for conics.
This commit is contained in:
Matthias Clasen
2023-08-24 23:29:50 -04:00
parent 25e6231a88
commit 5bc82f4141
3 changed files with 95 additions and 63 deletions

View File

@@ -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

View File

@@ -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,

View File

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