Add GskPathMeasure
An object to do measuring operations on paths - determining their length, cutting off subpaths, things like that.
This commit is contained in:
@@ -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>
|
||||
|
||||
1423
gsk/gskcontour.c
1423
gsk/gskcontour.c
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
180
gsk/gskcurve.c
180
gsk/gskcurve.c
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
489
gsk/gskpathmeasure.c
Normal 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
73
gsk/gskpathmeasure.h
Normal 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
195
gsk/gskpathpoint.c
Normal 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
43
gsk/gskpathpoint.h
Normal 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
16
gsk/gskpathpointprivate.h
Normal 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user