Add GskPathMeasure
GskPathMeasure is an auxiliary object for measuring path lengths.
This commit is contained in:
@@ -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>
|
||||
|
||||
397
gsk/gskcontour.c
397
gsk/gskcontour.c
@@ -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: */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
426
gsk/gskpathmeasure.c
Normal 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
66
gsk/gskpathmeasure.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user