From f098e590f8927e06b458edb89a8d16c088a42bc8 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 23 Dec 2020 18:52:05 -0500 Subject: [PATCH] API: Add gsk_path_measure_get_curvature We have this information available, and it can be used for some neat demos, so add an api for it. --- gsk/gskcontour.c | 71 +++++++++++++++++++++++++++++++++++++++++ gsk/gskcontourprivate.h | 4 +++ gsk/gskpathmeasure.c | 55 +++++++++++++++++++++++++++++++ gsk/gskpathmeasure.h | 5 +++ 4 files changed, 135 insertions(+) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 4d75698a83..aab3d9d9db 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, @@ -293,6 +297,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, @@ -510,6 +523,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, @@ -710,6 +724,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, @@ -891,6 +919,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, @@ -1249,6 +1278,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, @@ -1715,6 +1776,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, @@ -1842,6 +1904,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 6d2a98f8e1..1276836552 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -75,6 +75,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 17b4f5a57a..c3520c0253 100644 --- a/gsk/gskpathmeasure.c +++ b/gsk/gskpathmeasure.c @@ -322,6 +322,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 3539e61211..1920254f4e 100644 --- a/gsk/gskpathmeasure.h +++ b/gsk/gskpathmeasure.h @@ -58,6 +58,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);