diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 44206f3395..b36082a077 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -62,6 +62,10 @@ struct _GskContourClass float distance, graphene_point_t *pos, graphene_vec2_t *tangent); + float (* get_curvature) (const GskContour *contour, + gpointer measure_data, + float distance, + graphene_point_t *center); gboolean (* get_closest_point) (const GskContour *contour, gpointer measure_data, float tolerance, @@ -301,6 +305,15 @@ gsk_rect_contour_get_point (const GskContour *contour, graphene_vec2_init (tangent, 0.0f, - copysignf (self->height, 1.0f)); } +static float +gsk_rect_contour_get_curvature (const GskContour *contour, + gpointer measure_data, + float distance, + graphene_point_t *center) +{ + return 0; +} + static gboolean gsk_rect_contour_get_closest_point (const GskContour *contour, gpointer measure_data, @@ -564,6 +577,7 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS = gsk_rect_contour_init_measure, gsk_rect_contour_free_measure, gsk_rect_contour_get_point, + gsk_rect_contour_get_curvature, gsk_rect_contour_get_closest_point, gsk_rect_contour_copy, gsk_rect_contour_add_segment, @@ -766,6 +780,20 @@ gsk_circle_contour_get_point (const GskContour *contour, } } +static float +gsk_circle_contour_get_curvature (const GskContour *contour, + gpointer measure_data, + float distance, + graphene_point_t *center) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + + if (center) + *center = self->center; + + return 1 / self->radius; +} + static gboolean gsk_circle_contour_get_closest_point (const GskContour *contour, gpointer measure_data, @@ -951,6 +979,7 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS = gsk_circle_contour_init_measure, gsk_circle_contour_free_measure, gsk_circle_contour_get_point, + gsk_circle_contour_get_curvature, gsk_circle_contour_get_closest_point, gsk_circle_contour_copy, gsk_circle_contour_add_segment, @@ -1288,6 +1317,38 @@ gsk_standard_contour_get_point (const GskContour *contour, gsk_curve_get_tangent (&curve, progress, tangent); } +static float +gsk_standard_contour_get_curvature (const GskContour *contour, + gpointer measure_data, + float distance, + graphene_point_t *center) +{ + GskStandardContour *self = (GskStandardContour *) contour; + GArray *array = measure_data; + guint index; + float progress; + GskStandardContourMeasure *measure; + GskCurve curve; + + if (array->len == 0) + { + g_assert (distance == 0); + g_assert (gsk_pathop_op (self->ops[0]) == GSK_PATH_MOVE); + return 0; + } + + if (!g_array_binary_search (array, &distance, gsk_standard_contour_find_measure, &index)) + index = array->len - 1; + measure = &g_array_index (array, GskStandardContourMeasure, index); + progress = (distance - measure->start) / (measure->end - measure->start); + progress = measure->start_progress + (measure->end_progress - measure->start_progress) * progress; + g_assert (progress >= 0 && progress <= 1); + + gsk_curve_init (&curve, self->ops[measure->op]); + + return gsk_curve_get_curvature (&curve, progress, center); +} + static gboolean gsk_standard_contour_get_closest_point (const GskContour *contour, gpointer measure_data, @@ -1694,6 +1755,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = gsk_standard_contour_init_measure, gsk_standard_contour_free_measure, gsk_standard_contour_get_point, + gsk_standard_contour_get_curvature, gsk_standard_contour_get_closest_point, gsk_standard_contour_copy, gsk_standard_contour_add_segment, @@ -1823,6 +1885,15 @@ gsk_contour_get_point (const GskContour *self, self->klass->get_point (self, measure_data, distance, pos, tangent); } +float +gsk_contour_get_curvature (const GskContour *self, + gpointer measure_data, + float distance, + graphene_point_t *center) +{ + return self->klass->get_curvature (self, measure_data, distance, center); +} + gboolean gsk_contour_get_closest_point (const GskContour *self, gpointer measure_data, diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h index e5c56b999a..5219eb448a 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -78,6 +78,10 @@ void gsk_contour_get_point (const GskContou float distance, graphene_point_t *pos, graphene_vec2_t *tangent); +float gsk_contour_get_curvature (const GskContour *self, + gpointer measure_data, + float distance, + graphene_point_t *center); gboolean gsk_contour_get_closest_point (const GskContour *self, gpointer measure_data, float tolerance, diff --git a/gsk/gskpathmeasure.c b/gsk/gskpathmeasure.c index 01c3bdc6c4..a1e6bb41e9 100644 --- a/gsk/gskpathmeasure.c +++ b/gsk/gskpathmeasure.c @@ -372,6 +372,61 @@ gsk_path_measure_get_point (GskPathMeasure *self, tangent); } +/** + * gsk_path_measure_get_curvature: + * @self: a `GskPathMeasure` + * @distance: distance into the path + * @center: (optional) (out caller-allocates): The center + * of the osculating circle at the point + * + * Calculates the curvature at the point @distance units into + * the path. + * + * Optionally, returns the center of the osculating circle as well. + * + * If the curvature is infinite (at line segments), or does + * not exist (at sharp turns), zero is returned, and @center + * is not modified. + * + * Returns: The curvature of the path at the given point + */ +float +gsk_path_measure_get_curvature (GskPathMeasure *self, + float distance, + graphene_point_t *center) +{ + gsize i; + + g_return_val_if_fail (self != NULL, 0); + + distance = gsk_path_measure_clamp_distance (self, distance); + + for (i = self->first; i < self->last; i++) + { + if (distance < self->measures[i].length) + break; + + distance -= self->measures[i].length; + } + + /* weird corner cases */ + if (i == self->last) + { + /* the empty path goes here */ + if (self->first == self->last) + return 0; + + /* rounding errors can make this happen */ + i = self->last - 1; + distance = self->measures[i].length; + } + + return gsk_contour_get_curvature (gsk_path_get_contour (self->path, i), + self->measures[i].contour_data, + distance, + center); +} + /** * gsk_path_measure_get_closest_point: * @self: a `GskPathMeasure` diff --git a/gsk/gskpathmeasure.h b/gsk/gskpathmeasure.h index 4ea83af1c5..1194048934 100644 --- a/gsk/gskpathmeasure.h +++ b/gsk/gskpathmeasure.h @@ -65,6 +65,11 @@ void gsk_path_measure_get_point (GskPathMeasure graphene_point_t *pos, graphene_vec2_t *tangent); GDK_AVAILABLE_IN_ALL +float gsk_path_measure_get_curvature (GskPathMeasure *self, + float distance, + graphene_point_t *center); + +GDK_AVAILABLE_IN_ALL float gsk_path_measure_get_closest_point (GskPathMeasure *self, const graphene_point_t *point, graphene_point_t *out_pos);