curve: Allow specifying epsilon for length->t

Passing FLT_EPSILON gets vert precise results
at a high cost.
This commit is contained in:
Matthias Clasen
2023-08-12 07:29:39 -04:00
parent 9edc4b0854
commit 64e73668a8
4 changed files with 22 additions and 10 deletions

View File

@@ -761,7 +761,7 @@ gsk_standard_contour_get_point (const GskContour *contour,
GskCurve curve;
gsk_curve_init (&curve, self->ops[i]);
result->t = gsk_curve_at_length (&curve, distance);
result->t = gsk_curve_at_length (&curve, distance, 0.001);
#else
GskCurveLutEntry *e0, *e1;

View File

@@ -1553,6 +1553,10 @@ const double C[] = {
0.0123412297999871995468056670700372915759,
};
/* Compute arclength by using Gauss quadrature on
*
* \int_0^z \sqrt{ (dx/dt)^2 + (dy/dt)^2 } dt
*/
float
gsk_curve_get_length (const GskCurve *curve)
{
@@ -1581,33 +1585,40 @@ gsk_curve_get_length (const GskCurve *curve)
return z * sum;
}
/* Compute the inverse of the arclength using bisection,
* to a given precision
*/
float
gsk_curve_at_length (const GskCurve *curve,
float length)
float length,
float epsilon)
{
float t1, t2, t, l;
GskCurve c1;
//int loop_count = 0;
t1 = 0;
t2 = 1;
while (1)
while (t1 < t2)
{
//loop_count++;
t = (t1 + t2) / 2;
if (t1 == t2)
return t;
gsk_curve_split (curve, t, &c1, NULL);
l = gsk_curve_get_length (&c1);
if (fabs (length - l) < FLT_EPSILON)
return t;
if (fabs (length - l) < epsilon)
break;
else if (l < length)
t1 = t;
else
t2 = t;
}
//g_print ("loop count %d\n", loop_count);
return t;
}
/* }}} */

View File

@@ -155,7 +155,8 @@ gboolean gsk_curve_get_closest_point (const GskCurve
float gsk_curve_get_length (const GskCurve *curve);
float gsk_curve_at_length (const GskCurve *curve,
float distance);
float distance,
float epsilon);
G_END_DECLS

View File

@@ -156,7 +156,7 @@ test_bad_split (void)
l = gsk_curve_get_length (&c);
t[0] = 0.5;
t[1] = gsk_curve_at_length (&c, 2);
t[1] = gsk_curve_at_length (&c, 2, FLT_EPSILON);
for (unsigned int i = 0; i < G_N_ELEMENTS (t); i++)
{