Add GskPathMeasure

GskPathMeasure is an auxiliary object for
measuring path lengths.
This commit is contained in:
Matthias Clasen
2023-07-21 23:24:22 -04:00
parent 6af9dc8612
commit b4473cd795
9 changed files with 923 additions and 1 deletions

View File

@@ -22,6 +22,7 @@
#include <gsk/gskenums.h>
#include <gsk/gskpath.h>
#include <gsk/gskpathbuilder.h>
#include <gsk/gskpathmeasure.h>
#include <gsk/gskpathpoint.h>
#include <gsk/gskrenderer.h>
#include <gsk/gskrendernode.h>

View File

@@ -76,6 +76,24 @@ struct _GskContourClass
float (* get_curvature) (const GskContour *contour,
GskRealPathPoint *point,
graphene_point_t *center);
gpointer (* init_measure) (const GskContour *contour,
float tolerance,
float *out_length);
void (* free_measure) (const GskContour *contour,
gpointer measure_data);
void (* add_segment) (const GskContour *contour,
GskPathBuilder *builder,
gpointer measure_data,
gboolean emit_move_to,
float start,
float end);
void (* get_point) (const GskContour *contour,
gpointer measure_data,
float offset,
GskRealPathPoint *result);
float (* get_distance) (const GskContour *contour,
GskRealPathPoint *point,
gpointer measure_data);
};
/* {{{ Utilities */
@@ -367,6 +385,274 @@ gsk_standard_contour_get_start_end (const GskContour *contour,
*end = self->points[self->n_points - 1];
}
typedef struct
{
float start;
float end;
float start_progress;
float end_progress;
GskCurveLineReason reason;
graphene_point_t start_point;
graphene_point_t end_point;
gsize op;
} GskStandardContourMeasure;
typedef struct
{
GArray *array;
GskStandardContourMeasure measure;
} LengthDecompose;
static void
gsk_standard_contour_measure_get_point_at (GskStandardContourMeasure *measure,
float progress,
graphene_point_t *out_point)
{
graphene_point_interpolate (&measure->start_point,
&measure->end_point,
(progress - measure->start) / (measure->end - measure->start),
out_point);
}
static gboolean
gsk_standard_contour_measure_add_point (const graphene_point_t *from,
const graphene_point_t *to,
float from_progress,
float to_progress,
GskCurveLineReason reason,
gpointer user_data)
{
LengthDecompose *decomp = user_data;
float seg_length;
seg_length = graphene_point_distance (from, to, NULL, NULL);
if (seg_length == 0)
return TRUE;
decomp->measure.end += seg_length;
decomp->measure.start_progress = from_progress;
decomp->measure.end_progress = to_progress;
decomp->measure.start_point = *from;
decomp->measure.end_point = *to;
decomp->measure.reason = reason;
g_array_append_val (decomp->array, decomp->measure);
decomp->measure.start += seg_length;
return TRUE;
}
static gpointer
gsk_standard_contour_init_measure (const GskContour *contour,
float tolerance,
float *out_length)
{
const GskStandardContour *self = (const GskStandardContour *) contour;
gsize i;
float length;
GArray *array;
array = g_array_new (FALSE, FALSE, sizeof (GskStandardContourMeasure));
length = 0;
for (i = 1; i < self->n_ops; i ++)
{
GskCurve curve;
LengthDecompose decomp = { array, { length, length, 0, 0, GSK_CURVE_LINE_REASON_SHORT, { 0, 0 }, { 0, 0 }, i } };
gsk_curve_init (&curve, self->ops[i]);
gsk_curve_decompose (&curve, tolerance, gsk_standard_contour_measure_add_point, &decomp);
length = decomp.measure.start;
}
*out_length = length;
return array;
}
static void
gsk_standard_contour_free_measure (const GskContour *contour,
gpointer data)
{
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_add_segment (const GskContour *contour,
GskPathBuilder *builder,
gpointer measure_data,
gboolean emit_move_to,
float start,
float end)
{
GskStandardContour *self = (GskStandardContour *) contour;
GArray *array = measure_data;
guint start_index, end_index;
float start_progress, end_progress;
GskStandardContourMeasure *start_measure, *end_measure;
gsize i;
if (start > 0)
{
if (!g_array_binary_search (array, (float[1]) { start }, gsk_standard_contour_find_measure, &start_index))
start_index = array->len - 1;
start_measure = &g_array_index (array, GskStandardContourMeasure, start_index);
start_progress = (start - start_measure->start) / (start_measure->end - start_measure->start);
start_progress = start_measure->start_progress + (start_measure->end_progress - start_measure->start_progress) * start_progress;
g_assert (start_progress >= 0 && start_progress <= 1);
}
else
{
start_measure = NULL;
start_progress = 0.0;
}
if (g_array_binary_search (array, (float[1]) { end }, gsk_standard_contour_find_measure, &end_index))
{
end_measure = &g_array_index (array, GskStandardContourMeasure, end_index);
end_progress = (end - end_measure->start) / (end_measure->end - end_measure->start);
end_progress = end_measure->start_progress + (end_measure->end_progress - end_measure->start_progress) * end_progress;
g_assert (end_progress >= 0 && end_progress <= 1);
}
else
{
end_measure = NULL;
end_progress = 1.0;
}
/* Add the first partial operation,
* taking care that first and last operation might be identical */
if (start_measure)
{
GskCurve curve, cut;
const graphene_point_t *start_point;
gsk_curve_init (&curve, self->ops[start_measure->op]);
if (start_measure->reason == GSK_CURVE_LINE_REASON_STRAIGHT)
{
graphene_point_t p;
gsk_standard_contour_measure_get_point_at (start_measure, start, &p);
if (emit_move_to)
gsk_path_builder_move_to (builder, p.x, p.y);
if (end_measure == start_measure)
{
gsk_standard_contour_measure_get_point_at (end_measure, end, &p);
gsk_path_builder_line_to (builder, p.x, p.y);
return;
}
else
{
gsk_path_builder_line_to (builder,
start_measure->end_point.x,
start_measure->end_point.y);
start_index++;
if (start_index >= array->len)
return;
start_measure++;
start_progress = start_measure->start_progress;
emit_move_to = FALSE;
gsk_curve_init (&curve, self->ops[start_measure->op]);
}
}
if (end_measure && end_measure->op == start_measure->op)
{
if (end_measure->reason == GSK_CURVE_LINE_REASON_SHORT)
{
gsk_curve_segment (&curve, start_progress, end_progress, &cut);
if (emit_move_to)
{
start_point = gsk_curve_get_start_point (&cut);
gsk_path_builder_move_to (builder, start_point->x, start_point->y);
}
gsk_curve_builder_to (&cut, builder);
}
else
{
graphene_point_t p;
gsk_curve_segment (&curve, start_progress, end_measure->start_progress, &cut);
if (emit_move_to)
{
start_point = gsk_curve_get_start_point (&cut);
gsk_path_builder_move_to (builder, start_point->x, start_point->y);
}
gsk_curve_builder_to (&cut, builder);
gsk_standard_contour_measure_get_point_at (end_measure, end, &p);
gsk_path_builder_line_to (builder, p.x, p.y);
}
return;
}
gsk_curve_split (&curve, start_progress, NULL, &cut);
start_point = gsk_curve_get_start_point (&cut);
if (emit_move_to)
gsk_path_builder_move_to (builder, start_point->x, start_point->y);
gsk_curve_builder_to (&cut, builder);
i = start_measure->op + 1;
}
else
i = emit_move_to ? 0 : 1;
for (; i < (end_measure ? end_measure->op : self->n_ops - 1); i++)
{
gsk_path_builder_pathop_to (builder, self->ops[i]);
}
/* Add the last partial operation */
if (end_measure)
{
GskCurve curve, cut;
gsk_curve_init (&curve, self->ops[end_measure->op]);
if (end_measure->reason == GSK_CURVE_LINE_REASON_SHORT)
{
gsk_curve_split (&curve, end_progress, &cut, NULL);
gsk_curve_builder_to (&cut, builder);
}
else
{
graphene_point_t p;
gsk_curve_split (&curve, end_measure->start_progress, &cut, NULL);
gsk_curve_builder_to (&cut, builder);
gsk_standard_contour_measure_get_point_at (end_measure, end, &p);
gsk_path_builder_line_to (builder, p.x, p.y);
}
}
else if (i == self->n_ops - 1)
{
gskpathop op = self->ops[i];
if (gsk_pathop_op (op) == GSK_PATH_CLOSE)
gsk_path_builder_pathop_to (builder, gsk_pathop_encode (GSK_PATH_LINE, gsk_pathop_points (op)));
else
gsk_path_builder_pathop_to (builder, op);
}
}
static int
gsk_standard_contour_get_winding (const GskContour *contour,
const graphene_point_t *point)
@@ -456,6 +742,38 @@ gsk_standard_contour_get_closest_point (const GskContour *contour,
return FALSE;
}
static void
gsk_standard_contour_get_point (const GskContour *contour,
gpointer measure_data,
float distance,
GskRealPathPoint *result)
{
GArray *array = measure_data;
unsigned int idx;
GskStandardContourMeasure *measure;
float fraction, t;
if (array->len == 0)
{
result->data.std.idx = 0;
result->data.std.t = 0;
return;
}
if (!g_array_binary_search (array, &distance, gsk_standard_contour_find_measure, &idx))
idx = array->len - 1;
measure = &g_array_index (array, GskStandardContourMeasure, idx);
fraction = (distance - measure->start) / (measure->end - measure->start);
t = measure->start_progress + fraction * (measure->end_progress - measure->start_progress);
g_assert (t >= 0 && t <= 1);
result->data.std.idx = measure->op;
result->data.std.t = t;
}
static void
gsk_standard_contour_get_position (const GskContour *contour,
GskRealPathPoint *point,
@@ -542,6 +860,37 @@ gsk_standard_contour_get_curvature (const GskContour *contour,
return gsk_curve_get_curvature (&curve, point->data.std.t, center);
}
static float
gsk_standard_contour_get_distance (const GskContour *contour,
GskRealPathPoint *point,
gpointer measure_data)
{
GArray *array = measure_data;
if (G_UNLIKELY (point->data.std.idx == 0))
return 0;
for (unsigned int i = 0; i < array->len; i++)
{
GskStandardContourMeasure *measure = &g_array_index (array, GskStandardContourMeasure, i);
float fraction;
if (measure->op != point->data.std.idx)
continue;
if (measure->end_progress < point->data.std.t)
continue;
g_assert (measure->op == point->data.std.idx);
g_assert (measure->start_progress <= point->data.std.t && point->data.std.t <= measure->end_progress);
fraction = (point->data.std.t - measure->start_progress) / (measure->end_progress - measure->start_progress);
return measure->start + fraction * (measure->end - measure->start);
}
return 0;
}
static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
{
sizeof (GskStandardContour),
@@ -560,6 +909,11 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
gsk_standard_contour_get_position,
gsk_standard_contour_get_tangent,
gsk_standard_contour_get_curvature,
gsk_standard_contour_init_measure,
gsk_standard_contour_free_measure,
gsk_standard_contour_add_segment,
gsk_standard_contour_get_point,
gsk_standard_contour_get_distance,
};
/* You must ensure the contour has enough size allocated,
@@ -681,6 +1035,21 @@ gsk_contour_foreach (const GskContour *self,
return self->klass->foreach (self, tolerance, func, user_data);
}
gpointer
gsk_contour_init_measure (const GskContour *self,
float tolerance,
float *out_length)
{
return self->klass->init_measure (self, tolerance, out_length);
}
void
gsk_contour_free_measure (const GskContour *self,
gpointer data)
{
self->klass->free_measure (self, data);
}
void
gsk_contour_get_start_end (const GskContour *self,
graphene_point_t *start,
@@ -689,6 +1058,17 @@ gsk_contour_get_start_end (const GskContour *self,
self->klass->get_start_end (self, start, end);
}
void
gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gpointer measure_data,
gboolean emit_move_to,
float start,
float end)
{
self->klass->add_segment (self, builder, measure_data, emit_move_to, start, end);
}
int
gsk_contour_get_winding (const GskContour *self,
const graphene_point_t *point)
@@ -706,6 +1086,15 @@ gsk_contour_get_closest_point (const GskContour *self,
return self->klass->get_closest_point (self, point, threshold, result, out_dist);
}
void
gsk_contour_get_point (const GskContour *self,
gpointer measure_data,
float offset,
GskRealPathPoint *result)
{
self->klass->get_point (self, measure_data, offset, result);
}
void
gsk_contour_get_position (const GskContour *self,
GskRealPathPoint *point,
@@ -731,6 +1120,14 @@ gsk_contour_get_curvature (const GskContour *self,
return self->klass->get_curvature (self, point, center);
}
float
gsk_contour_get_distance (const GskContour *self,
GskRealPathPoint *point,
gpointer measure_data)
{
return self->klass->get_distance (self, point, measure_data);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -57,14 +57,17 @@ void gsk_contour_get_start_end (const GskContou
graphene_point_t *end);
int gsk_contour_get_winding (const GskContour *self,
const graphene_point_t *point);
gboolean gsk_contour_get_closest_point (const GskContour *self,
const graphene_point_t *point,
float threshold,
GskRealPathPoint *result,
float *out_dist);
void gsk_contour_get_position (const GskContour *self,
GskRealPathPoint *point,
graphene_point_t *pos);
void gsk_contour_get_tangent (const GskContour *self,
GskRealPathPoint *point,
GskPathDirection direction,
@@ -72,5 +75,23 @@ void gsk_contour_get_tangent (const GskContou
float gsk_contour_get_curvature (const GskContour *self,
GskRealPathPoint *point,
graphene_point_t *center);
gpointer gsk_contour_init_measure (const GskContour *self,
float tolerance,
float *out_length);
void gsk_contour_free_measure (const GskContour *self,
gpointer data);
void gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gpointer measure_data,
gboolean emit_move_to,
float start,
float end);
void gsk_contour_get_point (const GskContour *self,
gpointer measure_data,
float offset,
GskRealPathPoint *result);
float gsk_contour_get_distance (const GskContour *self,
GskRealPathPoint *point,
gpointer measure_data);
G_END_DECLS

View File

@@ -69,6 +69,12 @@ void gsk_path_builder_add_circle (GskPathBuilder
const graphene_point_t *center,
float radius);
GDK_AVAILABLE_IN_4_14
void gsk_path_builder_add_segment (GskPathBuilder *self,
GskPathMeasure *measure,
float start,
float end);
GDK_AVAILABLE_IN_4_14
void gsk_path_builder_move_to (GskPathBuilder *self,
float x,

426
gsk/gskpathmeasure.c Normal file
View File

@@ -0,0 +1,426 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gskpathmeasure.h"
#include "gskpathbuilder.h"
#include "gskpathpointprivate.h"
#include "gskpathprivate.h"
/**
* GskPathMeasure:
*
* `GskPathMeasure` is an object that allows measurements
* on `GskPath`s such as determining the length of the path.
*
* Many measuring operations require approximating the path
* with simpler shapes. Therefore, a `GskPathMeasure` has
* a tolerance that determines what precision is required
* for such approximations.
*
* A `GskPathMeasure` struct is a reference counted struct
* and should be treated as opaque.
*/
typedef struct _GskContourMeasure GskContourMeasure;
struct _GskContourMeasure
{
float length;
gpointer contour_data;
};
struct _GskPathMeasure
{
/*< private >*/
guint ref_count;
GskPath *path;
float tolerance;
float length;
gsize n_contours;
GskContourMeasure measures[];
};
G_DEFINE_BOXED_TYPE (GskPathMeasure, gsk_path_measure,
gsk_path_measure_ref,
gsk_path_measure_unref)
/**
* gsk_path_measure_new:
* @path: the path to measure
*
* Creates a measure object for the given @path.
*
* Returns: a new `GskPathMeasure` representing @path
*
* Since: 4.14
*/
GskPathMeasure *
gsk_path_measure_new (GskPath *path)
{
return gsk_path_measure_new_with_tolerance (path, GSK_PATH_TOLERANCE_DEFAULT);
}
/**
* gsk_path_measure_new_with_tolerance:
* @path: the path to measure
* @tolerance: the tolerance for measuring operations
*
* Creates a measure object for the given @path and @tolerance.
*
* Returns: a new `GskPathMeasure` representing @path
*
* Since: 4.14
*/
GskPathMeasure *
gsk_path_measure_new_with_tolerance (GskPath *path,
float tolerance)
{
GskPathMeasure *self;
gsize i, n_contours;
g_return_val_if_fail (path != NULL, NULL);
g_return_val_if_fail (tolerance > 0, NULL);
n_contours = gsk_path_get_n_contours (path);
self = g_malloc0 (sizeof (GskPathMeasure) + n_contours * sizeof (GskContourMeasure));
self->ref_count = 1;
self->path = gsk_path_ref (path);
self->tolerance = tolerance;
self->n_contours = n_contours;
for (i = 0; i < n_contours; i++)
{
self->measures[i].contour_data = gsk_contour_init_measure (gsk_path_get_contour (path, i),
self->tolerance,
&self->measures[i].length);
self->length += self->measures[i].length;
}
return self;
}
/**
* gsk_path_measure_ref:
* @self: a `GskPathMeasure`
*
* Increases the reference count of a `GskPathMeasure` by one.
*
* Returns: the passed in `GskPathMeasure`.
*
* Since: 4.14
*/
GskPathMeasure *
gsk_path_measure_ref (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, NULL);
self->ref_count++;
return self;
}
/**
* gsk_path_measure_unref:
* @self: a `GskPathMeasure`
*
* Decreases the reference count of a `GskPathMeasure` by one.
*
* If the resulting reference count is zero, frees the object.
*
* Since: 4.14
*/
void
gsk_path_measure_unref (GskPathMeasure *self)
{
gsize i;
g_return_if_fail (self != NULL);
g_return_if_fail (self->ref_count > 0);
self->ref_count--;
if (self->ref_count > 0)
return;
for (i = 0; i < self->n_contours; i++)
{
gsk_contour_free_measure (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data);
}
gsk_path_unref (self->path);
g_free (self);
}
/**
* gsk_path_measure_get_path:
* @self: a `GskPathMeasure`
*
* Returns the path that the measure was created for.
*
* Returns: (transfer none): the path of @self
*
* Since: 4.14
*/
GskPath *
gsk_path_measure_get_path (GskPathMeasure *self)
{
return self->path;
}
/**
* gsk_path_measure_get_tolerance:
* @self: a `GskPathMeasure`
*
* Returns the tolerance that the measure was created with.
*
* Returns: the tolerance of @self
*
* Since: 4.14
*/
float
gsk_path_measure_get_tolerance (GskPathMeasure *self)
{
return self->tolerance;
}
/**
* gsk_path_measure_get_length:
* @self: a `GskPathMeasure`
*
* Gets the length of the path being measured.
*
* The length is cached, so this function does not do any work.
*
* Returns: The length of the path measured by @self
*
* Since: 4.14
*/
float
gsk_path_measure_get_length (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0);
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);
}
static void
gsk_path_builder_add_segment_chunk (GskPathBuilder *self,
GskPathMeasure *measure,
gboolean emit_move_to,
float start,
float end)
{
g_assert (start < end);
for (gsize i = 0; i < measure->n_contours; i++)
{
if (measure->measures[i].length < start)
{
start -= measure->measures[i].length;
end -= measure->measures[i].length;
}
else if (start > 0 || end < measure->measures[i].length)
{
float len = MIN (end, measure->measures[i].length);
gsk_contour_add_segment (gsk_path_get_contour (measure->path, i),
self,
measure->measures[i].contour_data,
emit_move_to,
start,
len);
end -= len;
start = 0;
if (end <= 0)
break;
}
else
{
end -= measure->measures[i].length;
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (measure->path, i)));
}
emit_move_to = TRUE;
}
}
/**
* gsk_path_builder_add_segment:
* @self: a `GskPathBuilder`
* @measure: the `GskPathMeasure` to take the segment to
* @start: start distance into the path
* @end: end distance into the path
*
* Adds to @self the segment of @measure from @start to @end.
*
* The distances are given relative to the length of @measure's path,
* from 0 for the beginning of the path to its length for the end
* of the path. The values will be clamped to that range. The length
* can be obtained with [method@Gsk.PathMeasure.get_length].
*
* If @start >= @end after clamping, the path will first add the segment
* from @start to the end of the path, and then add the segment from
* the beginning to @end. If the path is closed, these segments will
* be connected.
*
* Since: 4.14
*/
void
gsk_path_builder_add_segment (GskPathBuilder *self,
GskPathMeasure *measure,
float start,
float end)
{
g_return_if_fail (self != NULL);
g_return_if_fail (measure != NULL);
start = gsk_path_measure_clamp_distance (measure, start);
end = gsk_path_measure_clamp_distance (measure, end);
if (start < end)
{
gsk_path_builder_add_segment_chunk (self, measure, TRUE, start, end);
}
else
{
/* If the path is closed, we can connect the 2 subpaths. */
gboolean closed = gsk_path_is_closed (measure->path);
gboolean need_move_to = !closed;
if (start < measure->length)
gsk_path_builder_add_segment_chunk (self, measure,
TRUE,
start, measure->length);
else
need_move_to = TRUE;
if (end > 0)
gsk_path_builder_add_segment_chunk (self, measure,
need_move_to,
0, end);
if (start == end && closed)
gsk_path_builder_close (self);
}
}
/**
* gsk_path_measure_get_point:
* @self: a `GskPathMeasure`
* @distance: the distance
* @result: (out caller-allocates): return location for the result
*
* Sets @result to the point at the given distance into the path.
*
* An empty path has no points, so `FALSE` is returned in that case.
*
* Returns: `TRUE` if @result was set
*
* Since: 4.14
*/
gboolean
gsk_path_measure_get_point (GskPathMeasure *self,
float distance,
GskPathPoint *result)
{
GskRealPathPoint *res = (GskRealPathPoint *) result;
gsize i;
float offset;
const GskContour *contour;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (result != NULL, FALSE);
if (self->n_contours == 0)
return FALSE;
offset = gsk_path_measure_clamp_distance (self, distance);
for (i = 0; i < self->n_contours - 1; i++)
{
if (offset < self->measures[i].length)
break;
offset -= self->measures[i].length;
}
g_assert (0 <= i && i < self->n_contours);
offset = CLAMP (offset, 0, self->measures[i].length);
contour = gsk_path_get_contour (self->path, i);
gsk_contour_get_point (contour, self->measures[i].contour_data, offset, res);
res->path = self->path;
res->contour = i;
return TRUE;
}
/**
* gsk_path_measure_get_distance:
* @self: a `GskPathMeasure`
* @point: a `GskPathPoint on the path of @self
*
* Returns the distance from the beginning of the path
* to @point.
*
* Returns: the distance of @point
*
* Since: 4.14
*/
float
gsk_path_measure_get_distance (GskPathMeasure *self,
const GskPathPoint *point)
{
GskRealPathPoint *p = (GskRealPathPoint *)point;
const GskContour *contour = gsk_path_get_contour (self->path, p->contour);
float contour_offset = 0;
g_return_val_if_fail (self != NULL, 0);
g_return_val_if_fail (self->path == p->path, 0);
g_return_val_if_fail (contour != NULL, 0);
for (gsize i = 0; i < self->n_contours; i++)
{
if (contour == gsk_path_get_contour (self->path, i))
return contour_offset + gsk_contour_get_distance (contour,
p,
self->measures[i].contour_data);
contour_offset += self->measures[i].length;
}
g_return_val_if_reached (0);
}

66
gsk/gskpathmeasure.h Normal file
View File

@@ -0,0 +1,66 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#pragma once
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gskpath.h>
#include <gsk/gskpathpoint.h>
G_BEGIN_DECLS
#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ())
GDK_AVAILABLE_IN_4_14
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_14
GskPathMeasure * gsk_path_measure_new (GskPath *path);
GDK_AVAILABLE_IN_4_14
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
float tolerance);
GDK_AVAILABLE_IN_4_14
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
GDK_AVAILABLE_IN_4_14
void gsk_path_measure_unref (GskPathMeasure *self);
GDK_AVAILABLE_IN_4_14
GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_14
float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_14
float gsk_path_measure_get_length (GskPathMeasure *self);
GDK_AVAILABLE_IN_4_14
gboolean gsk_path_measure_get_point (GskPathMeasure *self,
float distance,
GskPathPoint *result);
GDK_AVAILABLE_IN_4_14
float gsk_path_measure_get_distance (GskPathMeasure *self,
const GskPathPoint *point);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
G_END_DECLS

View File

@@ -22,6 +22,7 @@
#include "gskpathpointprivate.h"
#include "gskcontourprivate.h"
#include "gskpathmeasure.h"
#include "gdk/gdkprivate.h"
@@ -33,7 +34,8 @@
* It can be queried for properties of the path at that point, such as its
* tangent or its curvature.
*
* To obtain a `GskPathPoint`, use [method@Gsk.Path.get_closest_point].
* To obtain a `GskPathPoint`, use [method@Gsk.Path.get_closest_point]
* or [method@Gsk.PathMeasure.get_point].
*
* Note that `GskPathPoint` structs are meant to be stack-allocated, and
* don't a reference to the path object they are obtained from. It is the

View File

@@ -27,6 +27,7 @@
typedef struct _GskPath GskPath;
typedef struct _GskPathBuilder GskPathBuilder;
typedef struct _GskPathMeasure GskPathMeasure;
typedef struct _GskPathPoint GskPathPoint;
typedef struct _GskRenderer GskRenderer;
typedef struct _GskStroke GskStroke;

View File

@@ -28,6 +28,7 @@ gsk_public_sources = files([
'gskglshader.c',
'gskpath.c',
'gskpathbuilder.c',
'gskpathmeasure.c',
'gskpathpoint.c',
'gskrenderer.c',
'gskrendernode.c',
@@ -75,6 +76,7 @@ gsk_public_headers = files([
'gskglshader.h',
'gskpath.h',
'gskpathbuilder.h',
'gskpathmeasure.h',
'gskpathpoint.h',
'gskrenderer.h',
'gskrendernode.h',