curve: Add gsk_curve_get_bounds
Add getters for bounding boxes of curves. We have cheap ones, which are just the bounding box of the control points, and tighter ones, which require finding the actual extrema. Bounding boxes are needed to implement intersection via bisecting.
This commit is contained in:
249
gsk/gskcurve.c
249
gsk/gskcurve.c
@@ -70,6 +70,10 @@ struct _GskCurveClass
|
||||
float tolerance,
|
||||
GskCurveAddCurveFunc add_curve_func,
|
||||
gpointer user_data);
|
||||
void (* get_bounds) (const GskCurve *curve,
|
||||
GskBoundingBox *bounds);
|
||||
void (* get_tight_bounds) (const GskCurve *curve,
|
||||
GskBoundingBox *bounds);
|
||||
};
|
||||
|
||||
/* {{{ Utilities */
|
||||
@@ -296,6 +300,16 @@ gsk_line_curve_decompose_curve (const GskCurve *curve,
|
||||
return add_curve_func (GSK_PATH_LINE, self->points, 2, 0.f, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_line_curve_get_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskLineCurve *self = &curve->line;
|
||||
const graphene_point_t *pts = self->points;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[1]);
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_LINE_CURVE_CLASS = {
|
||||
gsk_line_curve_init,
|
||||
gsk_line_curve_init_foreach,
|
||||
@@ -313,6 +327,8 @@ static const GskCurveClass GSK_LINE_CURVE_CLASS = {
|
||||
gsk_line_curve_segment,
|
||||
gsk_line_curve_decompose,
|
||||
gsk_line_curve_decompose_curve,
|
||||
gsk_line_curve_get_bounds,
|
||||
gsk_line_curve_get_bounds,
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
@@ -609,6 +625,58 @@ gsk_quad_curve_decompose_curve (const GskCurve *curve,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_quad_curve_get_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskQuadCurve *self = &curve->quad;
|
||||
const graphene_point_t *pts = self->points;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[2]);
|
||||
gsk_bounding_box_expand (bounds, &pts[1]);
|
||||
}
|
||||
|
||||
/* Solve P' = 0 where P is
|
||||
* P = (1-t)^2*pa + 2*t*(1-t)*pb + t^2*pc
|
||||
*/
|
||||
static int
|
||||
get_quadratic_extrema (float pa, float pb, float pc, float t[1])
|
||||
{
|
||||
float d = pa - 2 * pb + pc;
|
||||
|
||||
if (fabs (d) > 0.0001)
|
||||
{
|
||||
t[0] = (pa - pb) / d;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_quad_curve_get_tight_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskQuadCurve *self = &curve->quad;
|
||||
const graphene_point_t *pts = self->points;
|
||||
float t[4];
|
||||
int n;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[2]);
|
||||
|
||||
n = 0;
|
||||
n += get_quadratic_extrema (pts[0].x, pts[1].x, pts[2].x, &t[n]);
|
||||
n += get_quadratic_extrema (pts[0].y, pts[1].y, pts[2].y, &t[n]);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
graphene_point_t p;
|
||||
|
||||
gsk_quad_curve_get_point (curve, t[i], &p);
|
||||
gsk_bounding_box_expand (bounds, &p);
|
||||
}
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_QUAD_CURVE_CLASS = {
|
||||
gsk_quad_curve_init,
|
||||
gsk_quad_curve_init_foreach,
|
||||
@@ -626,6 +694,8 @@ static const GskCurveClass GSK_QUAD_CURVE_CLASS = {
|
||||
gsk_quad_curve_segment,
|
||||
gsk_quad_curve_decompose,
|
||||
gsk_quad_curve_decompose_curve,
|
||||
gsk_quad_curve_get_bounds,
|
||||
gsk_quad_curve_get_tight_bounds,
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
@@ -1004,6 +1074,91 @@ gsk_cubic_curve_decompose_curve (const GskCurve *curve,
|
||||
&(AddLineData) { add_curve_func, user_data });
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_cubic_curve_get_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskCubicCurve *self = &curve->cubic;
|
||||
const graphene_point_t *pts = self->points;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[3]);
|
||||
gsk_bounding_box_expand (bounds, &pts[1]);
|
||||
gsk_bounding_box_expand (bounds, &pts[2]);
|
||||
}
|
||||
|
||||
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_cubic_curve_get_tight_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskCubicCurve *self = &curve->cubic;
|
||||
const graphene_point_t *pts = self->points;
|
||||
float t[4];
|
||||
int n;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[3]);
|
||||
|
||||
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_cubic_curve_get_point (curve, t[i], &p);
|
||||
gsk_bounding_box_expand (bounds, &p);
|
||||
}
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_CUBIC_CURVE_CLASS = {
|
||||
gsk_cubic_curve_init,
|
||||
gsk_cubic_curve_init_foreach,
|
||||
@@ -1021,6 +1176,8 @@ static const GskCurveClass GSK_CUBIC_CURVE_CLASS = {
|
||||
gsk_cubic_curve_segment,
|
||||
gsk_cubic_curve_decompose,
|
||||
gsk_cubic_curve_decompose_curve,
|
||||
gsk_cubic_curve_get_bounds,
|
||||
gsk_cubic_curve_get_tight_bounds,
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
@@ -1585,6 +1742,82 @@ gsk_conic_curve_decompose_curve (const GskCurve *curve,
|
||||
&(AddLineData) { add_curve_func, user_data });
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_conic_curve_get_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
const GskConicCurve *self = &curve->conic;
|
||||
const graphene_point_t *pts = self->points;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[3]);
|
||||
gsk_bounding_box_expand (bounds, &pts[1]);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
GskBoundingBox *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;
|
||||
|
||||
gsk_bounding_box_init (bounds, &pts[0], &pts[3]);
|
||||
|
||||
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);
|
||||
gsk_bounding_box_expand (bounds, &p);
|
||||
}
|
||||
}
|
||||
|
||||
static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
|
||||
gsk_conic_curve_init,
|
||||
gsk_conic_curve_init_foreach,
|
||||
@@ -1602,6 +1835,8 @@ static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
|
||||
gsk_conic_curve_segment,
|
||||
gsk_conic_curve_decompose,
|
||||
gsk_conic_curve_decompose_curve,
|
||||
gsk_conic_curve_get_bounds,
|
||||
gsk_conic_curve_get_tight_bounds,
|
||||
};
|
||||
|
||||
/* }}} */
|
||||
@@ -1780,6 +2015,20 @@ gsk_curve_decompose_curve (const GskCurve *curve,
|
||||
return get_class (curve->op)->decompose_curve (curve, flags, tolerance, add_curve_func, user_data);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
get_class (curve->op)->get_bounds (curve, bounds);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_curve_get_tight_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds)
|
||||
{
|
||||
get_class (curve->op)->get_tight_bounds (curve, bounds);
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* vim:set foldmethod=marker expandtab: */
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "gskpathopprivate.h"
|
||||
#include "gskpath.h"
|
||||
#include "gskboundingboxprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -158,6 +159,10 @@ gboolean gsk_curve_decompose_curve (const GskCurve
|
||||
float gsk_curve_get_curvature (const GskCurve *curve,
|
||||
float t,
|
||||
graphene_point_t *center);
|
||||
void gsk_curve_get_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds);
|
||||
void gsk_curve_get_tight_bounds (const GskCurve *curve,
|
||||
GskBoundingBox *bounds);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
Reference in New Issue
Block a user