contour: Rewrite gsk_standard_contour_get_closest_point
Use gsk_curve_get_closest_point. This makes the /path/closest_point test a lot more reliable. It still fails eventually, but it takes *much* longer.
This commit is contained in:
209
gsk/gskcontour.c
209
gsk/gskcontour.c
@@ -106,39 +106,6 @@ gsk_contour_get_size_default (const GskContour *contour)
|
||||
return contour->klass->struct_size;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_find_point_on_line (const graphene_point_t *a,
|
||||
const graphene_point_t *b,
|
||||
const graphene_point_t *p,
|
||||
float *offset,
|
||||
graphene_point_t *pos)
|
||||
{
|
||||
graphene_vec2_t n;
|
||||
graphene_vec2_t ap;
|
||||
float t;
|
||||
|
||||
graphene_vec2_init (&n, b->x - a->x, b->y - a->y);
|
||||
graphene_vec2_init (&ap, p->x - a->x, p->y - a->y);
|
||||
|
||||
t = graphene_vec2_dot (&ap, &n) / graphene_vec2_dot (&n, &n);
|
||||
|
||||
if (t <= 0)
|
||||
{
|
||||
*pos = *a;
|
||||
*offset = 0;
|
||||
}
|
||||
else if (t >= 1)
|
||||
{
|
||||
*pos = *b;
|
||||
*offset = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
graphene_point_interpolate (a, b, t, pos);
|
||||
*offset = t;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_g_string_append_double (GString *string,
|
||||
double d)
|
||||
@@ -1409,6 +1376,60 @@ gsk_standard_contour_get_curvature (const GskContour *contour,
|
||||
return gsk_curve_get_curvature (&curve, progress, center);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
graphene_point_t point;
|
||||
float distance;
|
||||
int idx; /* which curve is the current best */
|
||||
graphene_point_t pos; /* the closest point */
|
||||
graphene_vec2_t tangent; /* the tangent */
|
||||
float t; /* the t value of the closest point */
|
||||
int counter;
|
||||
} DistanceData;
|
||||
|
||||
static gboolean
|
||||
check_curve_distance (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
DistanceData *dd = data;
|
||||
GskCurve curve;
|
||||
GskBoundingBox bounds;
|
||||
graphene_point_t center;
|
||||
float dx, dy, radius;
|
||||
float distance;
|
||||
graphene_point_t pos;
|
||||
float t;
|
||||
|
||||
if (op == GSK_PATH_MOVE)
|
||||
goto out;
|
||||
|
||||
gsk_curve_init_foreach (&curve, op, pts, n_pts, weight);
|
||||
|
||||
/* poor mans bounding sphere */
|
||||
gsk_curve_get_bounds (&curve, &bounds);
|
||||
radius = graphene_point_distance (&bounds.min, &bounds.max, &dx, &dy) / 2;
|
||||
graphene_point_init (¢er, bounds.min.x + dx / 2, bounds.min.y + dy / 2);
|
||||
if (graphene_point_distance (¢er, &dd->point, NULL, NULL) - radius > dd->distance)
|
||||
goto out;
|
||||
|
||||
gsk_curve_get_closest_point (&curve, &dd->point, &distance, &pos, &t);
|
||||
if (distance < dd->distance)
|
||||
{
|
||||
dd->distance = distance;
|
||||
dd->idx = dd->counter;
|
||||
dd->pos = pos;
|
||||
dd->t = t;
|
||||
gsk_curve_get_tangent (&curve, t, &dd->tangent);
|
||||
}
|
||||
|
||||
out:
|
||||
dd->counter++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
gpointer measure_data,
|
||||
@@ -1421,20 +1442,17 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
graphene_vec2_t *out_tangent)
|
||||
{
|
||||
GskStandardContour *self = (GskStandardContour *) contour;
|
||||
GskStandardContourMeasure *measure;
|
||||
float progress, dist;
|
||||
float dist;
|
||||
GArray *array = measure_data;
|
||||
graphene_point_t p, last_point;
|
||||
gsize i;
|
||||
gboolean result = FALSE;
|
||||
DistanceData dd;
|
||||
|
||||
g_assert (gsk_pathop_op (self->ops[0]) == GSK_PATH_MOVE);
|
||||
last_point = self->points[0];
|
||||
|
||||
if (array->len == 0)
|
||||
{
|
||||
/* This is the special case for point-only */
|
||||
dist = graphene_point_distance (&last_point, point, NULL, NULL);
|
||||
dist = graphene_point_distance (&self->points[0], point, NULL, NULL);
|
||||
|
||||
if (dist > threshold)
|
||||
return FALSE;
|
||||
@@ -1446,7 +1464,7 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
*out_distance = dist;
|
||||
|
||||
if (out_pos)
|
||||
*out_pos = last_point;
|
||||
*out_pos = self->points[0];
|
||||
|
||||
if (out_tangent)
|
||||
*out_tangent = *graphene_vec2_x_axis ();
|
||||
@@ -1454,79 +1472,60 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
for (i = 0; i < array->len; i++)
|
||||
memset (&dd, 0, sizeof (DistanceData));
|
||||
dd.distance = threshold;
|
||||
dd.point = *point;
|
||||
dd.idx = -1;
|
||||
dd.counter = 0;
|
||||
|
||||
gsk_standard_contour_foreach (contour,
|
||||
GSK_PATH_TOLERANCE_DEFAULT,
|
||||
check_curve_distance,
|
||||
&dd);
|
||||
if (dd.idx == -1)
|
||||
{
|
||||
measure = &g_array_index (array, GskStandardContourMeasure, i);
|
||||
/* nothing below the threshold */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gsk_find_point_on_line (&last_point,
|
||||
&measure->end_point,
|
||||
point,
|
||||
&progress,
|
||||
&p);
|
||||
last_point = measure->end_point;
|
||||
dist = graphene_point_distance (point, &p, NULL, NULL);
|
||||
/* add some wiggleroom for the accurate check below */
|
||||
//g_print ("%zu: (%g-%g) dist %g\n", i, measure->start, measure->end, dist);
|
||||
if (dist <= threshold + 1.0f)
|
||||
/* If we get here,
|
||||
* dd->pos is the closest point,
|
||||
* dd->tangent is the tangent at that point
|
||||
* dd->distance is the distance
|
||||
* dd->idx is the index of the segment it lies on
|
||||
* dd->t is the position on the curve
|
||||
*
|
||||
* Remaining task:
|
||||
* find the offset
|
||||
*/
|
||||
|
||||
if (out_distance)
|
||||
*out_distance = dd.distance;
|
||||
|
||||
if (out_pos)
|
||||
*out_pos = dd.pos;
|
||||
|
||||
if (out_tangent)
|
||||
graphene_vec2_init_from_vec2 (out_tangent, &dd.tangent);
|
||||
|
||||
if (out_offset)
|
||||
{
|
||||
*out_offset = 0;
|
||||
|
||||
for (i = 0; i < array->len; i++)
|
||||
{
|
||||
GskCurve curve;
|
||||
graphene_point_t p2;
|
||||
float found_progress, test_progress, test_dist;
|
||||
const float step = 1/1024.f;
|
||||
|
||||
gsk_curve_init (&curve, self->ops[measure->op]);
|
||||
|
||||
found_progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress;
|
||||
gsk_curve_get_point (&curve, found_progress, &p);
|
||||
dist = graphene_point_distance (point, &p, NULL, NULL);
|
||||
//g_print ("!!! %zu: (%g-%g @ %g) dist %g\n", i, measure->start_progress, measure->end_progress, progress, dist);
|
||||
|
||||
/* The progress is non-uniform, so simple translation of progress doesn't work.
|
||||
* Check if larger values inch closer towards minimal distance. */
|
||||
while (progress + step < 1.0f) {
|
||||
test_progress = measure->start_progress + (measure->end_progress - measure->start_progress) * (progress + step);
|
||||
gsk_curve_get_point (&curve, test_progress, &p2);
|
||||
test_dist = graphene_point_distance (point, &p2, NULL, NULL);
|
||||
if (test_dist > dist)
|
||||
break;
|
||||
progress += step;
|
||||
p = p2;
|
||||
found_progress = test_progress;
|
||||
dist = test_dist;
|
||||
}
|
||||
/* Also check smaller ones */
|
||||
while (progress - step > 0.0f) {
|
||||
test_progress = measure->start_progress + (measure->end_progress - measure->start_progress) * (progress - step);
|
||||
gsk_curve_get_point (&curve, test_progress, &p2);
|
||||
test_dist = graphene_point_distance (point, &p2, NULL, NULL);
|
||||
if (test_dist > dist)
|
||||
break;
|
||||
progress -= step;
|
||||
p = p2;
|
||||
found_progress = test_progress;
|
||||
dist = test_dist;
|
||||
}
|
||||
//g_print ("!!! %zu: (%g-%g @ %g) dist %g\n", i, measure->start_progress, measure->end_progress, progress, dist);
|
||||
/* double check that the point actually is closer */
|
||||
if (dist <= threshold)
|
||||
GskStandardContourMeasure *measure = &g_array_index (array, GskStandardContourMeasure, i);
|
||||
if (measure->op == dd.idx &&
|
||||
measure->start_progress <= dd.t && dd.t <= measure->end_progress)
|
||||
{
|
||||
if (out_distance)
|
||||
*out_distance = dist;
|
||||
if (out_pos)
|
||||
*out_pos = p;
|
||||
if (out_offset)
|
||||
*out_offset = measure->start + (measure->end - measure->start) * progress;
|
||||
if (out_tangent)
|
||||
gsk_curve_get_tangent (&curve, found_progress, out_tangent);
|
||||
result = TRUE;
|
||||
if (tolerance >= dist)
|
||||
return TRUE;
|
||||
threshold = dist - tolerance;
|
||||
float t = (dd.t - measure->start_progress) / (measure->end_progress - measure->start_progress);
|
||||
*out_offset = measure->start + t * (measure->end - measure->start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
Reference in New Issue
Block a user