Add gsk_curve_get_bounds

Add getters for bounding boxes of curves.
This commit is contained in:
Matthias Clasen
2020-12-07 01:22:39 -05:00
parent 6d04869c85
commit 3efd6c9a12
2 changed files with 206 additions and 5 deletions

View File

@@ -59,6 +59,10 @@ struct _GskCurveClass
graphene_vec2_t *tangent);
void (* get_end_tangent) (const GskCurve *curve,
graphene_vec2_t *tangent);
void (* get_bounds) (const GskCurve *curve,
graphene_rect_t *bounds);
void (* get_tight_bounds) (const GskCurve *curve,
graphene_rect_t *bounds);
};
static void
@@ -203,6 +207,17 @@ gsk_line_curve_get_start_end_tangent (const GskCurve *curve,
get_tangent (&self->points[0], &self->points[1], tangent);
}
static void
gsk_line_curve_get_bounds (const GskCurve *curve,
graphene_rect_t *bounds)
{
const GskLineCurve *self = &curve->line;
const graphene_point_t *pts = self->points;
graphene_rect_init (bounds, pts[0].x, pts[0].y, 0, 0);
graphene_rect_expand (bounds, &pts[1], bounds);
}
static const GskCurveClass GSK_LINE_CURVE_CLASS = {
gsk_line_curve_init,
gsk_line_curve_init_foreach,
@@ -215,7 +230,9 @@ static const GskCurveClass GSK_LINE_CURVE_CLASS = {
gsk_line_curve_get_start_point,
gsk_line_curve_get_end_point,
gsk_line_curve_get_start_end_tangent,
gsk_line_curve_get_start_end_tangent
gsk_line_curve_get_start_end_tangent,
gsk_line_curve_get_bounds,
gsk_line_curve_get_bounds
};
/** CURVE **/
@@ -431,6 +448,93 @@ gsk_curve_curve_get_end_tangent (const GskCurve *curve,
get_tangent (&self->points[2], &self->points[3], tangent);
}
static void
gsk_curve_curve_get_bounds (const GskCurve *curve,
graphene_rect_t *bounds)
{
const GskCurveCurve *self = &curve->curve;
const graphene_point_t *pts = self->points;
graphene_rect_init (bounds, pts[0].x, pts[0].y, 0, 0);
graphene_rect_expand (bounds, &pts[1], bounds);
graphene_rect_expand (bounds, &pts[2], bounds);
graphene_rect_expand (bounds, &pts[3], bounds);
}
static inline gboolean
acceptable (float t)
{
return 0 <= t && t <= 1;
}
/* Solve P' = 0 where P is
* P = (1-t)^3*pa + 3*t*(1-t)^2*pb + 3*t^2*(1-t)*pc + t^3*pd
*/
static int
get_cubic_extrema (float pa, float pb, float pc, float pd, float t[2])
{
float a, b, c;
float d, tt;
int n = 0;
a = 3 * (pd - 3*pc + 3*pb - pa);
b = 6 * (pc - 2*pb + pa);
c = 3 * (pb - pa);
if (fabs (a) > 0.0001)
{
if (b*b > 4*a*c)
{
d = sqrt (b*b - 4*a*c);
tt = (-b + d)/(2*a);
if (acceptable (tt))
t[n++] = tt;
tt = (-b - d)/(2*a);
if (acceptable (tt))
t[n++] = tt;
}
else
{
tt = -b / (2*a);
if (acceptable (tt))
t[n++] = tt;
}
}
else if (fabs (b) > 0.0001)
{
tt = -c / b;
if (acceptable (tt))
t[n++] = tt;
}
return n;
}
static void
gsk_curve_curve_get_tight_bounds (const GskCurve *curve,
graphene_rect_t *bounds)
{
const GskCurveCurve *self = &curve->curve;
const graphene_point_t *pts = self->points;
float t[4];
int n;
graphene_rect_init (bounds, pts[0].x, pts[0].y, 0, 0);
graphene_rect_expand (bounds, &pts[3], bounds);
n = 0;
n += get_cubic_extrema (pts[0].x, pts[1].x, pts[2].x, pts[3].x, &t[n]);
n += get_cubic_extrema (pts[0].y, pts[1].y, pts[2].y, pts[3].y, &t[n]);
for (int i = 0; i < n; i++)
{
graphene_point_t p;
gsk_curve_curve_get_point (curve, t[i], &p);
graphene_rect_expand (bounds, &p, bounds);
}
}
static const GskCurveClass GSK_CURVE_CURVE_CLASS = {
gsk_curve_curve_init,
gsk_curve_curve_init_foreach,
@@ -443,7 +547,9 @@ static const GskCurveClass GSK_CURVE_CURVE_CLASS = {
gsk_curve_curve_get_start_point,
gsk_curve_curve_get_end_point,
gsk_curve_curve_get_start_tangent,
gsk_curve_curve_get_end_tangent
gsk_curve_curve_get_end_tangent,
gsk_curve_curve_get_bounds,
gsk_curve_curve_get_tight_bounds
};
/** CONIC **/
@@ -846,6 +952,84 @@ gsk_conic_curve_get_end_tangent (const GskCurve *curve,
get_tangent (&self->points[1], &self->points[3], tangent);
}
static void
gsk_conic_curve_get_bounds (const GskCurve *curve,
graphene_rect_t *bounds)
{
const GskCurveCurve *self = &curve->curve;
const graphene_point_t *pts = self->points;
graphene_rect_init (bounds, pts[0].x, pts[0].y, 0, 0);
graphene_rect_expand (bounds, &pts[1], bounds);
graphene_rect_expand (bounds, &pts[3], bounds);
}
/* Solve N = 0 where N is the numerator of (P/Q)', with
* P = (1-t)^2*a + 2*t*(1-t)*w*b + t^2*c
* Q = (1-t)^2 + 2*t*(1-t)*w + t^2
*/
static int
get_conic_extrema (float a, float b, float c, float w, float t[4])
{
float q, tt;
int n = 0;
float w2 = w*w;
float wac = (w - 1)*(a - c);
if (wac != 0)
{
q = - sqrt (a*a - 4*a*b*w2 + 4*a*c*w2 - 2*a*c + 4*b*b*w2 - 4*b*c*w2 + c*c);
tt = (- q + 2*a*w - a - 2*b*w + c)/(2*wac);
if (acceptable (tt))
t[n++] = tt;
tt = (q + 2*a*w - a - 2*b*w + c)/(2*wac);
if (acceptable (tt))
t[n++] = tt;
}
if (w * (b - c) != 0 && a == c)
t[n++] = 0.5;
if (w == 1 && a - 2*b + c != 0)
{
tt = (a - b) / (a - 2*b + c);
if (acceptable (tt))
t[n++] = tt;
}
return n;
}
static void
gsk_conic_curve_get_tight_bounds (const GskCurve *curve,
graphene_rect_t *bounds)
{
const GskConicCurve *self = &curve->conic;
float w = gsk_conic_curve_get_weight (self);
const graphene_point_t *pts = self->points;
float t[8];
int n;
graphene_rect_init (bounds, pts[0].x, pts[0].y, 0, 0);
graphene_rect_expand (bounds, &pts[3], bounds);
n = 0;
n += get_conic_extrema (pts[0].x, pts[1].x, pts[3].x, w, &t[n]);
n += get_conic_extrema (pts[0].y, pts[1].y, pts[3].y, w, &t[n]);
for (int i = 0; i < n; i++)
{
graphene_point_t p;
gsk_conic_curve_get_point (curve, t[i], &p);
graphene_rect_expand (bounds, &p, bounds);
}
}
static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
gsk_conic_curve_init,
gsk_conic_curve_init_foreach,
@@ -858,7 +1042,9 @@ static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
gsk_conic_curve_get_start_point,
gsk_conic_curve_get_end_point,
gsk_conic_curve_get_start_tangent,
gsk_conic_curve_get_end_tangent
gsk_conic_curve_get_end_tangent,
gsk_conic_curve_get_bounds,
gsk_conic_curve_get_tight_bounds
};
/** API **/
@@ -976,3 +1162,16 @@ gsk_curve_get_end_tangent (const GskCurve *curve,
get_class (curve->op)->get_end_tangent (curve, tangent);
}
void
gsk_curve_get_bounds (const GskCurve *curve,
graphene_rect_t *bounds)
{
get_class (curve->op)->get_bounds (curve, bounds);
}
void
gsk_curve_get_tight_bounds (const GskCurve *curve,
graphene_rect_t *bounds)
{
get_class (curve->op)->get_tight_bounds (curve, bounds);
}

View File

@@ -113,8 +113,10 @@ void gsk_curve_get_start_tangent (const GskCurve
graphene_vec2_t *tangent);
void gsk_curve_get_end_tangent (const GskCurve *curve,
graphene_vec2_t *tangent);
void gsk_curve_get_bounds (const GskCurve *curve,
graphene_rect_t *bounds);
void gsk_curve_get_tight_bounds (const GskCurve *curve,
graphene_rect_t *bounds);
G_END_DECLS