Add GskPathMeasure

An object to do measuring operations on paths - determining
their length, cutting off subpaths, things like that.
This commit is contained in:
Matthias Clasen
2023-07-08 10:48:58 -04:00
parent 1566c023c1
commit ab7d9b0ef8
13 changed files with 2468 additions and 3 deletions

View File

@@ -22,6 +22,8 @@
#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>
#include <gsk/gskroundedrect.h>

File diff suppressed because it is too large Load Diff

View File

@@ -58,6 +58,11 @@ void gsk_contour_print (const GskContou
GString *string);
gboolean gsk_contour_get_bounds (const GskContour *self,
graphene_rect_t *bounds);
gpointer gsk_contour_init_measure (const GskContour *self,
float tolerance,
float *out_length);
void gsk_contour_free_measure (const GskContour *self,
gpointer data);
gboolean gsk_contour_foreach (const GskContour *self,
float tolerance,
GskPathForeachFunc func,
@@ -65,5 +70,34 @@ gboolean gsk_contour_foreach (const GskContou
void gsk_contour_get_start_end (const GskContour *self,
graphene_point_t *start,
graphene_point_t *end);
void gsk_contour_get_point (const GskContour *self,
gpointer measure_data,
float distance,
GskPathDirection direction,
graphene_point_t *pos,
graphene_vec2_t *tangent);
gboolean gsk_contour_get_closest_point (const GskContour *self,
gpointer measure_data,
float tolerance,
const graphene_point_t *point,
float threshold,
float *out_distance,
graphene_point_t *out_pos,
float *out_offset,
graphene_vec2_t *out_tangent);
int gsk_contour_get_winding (const GskContour *self,
gpointer measure_data,
const graphene_point_t *point);
void gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gpointer measure_data,
gboolean emit_move_to,
float start,
float end);
float gsk_contour_get_curvature (const GskContour *self,
gpointer measure_data,
float distance,
graphene_point_t *center);
G_END_DECLS

View File

@@ -51,6 +51,8 @@ struct _GskCurveClass
graphene_vec2_t *tangent);
void (* reverse) (const GskCurve *curve,
GskCurve *reverse);
float (* get_curvature) (const GskCurve *curve,
float t);
void (* split) (const GskCurve *curve,
float progress,
GskCurve *result1,
@@ -221,6 +223,13 @@ gsk_line_curve_get_tangent (const GskCurve *curve,
get_tangent (&self->points[0], &self->points[1], tangent);
}
static float
gsk_line_curve_get_curvature (const GskCurve *curve,
float t)
{
return 0;
}
static void
gsk_line_curve_reverse (const GskCurve *curve,
GskCurve *reverse)
@@ -299,6 +308,7 @@ static const GskCurveClass GSK_LINE_CURVE_CLASS = {
gsk_line_curve_get_point,
gsk_line_curve_get_tangent,
gsk_line_curve_reverse,
gsk_line_curve_get_curvature,
gsk_line_curve_split,
gsk_line_curve_segment,
gsk_line_curve_decompose,
@@ -441,6 +451,17 @@ gsk_quad_curve_get_tangent (const GskCurve *curve,
graphene_vec2_normalize (tangent, tangent);
}
static float gsk_cubic_curve_get_curvature (const GskCurve *curve,
float t);
static float
gsk_quad_curve_get_curvature (const GskCurve *curve,
float t)
{
return gsk_cubic_curve_get_curvature (curve, t);
}
static void
gsk_quad_curve_reverse (const GskCurve *curve,
GskCurve *reverse)
@@ -600,6 +621,7 @@ static const GskCurveClass GSK_QUAD_CURVE_CLASS = {
gsk_quad_curve_get_point,
gsk_quad_curve_get_tangent,
gsk_quad_curve_reverse,
gsk_quad_curve_get_curvature,
gsk_quad_curve_split,
gsk_quad_curve_segment,
gsk_quad_curve_decompose,
@@ -775,6 +797,105 @@ gsk_cubic_curve_reverse (const GskCurve *curve,
reverse->cubic.has_coefficients = FALSE;
}
static void
gsk_curve_get_derivative (const GskCurve *curve,
GskCurve *deriv)
{
switch (curve->op)
{
case GSK_PATH_LINE:
{
const GskLineCurve *self = &curve->line;
graphene_point_t p;
p.x = self->points[1].x - self->points[0].x;
p.y = self->points[1].y - self->points[0].y;
gsk_line_curve_init_from_points (&deriv->line, GSK_PATH_LINE, &p, &p);
}
break;
case GSK_PATH_QUAD:
{
const GskQuadCurve *self = &curve->quad;
graphene_point_t p[2];
p[0].x = 2.f * (self->points[1].x - self->points[0].x);
p[0].y = 2.f * (self->points[1].y - self->points[0].y);
p[1].x = 2.f * (self->points[2].x - self->points[1].x);
p[1].y = 2.f * (self->points[2].y - self->points[1].y);
gsk_line_curve_init_from_points (&deriv->line, GSK_PATH_LINE, &p[0], &p[1]);
}
break;
case GSK_PATH_CUBIC:
{
const GskCubicCurve *self = &curve->cubic;
graphene_point_t p[3];
p[0].x = 3.f * (self->points[1].x - self->points[0].x);
p[0].y = 3.f * (self->points[1].y - self->points[0].y);
p[1].x = 3.f * (self->points[2].x - self->points[1].x);
p[1].y = 3.f * (self->points[2].y - self->points[1].y);
p[2].x = 3.f * (self->points[3].x - self->points[2].x);
p[2].y = 3.f * (self->points[3].y - self->points[2].y);
gsk_quad_curve_init_from_points (&deriv->quad, p);
}
break;
case GSK_PATH_MOVE:
case GSK_PATH_CLOSE:
case GSK_PATH_CONIC:
default:
g_assert_not_reached ();
}
}
static inline float
cross (const graphene_vec2_t *v1,
const graphene_vec2_t *v2)
{
return graphene_vec2_get_x (v1) * graphene_vec2_get_y (v2)
- graphene_vec2_get_y (v1) * graphene_vec2_get_x (v2);
}
static inline float
pow3 (float w)
{
return w * w * w;
}
static float
gsk_cubic_curve_get_curvature (const GskCurve *curve,
float t)
{
GskCurve c1, c2;
graphene_point_t p, pp;
graphene_vec2_t d, dd;
float num, denom;
gsk_curve_get_derivative (curve, &c1);
gsk_curve_get_derivative (&c1, &c2);
gsk_curve_get_point (&c1, t, &p);
gsk_curve_get_point (&c2, t, &pp);
graphene_vec2_init (&d, p.x, p.y);
graphene_vec2_init (&dd, pp.x, pp.y);
num = cross (&d, &dd);
if (num == 0)
return 0;
denom = pow3 (graphene_vec2_length (&d));
if (denom == 0)
return 0;
return num / denom;
}
static void
gsk_cubic_curve_split (const GskCurve *curve,
float progress,
@@ -895,6 +1016,7 @@ static const GskCurveClass GSK_CUBIC_CURVE_CLASS = {
gsk_cubic_curve_get_point,
gsk_cubic_curve_get_tangent,
gsk_cubic_curve_reverse,
gsk_cubic_curve_get_curvature,
gsk_cubic_curve_split,
gsk_cubic_curve_segment,
gsk_cubic_curve_decompose,
@@ -1094,6 +1216,38 @@ gsk_conic_curve_get_tangent (const GskCurve *curve,
graphene_vec2_normalize (tangent, tangent);
}
/* See M. Floater, Derivatives of rational Bezier curves */
static float
gsk_conic_curve_get_curvature (const GskCurve *curve,
float t)
{
graphene_point_t p[3], p1[2];
float w, w1[2], w2;
graphene_vec2_t t1, t2, t3;
w = curve->conic.points[2].x;
p[0] = curve->conic.points[0];
p[1] = curve->conic.points[1];
p[2] = curve->conic.points[3];
w1[0] = (1 - t) + t*w;
w1[1] = (1 - t)*w + t;
w2 = (1 - t)*w1[0] + t*w1[1];
p1[0].x = ((1 - t)*p[0].x + t*w*p[1].x)/w1[0];
p1[0].y = ((1 - t)*p[0].y + t*w*p[1].y)/w1[0];
p1[1].x = ((1 - t)*w*p[1].x + t*p[2].x)/w1[1];
p1[1].y = ((1 - t)*w*p[1].y + t*p[2].y)/w1[1];
graphene_vec2_init (&t1, p[1].x - p[0].x, p[1].y - p[0].y);
graphene_vec2_init (&t2, p[2].x - p[1].x, p[2].y - p[1].y);
graphene_vec2_init (&t3, p1[1].x - p1[0].x, p1[1].y - p1[0].y);
return 0.5 * ((w*pow3 (w2))/(pow3 (w1[0])*pow3 (w1[1]))) * (cross (&t1, &t2) / pow3 (graphene_vec2_length (&t3)));
}
static void
gsk_conic_curve_reverse (const GskCurve *curve,
GskCurve *reverse)
@@ -1443,6 +1597,7 @@ static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
gsk_conic_curve_get_point,
gsk_conic_curve_get_tangent,
gsk_conic_curve_reverse,
gsk_conic_curve_get_curvature,
gsk_conic_curve_split,
gsk_conic_curve_segment,
gsk_conic_curve_decompose,
@@ -1550,6 +1705,31 @@ gsk_curve_get_tangent (const GskCurve *curve,
get_class (curve->op)->get_tangent (curve, progress, tangent);
}
float
gsk_curve_get_curvature (const GskCurve *curve,
float t,
graphene_point_t *center)
{
float k;
k = get_class (curve->op)->get_curvature (curve, t);
if (center != NULL && k != 0)
{
graphene_point_t p;
graphene_vec2_t tangent;
float r;
r = 1/k;
gsk_curve_get_point (curve, t, &p);
gsk_curve_get_tangent (curve, t, &tangent);
center->x = p.x - r * graphene_vec2_get_y (&tangent);
center->y = p.y + r * graphene_vec2_get_x (&tangent);
}
return k;
}
void
gsk_curve_reverse (const GskCurve *curve,
GskCurve *reverse)

View File

@@ -155,5 +155,9 @@ gboolean gsk_curve_decompose_curve (const GskCurve
#define gsk_curve_builder_to(curve, builder) gsk_path_builder_pathop_to ((builder), gsk_curve_pathop (curve))
float gsk_curve_get_curvature (const GskCurve *curve,
float t,
graphene_point_t *center);
G_END_DECLS

View File

@@ -75,6 +75,12 @@ void gsk_path_builder_add_ellipse (GskPathBuilder
const graphene_point_t *center,
const graphene_size_t *radius);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_add_segment (GskPathBuilder *self,
GskPathMeasure *measure,
float start,
float end);
GDK_AVAILABLE_IN_ALL
void gsk_path_builder_move_to (GskPathBuilder *self,
float x,

489
gsk/gskpathmeasure.c Normal file
View File

@@ -0,0 +1,489 @@
/*
* 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` 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 amount 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
*/
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
*/
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`.
*/
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.
*/
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
*/
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
*/
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
*/
float
gsk_path_measure_get_length (GskPathMeasure *self)
{
g_return_val_if_fail (self != NULL, 0);
return self->length;
}
/**
* gsk_path_measure_is_closed:
* @self: a `GskPathMeasure`
*
* Returns if the path being measured represents a single closed
* contour.
*
* Returns: `TRUE` if the current path is closed
*/
gboolean
gsk_path_measure_is_closed (GskPathMeasure *self)
{
const GskContour *contour;
g_return_val_if_fail (self != NULL, FALSE);
/* XXX: is the empty path closed? Currently it's not */
if (self->n_contours != 1)
return FALSE;
contour = gsk_path_get_contour (self->path, 0);
return gsk_contour_get_flags (contour) & GSK_PATH_CLOSED ? TRUE : FALSE;
}
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_in_fill:
* @self: a `GskPathMeasure`
* @point: the point to test
* @fill_rule: the fill rule to follow
*
* Returns whether the given point is inside the area
* that would be affected if the path was filled according
* to @fill_rule.
*
* Returns: `TRUE` if @point is inside
*/
gboolean
gsk_path_measure_in_fill (GskPathMeasure *self,
const graphene_point_t *point,
GskFillRule fill_rule)
{
int winding = 0;
int i;
for (i = 0; i < self->n_contours; i++)
winding += gsk_contour_get_winding (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
point);
switch (fill_rule)
{
case GSK_FILL_RULE_EVEN_ODD:
return winding & 1;
case GSK_FILL_RULE_WINDING:
return winding != 0;
default:
g_assert_not_reached ();
}
}
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.
*/
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_measure_is_closed (measure);
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
*
* Returns a `GskPathPoint` representing the point at the given
* distance into the path.
*
* An empty path has no points, so `NULL` is returned in that case.
*
* Returns: (transfer full) (nullable): a newly allocated `GskPathPoint`
*/
GskPathPoint *
gsk_path_measure_get_point (GskPathMeasure *self,
float distance)
{
gsize i;
float contour_offset;
float offset;
const GskContour *contour;
g_return_val_if_fail (self != NULL, NULL);
if (self->n_contours == 0)
return NULL;
contour_offset = 0;
offset = gsk_path_measure_clamp_distance (self, distance);
for (i = 0; i < self->n_contours - 1; i++)
{
if (offset < self->measures[i].length)
break;
contour_offset += self->measures[i].length;
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);
return gsk_path_point_new (self,
contour, self->measures[i].contour_data,
contour_offset, offset);
}
/**
* gsk_path_measure_get_closest_point:
* @self: a `GskPathMeasure`
* @point: the point to fond the closest point to
* @threshold: The maximum allowed distance between the path and @point.
* Use `INFINITY` to look for any point.
*
* Returns a `GskPathPoint` representing the point on the path
* that is closest to the given point.
*
* If no point on the path is closer than @threshold, `NULL` is returned.
*
* Returns: (transfer full) (nullable): a newly allocated `GskPathPoint`
*/
GskPathPoint *
gsk_path_measure_get_closest_point (GskPathMeasure *self,
const graphene_point_t *point,
float threshold)
{
gssize best_idx;
float best_offset;
float best_contour_offset;
float contour_offset;
contour_offset = 0;
best_idx = -1;
for (gsize i = 0; i < self->n_contours; i++)
{
float distance, offset;
if (gsk_contour_get_closest_point (gsk_path_get_contour (self->path, i),
self->measures[i].contour_data,
self->tolerance,
point,
threshold,
&distance,
NULL,
&offset,
NULL))
{
best_idx = i;
best_offset = offset;
best_contour_offset = contour_offset;
if (distance < self->tolerance)
break;
threshold = distance - self->tolerance;
}
contour_offset += self->measures[i].length;
}
if (best_idx != -1)
return gsk_path_point_new (self,
gsk_path_get_contour (self->path, best_idx),
self->measures[best_idx].contour_data,
best_contour_offset, best_offset);
return NULL;
}

73
gsk/gskpathmeasure.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* 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_ALL
GType gsk_path_measure_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_new (GskPath *path);
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
float tolerance);
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_measure_unref (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
float gsk_path_measure_get_length (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_is_closed (GskPathMeasure *self);
GDK_AVAILABLE_IN_ALL
gboolean gsk_path_measure_in_fill (GskPathMeasure *self,
const graphene_point_t *point,
GskFillRule fill_rule);
GDK_AVAILABLE_IN_ALL
GskPathPoint * gsk_path_measure_get_point (GskPathMeasure *self,
float distance);
GDK_AVAILABLE_IN_ALL
GskPathPoint * gsk_path_measure_get_closest_point (GskPathMeasure *self,
const graphene_point_t *point,
float threshold);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
G_END_DECLS

195
gsk/gskpathpoint.c Normal file
View File

@@ -0,0 +1,195 @@
#include "config.h"
#include "gskpathpointprivate.h"
#include "gskcontourprivate.h"
#include "gskpathmeasure.h"
#include "gdk/gdkprivate.h"
/**
* GskPathPoint:
*
* `GskPathPoint` is an opaque, immutable type representing a point on a path.
*
* 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.PathMeasure.get_path_point]
* or [method@Gsk.PathMeasure.get_closest_point].
*/
struct _GskPathPoint
{
guint ref_count;
GskPathMeasure *measure;
const GskContour *contour;
gpointer measure_data;
float contour_offset; /* distance from beginning of path to contour */
float offset; /* offset of point inside contour */
};
G_DEFINE_BOXED_TYPE (GskPathPoint, gsk_path_point,
gsk_path_point_ref,
gsk_path_point_unref)
GskPathPoint *
gsk_path_point_new (GskPathMeasure *measure,
const GskContour *contour,
gpointer measure_data,
float contour_offset,
float offset)
{
GskPathPoint *self;
self = g_new0 (GskPathPoint, 1);
self->ref_count = 1;
self->measure = gsk_path_measure_ref (measure);
self->contour = contour;
self->measure_data = measure_data;
self->contour_offset = contour_offset;
self->offset = offset;
return self;
}
/**
* gsk_path_point_ref:
* @self: a `GskPathPoint`
*
* Increases the reference count of a `GskPathPoint` by one.
*
* Returns: the passed in `GskPathPoint`
*/
GskPathPoint *
gsk_path_point_ref (GskPathPoint *self)
{
g_return_val_if_fail (self != NULL, NULL);
self->ref_count++;
return self;
}
/**
* gsk_path_point_unref:
* @self: a `GskPathPoint`
*
* Decreases the reference count of a `GskPathPoint` by one.
*
* If the resulting reference count is zero, frees the path_measure.
*/
void
gsk_path_point_unref (GskPathPoint *self)
{
g_return_if_fail (self != NULL);
g_return_if_fail (self->ref_count > 0);
self->ref_count--;
if (self->ref_count > 0)
return;
gsk_path_measure_unref (self->measure);
g_free (self);
}
GskPathMeasure *
gsk_path_point_get_measure (GskPathPoint *self)
{
return self->measure;
}
/**
* gsk_path_point_get_distance:
* @self: a `GskPathPoint`
*
* Returns the distance of the given point from the start of the path.
*
* This is the length of the contour from the beginning of the path
* to the point.
*
* Returns: The offset of point in path
*/
float
gsk_path_point_get_distance (GskPathPoint *self)
{
return self->contour_offset + self->offset;
}
/**
* gsk_path_point_get_position:
* @self: a `GskPathPoint`
* @position: (out caller-allocates): Return location for
* the coordinates of the point
*
* Gets the position of the point.
*/
void
gsk_path_point_get_position (GskPathPoint *self,
graphene_point_t *position)
{
gsk_contour_get_point (self->contour,
self->measure_data,
self->offset,
GSK_PATH_END,
position, NULL);
}
/**
* gsk_path_point_get_tangent:
* @self: a `GskPathPoint`
* @direction: the direction for which to return the tangent
* @tangent: (out caller-allocates): Return location for
* the tangent at the point
*
* Gets the tangent of the path at the point.
*
* Note that certain points on a path may not have a single
* tangent, such as sharp turns. At such points, there are
* two tangents -- the direction of the path going into the
* point, and the direction coming out of it.
*
* The @direction argument lets you choose which one to get.
*/
void
gsk_path_point_get_tangent (GskPathPoint *self,
GskPathDirection direction,
graphene_vec2_t *tangent)
{
gsk_contour_get_point (self->contour,
self->measure_data,
self->offset,
direction,
NULL, tangent);
}
/**
* gsk_path_point_get_curvature:
* @self: a `GskPathPoint`
* @center: (out caller-allocates): Return location for
* the center of the osculating circle
*
* 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), zero is returned,
* and @center is not modified.
*
* Returns: The curvature of the path at the given point
*/
float
gsk_path_point_get_curvature (GskPathPoint *self,
graphene_point_t *center)
{
return gsk_contour_get_curvature (self->contour,
self->measure_data,
self->offset,
center);
}

43
gsk/gskpathpoint.h Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gsk/gsk.h> can be included directly."
#endif
#include <gsk/gsktypes.h>
G_BEGIN_DECLS
#define GSK_TYPE_PATH_POINT (gsk_path_point_get_type ())
GDK_AVAILABLE_IN_ALL
GType gsk_path_point_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskPathPoint * gsk_path_point_ref (GskPathPoint *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_point_unref (GskPathPoint *self);
GDK_AVAILABLE_IN_ALL
GskPathMeasure * gsk_path_point_get_measure (GskPathPoint *self);
GDK_AVAILABLE_IN_ALL
float gsk_path_point_get_distance (GskPathPoint *self);
GDK_AVAILABLE_IN_ALL
void gsk_path_point_get_position (GskPathPoint *self,
graphene_point_t *position);
GDK_AVAILABLE_IN_ALL
void gsk_path_point_get_tangent (GskPathPoint *self,
GskPathDirection direction,
graphene_vec2_t *tangent);
GDK_AVAILABLE_IN_ALL
float gsk_path_point_get_curvature (GskPathPoint *self,
graphene_point_t *center);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathPoint, gsk_path_point_unref)
G_END_DECLS

16
gsk/gskpathpointprivate.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "gskpathpoint.h"
#include "gskcontourprivate.h"
#include "gskpathmeasure.h"
G_BEGIN_DECLS
GskPathPoint * gsk_path_point_new (GskPathMeasure *measure,
const GskContour *contour,
gpointer measure_data,
float contour_offset,
float offset);
G_END_DECLS

View File

@@ -27,6 +27,8 @@
typedef struct _GskPath GskPath;
typedef struct _GskPathBuilder GskPathBuilder;
typedef struct _GskPathMeasure GskPathMeasure;
typedef struct _GskPathPoint GskPathPoint;
typedef struct _GskRenderer GskRenderer;
typedef struct _GskTransform GskTransform;

View File

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