[gtk/curve-ops: 27/109] path: Add gsk_path_measure_get_point()
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/curve-ops: 27/109] path: Add gsk_path_measure_get_point()
- Date: Sat, 26 Dec 2020 16:49:27 +0000 (UTC)
commit 32a6caeb8b4026492551cef6a905a1fc23c75adc
Author: Benjamin Otte <otte redhat com>
Date: Thu Nov 19 09:33:35 2020 +0100
path: Add gsk_path_measure_get_point()
Allows querying the coordinates and direction of any specific point on a
path.
docs/reference/gsk/gsk4-sections.txt | 1 +
gsk/gskpath.c | 192 ++++++++++++++++++++++++++++++++---
gsk/gskpathmeasure.c | 82 ++++++++++++++-
gsk/gskpathmeasure.h | 5 +
gsk/gskpathprivate.h | 6 ++
gsk/gskspline.c | 35 +++++++
gsk/gsksplineprivate.h | 4 +
7 files changed, 308 insertions(+), 17 deletions(-)
---
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 90e718bfec..a6373b92e1 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -369,6 +369,7 @@ gsk_path_measure_ref
gsk_path_measure_unref
<SUBSECTION>
gsk_path_measure_get_length
+gsk_path_measure_get_point
gsk_path_measure_add_segment
<SUBSECTION Private>
GSK_TYPE_PATH_MEASURE
diff --git a/gsk/gskpath.c b/gsk/gskpath.c
index 0ad09a6bb8..52c5a59571 100644
--- a/gsk/gskpath.c
+++ b/gsk/gskpath.c
@@ -71,6 +71,11 @@ struct _GskContourClass
float *out_length);
void (* free_measure) (const GskContour *contour,
gpointer measure_data);
+ void (* get_point) (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent);
void (* copy) (const GskContour *contour,
GskContour *dest);
void (* add_segment) (const GskContour *contour,
@@ -220,6 +225,51 @@ gsk_rect_contour_free_measure (const GskContour *contour,
{
}
+static void
+gsk_rect_contour_get_point (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ const GskRectContour *self = (const GskRectContour *) contour;
+
+ if (distance < fabsf (self->width))
+ {
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (self->x + copysignf (distance, self->width), self->y);
+ if (tangent)
+ graphene_vec2_init (tangent, copysignf (1.0f, self->width), 0.0f);
+ return;
+ }
+ distance -= fabsf (self->width);
+
+ if (distance < fabsf (self->height))
+ {
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y + copysignf (distance, self->height));
+ if (tangent)
+ graphene_vec2_init (tangent, 0.0f, copysignf (self->height, 1.0f));
+ return;
+ }
+ distance -= fabs (self->height);
+
+ if (distance < fabsf (self->width))
+ {
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (self->x + self->width - copysignf (distance, self->width), self->y +
self->height);
+ if (tangent)
+ graphene_vec2_init (tangent, - copysignf (1.0f, self->width), 0.0f);
+ return;
+ }
+ distance -= fabsf (self->width);
+
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (self->x, self->y + self->height - copysignf (distance, self->height));
+ if (tangent)
+ graphene_vec2_init (tangent, 0.0f, - copysignf (self->height, 1.0f));
+}
+
static void
gsk_rect_contour_copy (const GskContour *contour,
GskContour *dest)
@@ -306,6 +356,7 @@ static const GskContourClass GSK_RECT_CONTOUR_CLASS =
gsk_rect_contour_foreach,
gsk_rect_contour_init_measure,
gsk_rect_contour_free_measure,
+ gsk_rect_contour_get_point,
gsk_rect_contour_copy,
gsk_rect_contour_add_segment
};
@@ -455,6 +506,34 @@ gsk_circle_contour_free_measure (const GskContour *contour,
{
}
+static void
+gsk_circle_contour_get_point (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ const GskCircleContour *self = (const GskCircleContour *) contour;
+ float delta = self->end_angle - self->start_angle;
+ float length = self->radius * DEG_TO_RAD (delta);
+ float angle = self->start_angle + distance/length * delta;
+ graphene_point_t p;
+
+ p = GRAPHENE_POINT_INIT (self->center.x + cos (DEG_TO_RAD (angle)) * self->radius,
+ self->center.y + sin (DEG_TO_RAD (angle)) * self->radius);
+
+ if (pos)
+ *pos = p;
+
+ if (tangent)
+ {
+ graphene_vec2_init (tangent,
+ p.y - self->center.y,
+ - p.x + self->center.x);
+ graphene_vec2_normalize (tangent, tangent);
+ }
+}
+
static void
gsk_circle_contour_copy (const GskContour *contour,
GskContour *dest)
@@ -503,6 +582,7 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS =
gsk_circle_contour_foreach,
gsk_circle_contour_init_measure,
gsk_circle_contour_free_measure,
+ gsk_circle_contour_get_point,
gsk_circle_contour_copy,
gsk_circle_contour_add_segment
};
@@ -788,6 +868,89 @@ gsk_standard_contour_free_measure (const GskContour *contour,
g_array_free (data, TRUE);
}
+static int
+gsk_standard_contour_find_measure (gconstpointer m,
+ gconstpointer l)
+{
+ const GskStandardContourMeasure *measure = m;
+ float length = *(const float *) l;
+
+ if (measure->start > length)
+ return 1;
+ else if (measure->end <= length)
+ return -1;
+ else
+ return 0;
+}
+
+static void
+gsk_standard_contour_measure_get_point (GskStandardContour *self,
+ gsize op,
+ float progress,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ const graphene_point_t *pts;
+
+ pts = &self->points[self->ops[op].point];
+ switch (self->ops[op].op)
+ {
+ case GSK_PATH_LINE:
+ case GSK_PATH_CLOSE:
+ if (pos)
+ graphene_point_interpolate (&pts[0], &pts[1], progress, pos);
+ if (tangent)
+ {
+ graphene_vec2_init (tangent, pts[1].x - pts[0].x, pts[1].y - pts[0].y);
+ graphene_vec2_normalize (tangent, tangent);
+ }
+ break;
+
+ case GSK_PATH_CURVE:
+ gsk_spline_get_point_cubic (pts, progress, pos, tangent);
+ break;
+
+ case GSK_PATH_MOVE:
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+}
+
+static void
+gsk_standard_contour_get_point (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ GskStandardContour *self = (GskStandardContour *) contour;
+ GArray *array = measure_data;
+ guint index;
+ float progress;
+ GskStandardContourMeasure *measure;
+
+ if (array->len == 0)
+ {
+ g_assert (distance == 0);
+ g_assert (self->ops[0].op == GSK_PATH_MOVE);
+ if (pos)
+ *pos = self->points[0];
+ if (tangent)
+ graphene_vec2_init (tangent, 1.f, 0.f);
+ return;
+ }
+
+ 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_standard_contour_measure_get_point (self, measure->op, progress, pos, tangent);
+}
+
static void
gsk_standard_contour_init (GskContour *contour,
GskPathFlags flags,
@@ -805,21 +968,6 @@ gsk_standard_contour_copy (const GskContour *contour,
gsk_standard_contour_init (dest, self->flags, self->ops, self->n_ops, self->points, self->n_points);
}
-static int
-gsk_standard_contour_find_measure (gconstpointer m,
- gconstpointer l)
-{
- const GskStandardContourMeasure *measure = m;
- float length = *(const float *) l;
-
- if (measure->start > length)
- return 1;
- else if (measure->end <= length)
- return -1;
- else
- return 0;
-}
-
static void
gsk_standard_contour_add_segment (const GskContour *contour,
GskPathBuilder *builder,
@@ -985,6 +1133,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
gsk_standard_contour_foreach,
gsk_standard_contour_init_measure,
gsk_standard_contour_free_measure,
+ gsk_standard_contour_get_point,
gsk_standard_contour_copy,
gsk_standard_contour_add_segment
};
@@ -1068,6 +1217,19 @@ gsk_contour_free_measure (GskPath *path,
self->klass->free_measure (self, data);
}
+void
+gsk_contour_get_point (GskPath *path,
+ gsize i,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ GskContour *self = path->contours[i];
+
+ self->klass->get_point (self, measure_data, distance, pos, tangent);
+}
+
/* PATH */
static GskPath *
diff --git a/gsk/gskpathmeasure.c b/gsk/gskpathmeasure.c
index 61b802508b..792a390e3e 100644
--- a/gsk/gskpathmeasure.c
+++ b/gsk/gskpathmeasure.c
@@ -182,6 +182,84 @@ gsk_path_measure_get_length (GskPathMeasure *self)
return self->length;
}
+static float
+gsk_path_measure_clamp_distance (GskPathMeasure *self,
+ float distance)
+{
+ if (isnan (distance))
+ return 0;
+
+ return CLAMP (distance, 0, self->length);
+}
+
+/**
+ * gsk_path_measure_get_point:
+ * @self: a #GskPathMeasure
+ * @distance: distance into the path
+ * @pos: (out) (optional) (caller-allocates): The coordinates
+ * of the position at @distance
+ * @tangent: (out) (optional) (caller-allocates): The tangent
+ * to the position at @distance
+ *
+ * Calculates the coordinates and tangent of the point @distance
+ * units into the path. The value will be clamped to the length
+ * of the path.
+ *
+ * If the point is a discontinuous edge in the path, the returned
+ * point and tangent will describe the line starting at that point
+ * going forward.
+ *
+ * If @self describes an empty path, the returned point will be
+ * set to `(0, 0)` and the tangent will be the x axis or `(1, 0)`.
+ **/
+void
+gsk_path_measure_get_point (GskPathMeasure *self,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ gsize i;
+
+ g_return_if_fail (self != NULL);
+
+ if (pos == NULL && tangent == NULL)
+ return;
+
+ distance = gsk_path_measure_clamp_distance (self, distance);
+
+ for (i = 0; i < self->n_contours; i++)
+ {
+ if (distance < self->measures[i].length)
+ break;
+
+ distance -= self->measures[i].length;
+ }
+
+ /* weird corner cases */
+ if (i == self->n_contours)
+ {
+ /* the empty path goes here */
+ if (self->n_contours == 0)
+ {
+ if (pos)
+ graphene_point_init (pos, 0.f, 0.f);
+ if (tangent)
+ graphene_vec2_init (tangent, 1.f, 0.f);
+ return;
+ }
+ /* rounding errors can make this happen */
+ i = self->n_contours - 1;
+ distance = self->measures[i].length;
+ }
+
+ gsk_contour_get_point (self->path,
+ i,
+ self->measures[i].contour_data,
+ distance,
+ pos,
+ tangent);
+}
+
/**
* gsk_path_measure_add_segment:
* @self: a #GskPathMeasure
@@ -209,8 +287,8 @@ gsk_path_measure_add_segment (GskPathMeasure *self,
g_return_if_fail (self != NULL);
g_return_if_fail (builder != NULL);
- start = CLAMP (start, 0, self->length);
- end = CLAMP (end, 0, self->length);
+ start = gsk_path_measure_clamp_distance (self, start);
+ end = gsk_path_measure_clamp_distance (self, end);
if (start >= end)
return;
diff --git a/gsk/gskpathmeasure.h b/gsk/gskpathmeasure.h
index f38e77302b..72db10690c 100644
--- a/gsk/gskpathmeasure.h
+++ b/gsk/gskpathmeasure.h
@@ -46,6 +46,11 @@ void gsk_path_measure_unref (GskPathMeasure
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_length (GskPathMeasure *self);
+GDK_AVAILABLE_IN_ALL
+void gsk_path_measure_get_point (GskPathMeasure *self,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent);
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_add_segment (GskPathMeasure *self,
diff --git a/gsk/gskpathprivate.h b/gsk/gskpathprivate.h
index 6c51d09524..7af9dbcaff 100644
--- a/gsk/gskpathprivate.h
+++ b/gsk/gskpathprivate.h
@@ -41,6 +41,12 @@ gpointer gsk_contour_init_measure (GskPath
void gsk_contour_free_measure (GskPath *path,
gsize i,
gpointer data);
+void gsk_contour_get_point (GskPath *path,
+ gsize i,
+ gpointer measure_data,
+ float distance,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent);
void gsk_path_builder_add_contour (GskPathBuilder *builder,
GskPath *path,
diff --git a/gsk/gskspline.c b/gsk/gskspline.c
index fc91816673..e1e7f39c53 100644
--- a/gsk/gskspline.c
+++ b/gsk/gskspline.c
@@ -47,6 +47,41 @@ gsk_spline_decompose_add_point (GskCubicDecomposition *decomp,
decomp->last_progress += progress;
}
+static void
+gsk_spline_cubic_get_coefficients (graphene_point_t coeffs[4],
+ const graphene_point_t pts[4])
+{
+ coeffs[0] = GRAPHENE_POINT_INIT (pts[3].x - 3.0f * pts[2].x + 3.0f * pts[1].x - pts[0].x,
+ pts[3].y - 3.0f * pts[2].y + 3.0f * pts[1].y - pts[0].y);
+ coeffs[1] = GRAPHENE_POINT_INIT (3.0f * pts[2].x - 6.0f * pts[1].x + 3.0f * pts[0].x,
+ 3.0f * pts[2].y - 6.0f * pts[1].y + 3.0f * pts[0].y);
+ coeffs[2] = GRAPHENE_POINT_INIT (3.0f * pts[1].x - 3.0f * pts[0].x,
+ 3.0f * pts[1].y - 3.0f * pts[0].y);
+ coeffs[3] = pts[0];
+}
+
+void
+gsk_spline_get_point_cubic (const graphene_point_t pts[4],
+ float progress,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent)
+{
+ graphene_point_t c[4];
+
+ gsk_spline_cubic_get_coefficients (c, pts);
+
+ if (pos)
+ *pos = GRAPHENE_POINT_INIT (((c[0].x * progress + c[1].x) * progress +c[2].x) * progress + c[3].x,
+ ((c[0].y * progress + c[1].y) * progress +c[2].y) * progress + c[3].y);
+ if (tangent)
+ {
+ graphene_vec2_init (tangent,
+ (3.0f * c[0].x * progress + 2.0f * c[1].x) * progress + c[2].x,
+ (3.0f * c[0].y * progress + 2.0f * c[1].y) * progress + c[2].y);
+ graphene_vec2_normalize (tangent, tangent);
+ }
+}
+
void
gsk_spline_split_cubic (const graphene_point_t pts[4],
graphene_point_t result1[4],
diff --git a/gsk/gsksplineprivate.h b/gsk/gsksplineprivate.h
index a266b0d1a1..7bd6282402 100644
--- a/gsk/gsksplineprivate.h
+++ b/gsk/gsksplineprivate.h
@@ -31,6 +31,10 @@ typedef void (* GskSplineAddPointFunc) (const graphene_point_t *from,
float to_progress,
gpointer user_data);
+void gsk_spline_get_point_cubic (const graphene_point_t pts[4],
+ float progress,
+ graphene_point_t *pos,
+ graphene_vec2_t *tangent);
void gsk_spline_split_cubic (const graphene_point_t pts[4],
graphene_point_t result1[4],
graphene_point_t result2[4],
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]