2023-07-08 14:15:22 +00:00
|
|
|
/*
|
|
|
|
* 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 "gskcontourprivate.h"
|
|
|
|
|
|
|
|
#include "gskcurveprivate.h"
|
|
|
|
#include "gskpathbuilder.h"
|
|
|
|
#include "gskpathprivate.h"
|
|
|
|
#include "gskpathpointprivate.h"
|
|
|
|
#include "gsksplineprivate.h"
|
2023-07-22 03:35:14 +00:00
|
|
|
#include "gskstrokeprivate.h"
|
2023-07-08 14:15:22 +00:00
|
|
|
|
|
|
|
typedef struct _GskContourClass GskContourClass;
|
|
|
|
|
|
|
|
struct _GskContour
|
|
|
|
{
|
|
|
|
const GskContourClass *klass;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _GskContourClass
|
|
|
|
{
|
|
|
|
gsize struct_size;
|
|
|
|
const char *type_name;
|
|
|
|
|
|
|
|
void (* copy) (const GskContour *contour,
|
|
|
|
GskContour *dest);
|
|
|
|
gsize (* get_size) (const GskContour *contour);
|
|
|
|
GskPathFlags (* get_flags) (const GskContour *contour);
|
|
|
|
void (* print) (const GskContour *contour,
|
|
|
|
GString *string);
|
|
|
|
gboolean (* get_bounds) (const GskContour *contour,
|
|
|
|
GskBoundingBox *bounds);
|
2023-07-22 03:35:14 +00:00
|
|
|
gboolean (* get_stroke_bounds) (const GskContour *contour,
|
|
|
|
const GskStroke *stroke,
|
|
|
|
GskBoundingBox *bounds);
|
2023-07-08 14:15:22 +00:00
|
|
|
void (* get_start_end) (const GskContour *self,
|
|
|
|
graphene_point_t *start,
|
|
|
|
graphene_point_t *end);
|
|
|
|
gboolean (* foreach) (const GskContour *contour,
|
|
|
|
float tolerance,
|
|
|
|
GskPathForeachFunc func,
|
|
|
|
gpointer user_data);
|
|
|
|
GskContour * (* reverse) (const GskContour *contour);
|
|
|
|
int (* get_winding) (const GskContour *contour,
|
|
|
|
const graphene_point_t *point);
|
2023-08-08 12:36:27 +00:00
|
|
|
gsize (* get_n_points) (const GskContour *contour);
|
2023-07-08 14:15:22 +00:00
|
|
|
gboolean (* get_closest_point) (const GskContour *contour,
|
|
|
|
const graphene_point_t *point,
|
|
|
|
float threshold,
|
|
|
|
GskRealPathPoint *result,
|
|
|
|
float *out_dist);
|
|
|
|
void (* get_position) (const GskContour *contour,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
graphene_point_t *position);
|
|
|
|
void (* get_tangent) (const GskContour *contour,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
GskPathDirection direction,
|
|
|
|
graphene_vec2_t *tangent);
|
|
|
|
float (* get_curvature) (const GskContour *contour,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
graphene_point_t *center);
|
2023-08-08 22:45:18 +00:00
|
|
|
void (* add_segment) (const GskContour *contour,
|
|
|
|
GskPathBuilder *builder,
|
|
|
|
gboolean emit_move_to,
|
|
|
|
GskRealPathPoint *start,
|
|
|
|
GskRealPathPoint *end);
|
2023-07-08 14:15:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* {{{ Utilities */
|
|
|
|
|
|
|
|
#define DEG_TO_RAD(x) ((x) * (G_PI / 180.f))
|
|
|
|
#define RAD_TO_DEG(x) ((x) / (G_PI / 180.f))
|
|
|
|
|
|
|
|
static void
|
|
|
|
_g_string_append_double (GString *string,
|
|
|
|
double d)
|
|
|
|
{
|
|
|
|
char buf[G_ASCII_DTOSTR_BUF_SIZE];
|
|
|
|
|
|
|
|
g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, d);
|
|
|
|
g_string_append (string, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_g_string_append_point (GString *string,
|
|
|
|
const graphene_point_t *pt)
|
|
|
|
{
|
|
|
|
_g_string_append_double (string, pt->x);
|
|
|
|
g_string_append_c (string, ' ');
|
|
|
|
_g_string_append_double (string, pt->y);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ Standard */
|
|
|
|
|
|
|
|
typedef struct _GskStandardContour GskStandardContour;
|
|
|
|
struct _GskStandardContour
|
|
|
|
{
|
|
|
|
GskContour contour;
|
|
|
|
|
|
|
|
GskPathFlags flags;
|
|
|
|
|
|
|
|
gsize n_ops;
|
|
|
|
gsize n_points;
|
|
|
|
graphene_point_t *points;
|
|
|
|
gskpathop ops[];
|
|
|
|
};
|
|
|
|
|
|
|
|
static gsize
|
|
|
|
gsk_standard_contour_compute_size (gsize n_ops,
|
|
|
|
gsize n_points)
|
|
|
|
{
|
|
|
|
return sizeof (GskStandardContour)
|
|
|
|
+ sizeof (gskpathop) * n_ops
|
|
|
|
+ sizeof (graphene_point_t) * n_points;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gsk_standard_contour_init (GskContour *contour,
|
|
|
|
GskPathFlags flags,
|
|
|
|
const graphene_point_t *points,
|
|
|
|
gsize n_points,
|
|
|
|
const gskpathop *ops,
|
|
|
|
gsize n_ops,
|
|
|
|
ptrdiff_t offset);
|
|
|
|
|
|
|
|
static void
|
|
|
|
gsk_standard_contour_copy (const GskContour *contour,
|
|
|
|
GskContour *dest)
|
|
|
|
{
|
|
|
|
const GskStandardContour *self = (const GskStandardContour *) contour;
|
|
|
|
|
|
|
|
gsk_standard_contour_init (dest, self->flags, self->points, self->n_points, self->ops, self->n_ops, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gsize
|
|
|
|
gsk_standard_contour_get_size (const GskContour *contour)
|
|
|
|
{
|
|
|
|
const GskStandardContour *self = (const GskStandardContour *) contour;
|
|
|
|
|
|
|
|
return gsk_standard_contour_compute_size (self->n_ops, self->n_points);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gsk_standard_contour_foreach (const GskContour *contour,
|
|
|
|
float tolerance,
|
|
|
|
GskPathForeachFunc func,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
const GskStandardContour *self = (const GskStandardContour *) contour;
|
|
|
|
gsize i;
|
|
|
|
|
|
|
|
for (i = 0; i < self->n_ops; i ++)
|
|
|
|
{
|
|
|
|
if (!gsk_pathop_foreach (self->ops[i], func, user_data))
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
add_reverse (GskPathOperation op,
|
|
|
|
const graphene_point_t *pts,
|
|
|
|
gsize n_pts,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
GskPathBuilder *builder = user_data;
|
|
|
|
GskCurve c, r;
|
|
|
|
|
|
|
|
if (op == GSK_PATH_MOVE)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (op == GSK_PATH_CLOSE)
|
|
|
|
op = GSK_PATH_LINE;
|
|
|
|
|
|
|
|
gsk_curve_init_foreach (&c, op, pts, n_pts);
|
|
|
|
gsk_curve_reverse (&c, &r);
|
|
|
|
gsk_curve_builder_to (&r, builder);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GskContour *
|
|
|
|
gsk_standard_contour_reverse (const GskContour *contour)
|
|
|
|
{
|
|
|
|
const GskStandardContour *self = (const GskStandardContour *) contour;
|
|
|
|
GskPathBuilder *builder;
|
|
|
|
GskPath *path;
|
|
|
|
GskContour *res;
|
|
|
|
|
|
|
|
builder = gsk_path_builder_new ();
|
|
|
|
|
|
|
|
gsk_path_builder_move_to (builder, self->points[self->n_points - 1].x,
|
|
|
|
self->points[self->n_points - 1].y);
|
|
|
|
|
|
|
|
for (int i = self->n_ops - 1; i >= 0; i--)
|
|
|
|
gsk_pathop_foreach (self->ops[i], add_reverse, builder);
|
|
|
|
|
|
|
|
if (self->flags & GSK_PATH_CLOSED)
|
|
|
|
gsk_path_builder_close (builder);
|
|
|
|
|
|
|
|
path = gsk_path_builder_free_to_path (builder);
|
|
|
|
|
|
|
|
g_assert (gsk_path_get_n_contours (path) == 1);
|
|
|
|
|
|
|
|
res = gsk_contour_dup (gsk_path_get_contour (path, 0));
|
|
|
|
|
|
|
|
gsk_path_unref (path);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static GskPathFlags
|
|
|
|
gsk_standard_contour_get_flags (const GskContour *contour)
|
|
|
|
{
|
|
|
|
const GskStandardContour *self = (const GskStandardContour *) contour;
|
|
|
|
|
|
|
|
return self->flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gsk_standard_contour_print (const GskContour *contour,
|
|
|
|
GString *string)
|
|
|
|
{
|
|
|
|
const GskStandardContour *self = (const GskStandardContour *) contour;
|
|
|
|
gsize i;
|
|
|
|
|
|
|
|
for (i = 0; i < self->n_ops; i ++)
|
|
|
|
{
|
|
|
|
const graphene_point_t *pt = gsk_pathop_points (self->ops[i]);
|
|
|
|
|
|
|
|
switch (gsk_pathop_op (self->ops[i]))
|
|
|
|
{
|
|
|
|
case GSK_PATH_MOVE:
|
|
|
|
g_string_append (string, "M ");
|
|
|
|
_g_string_append_point (string, &pt[0]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GSK_PATH_CLOSE:
|
|
|
|
g_string_append (string, " Z");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GSK_PATH_LINE:
|
|
|
|
g_string_append (string, " L ");
|
|
|
|
_g_string_append_point (string, &pt[1]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GSK_PATH_QUAD:
|
|
|
|
g_string_append (string, " Q ");
|
|
|
|
_g_string_append_point (string, &pt[1]);
|
|
|
|
g_string_append (string, ", ");
|
|
|
|
_g_string_append_point (string, &pt[2]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GSK_PATH_CUBIC:
|
|
|
|
g_string_append (string, " C ");
|
|
|
|
_g_string_append_point (string, &pt[1]);
|
|
|
|
g_string_append (string, ", ");
|
|
|
|
_g_string_append_point (string, &pt[2]);
|
|
|
|
g_string_append (string, ", ");
|
|
|
|
_g_string_append_point (string, &pt[3]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gsk_standard_contour_get_bounds (const GskContour *contour,
|
|
|
|
GskBoundingBox *bounds)
|
|
|
|
{
|
|
|
|
const GskStandardContour *self = (const GskStandardContour *) contour;
|
|
|
|
gsize i;
|
|
|
|
|
|
|
|
if (self->n_points == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
gsk_bounding_box_init (bounds, &self->points[0], &self->points[0]);
|
|
|
|
for (i = 1; i < self->n_points; i ++)
|
|
|
|
gsk_bounding_box_expand (bounds, &self->points[i]);
|
|
|
|
|
|
|
|
return bounds->max.x > bounds->min.x && bounds->max.y > bounds->min.y;
|
|
|
|
}
|
|
|
|
|
2023-07-22 03:35:14 +00:00
|
|
|
static gboolean
|
|
|
|
add_stroke_bounds (GskPathOperation op,
|
|
|
|
const graphene_point_t *pts,
|
|
|
|
gsize n_pts,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
struct {
|
|
|
|
GskBoundingBox *bounds;
|
|
|
|
float lw;
|
|
|
|
float mw;
|
|
|
|
} *data = user_data;
|
|
|
|
GskBoundingBox bounds;
|
|
|
|
|
|
|
|
for (int i = 1; i < n_pts - 1; i++)
|
|
|
|
{
|
|
|
|
gsk_bounding_box_init (&bounds,
|
|
|
|
&GRAPHENE_POINT_INIT (pts[i].x - data->lw/2, pts[i].y - data->lw/2),
|
|
|
|
&GRAPHENE_POINT_INIT (pts[i].x + data->lw/2, pts[i].y + data->lw/2));
|
|
|
|
gsk_bounding_box_union (&bounds, data->bounds, data->bounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
gsk_bounding_box_init (&bounds,
|
|
|
|
&GRAPHENE_POINT_INIT (pts[n_pts - 1].x - data->mw/2, pts[n_pts - 1].y - data->mw/2),
|
|
|
|
&GRAPHENE_POINT_INIT (pts[n_pts - 1].x + data->mw/2, pts[n_pts - 1].y + data->mw/2));
|
|
|
|
gsk_bounding_box_union (&bounds, data->bounds, data->bounds);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
gsk_standard_contour_get_stroke_bounds (const GskContour *contour,
|
|
|
|
const GskStroke *stroke,
|
|
|
|
GskBoundingBox *bounds)
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
struct {
|
|
|
|
GskBoundingBox *bounds;
|
|
|
|
float lw;
|
|
|
|
float mw;
|
|
|
|
} data;
|
|
|
|
|
|
|
|
data.bounds = bounds;
|
|
|
|
data.lw = stroke->line_width;
|
|
|
|
data.mw = gsk_stroke_get_join_width (stroke);
|
|
|
|
|
|
|
|
gsk_bounding_box_init (bounds,
|
|
|
|
&GRAPHENE_POINT_INIT (self->points[0].x - data.mw/2, self->points[0].y - data.mw/2),
|
|
|
|
&GRAPHENE_POINT_INIT (self->points[0].x + data.mw/2, self->points[0].y + data.mw/2));
|
|
|
|
|
|
|
|
gsk_standard_contour_foreach (contour, GSK_PATH_TOLERANCE_DEFAULT, add_stroke_bounds, &data);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2023-07-08 14:15:22 +00:00
|
|
|
static void
|
|
|
|
gsk_standard_contour_get_start_end (const GskContour *contour,
|
|
|
|
graphene_point_t *start,
|
|
|
|
graphene_point_t *end)
|
|
|
|
{
|
|
|
|
const GskStandardContour *self = (const GskStandardContour *) contour;
|
|
|
|
|
|
|
|
if (start)
|
|
|
|
*start = self->points[0];
|
|
|
|
|
|
|
|
if (end)
|
|
|
|
*end = self->points[self->n_points - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
gsk_standard_contour_get_winding (const GskContour *contour,
|
|
|
|
const graphene_point_t *point)
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
int winding = 0;
|
|
|
|
|
|
|
|
for (gsize i = 0; i < self->n_ops; i ++)
|
|
|
|
{
|
|
|
|
GskCurve c;
|
|
|
|
|
|
|
|
if (gsk_pathop_op (self->ops[i]) == GSK_PATH_MOVE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gsk_curve_init (&c, self->ops[i]);
|
|
|
|
winding += gsk_curve_get_crossing (&c, point);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((self->flags & GSK_PATH_CLOSED) == 0)
|
|
|
|
{
|
|
|
|
GskCurve c;
|
|
|
|
|
|
|
|
gsk_curve_init (&c, gsk_pathop_encode (GSK_PATH_CLOSE,
|
|
|
|
(const graphene_point_t[]) { self->points[self->n_points - 1],
|
|
|
|
self->points[0] }));
|
|
|
|
|
|
|
|
winding += gsk_curve_get_crossing (&c, point);
|
|
|
|
}
|
|
|
|
|
|
|
|
return winding;
|
|
|
|
}
|
|
|
|
|
2023-08-08 12:36:27 +00:00
|
|
|
static gsize
|
|
|
|
gsk_standard_contour_get_n_points (const GskContour *contour)
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
|
|
|
|
return self->n_ops;
|
|
|
|
}
|
|
|
|
|
2023-07-08 14:15:22 +00:00
|
|
|
static gboolean
|
|
|
|
gsk_standard_contour_get_closest_point (const GskContour *contour,
|
|
|
|
const graphene_point_t *point,
|
|
|
|
float threshold,
|
|
|
|
GskRealPathPoint *result,
|
|
|
|
float *out_dist)
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
unsigned int best_idx = G_MAXUINT;
|
|
|
|
float best_t = 0;
|
|
|
|
|
|
|
|
g_assert (gsk_pathop_op (self->ops[0]) == GSK_PATH_MOVE);
|
|
|
|
|
|
|
|
if (self->n_ops == 1)
|
|
|
|
{
|
|
|
|
float dist;
|
|
|
|
|
|
|
|
dist = graphene_point_distance (point, &self->points[0], NULL, NULL);
|
|
|
|
if (dist <= threshold)
|
|
|
|
{
|
|
|
|
*out_dist = dist;
|
2023-08-08 22:14:27 +00:00
|
|
|
result->idx = 0;
|
|
|
|
result->t = 0;
|
2023-07-08 14:15:22 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (gsize i = 0; i < self->n_ops; i ++)
|
|
|
|
{
|
|
|
|
GskCurve c;
|
|
|
|
float distance, t;
|
|
|
|
|
|
|
|
if (gsk_pathop_op (self->ops[i]) == GSK_PATH_MOVE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gsk_curve_init (&c, self->ops[i]);
|
2023-08-09 19:06:58 +00:00
|
|
|
if (gsk_curve_get_closest_point (&c, point, threshold, &distance, &t) &&
|
|
|
|
distance < threshold)
|
2023-07-08 14:15:22 +00:00
|
|
|
{
|
|
|
|
best_idx = i;
|
|
|
|
best_t = t;
|
|
|
|
threshold = distance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (best_idx != G_MAXUINT)
|
|
|
|
{
|
|
|
|
*out_dist = threshold;
|
2023-08-08 22:14:27 +00:00
|
|
|
result->idx = best_idx;
|
|
|
|
result->t = best_t;
|
2023-07-08 14:15:22 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gsk_standard_contour_get_position (const GskContour *contour,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
graphene_point_t *position)
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
GskCurve curve;
|
|
|
|
|
2023-08-08 22:14:27 +00:00
|
|
|
if (G_UNLIKELY (point->idx == 0))
|
2023-07-08 14:15:22 +00:00
|
|
|
{
|
|
|
|
*position = self->points[0];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-08 22:14:27 +00:00
|
|
|
gsk_curve_init (&curve, self->ops[point->idx]);
|
|
|
|
gsk_curve_get_point (&curve, point->t, position);
|
2023-07-08 14:15:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gsk_standard_contour_get_tangent (const GskContour *contour,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
GskPathDirection direction,
|
|
|
|
graphene_vec2_t *tangent)
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
GskCurve curve;
|
2023-08-07 12:30:30 +00:00
|
|
|
gsize idx;
|
|
|
|
float t;
|
2023-07-08 14:15:22 +00:00
|
|
|
|
2023-08-08 22:14:27 +00:00
|
|
|
if (G_UNLIKELY (point->idx == 0))
|
2023-07-08 14:15:22 +00:00
|
|
|
{
|
|
|
|
graphene_vec2_init (tangent, 1, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-08 22:14:27 +00:00
|
|
|
idx = point->idx;
|
|
|
|
t = point->t;
|
2023-08-07 12:30:30 +00:00
|
|
|
|
|
|
|
if (t == 0 && direction == GSK_PATH_START)
|
|
|
|
{
|
|
|
|
/* Look at the previous segment */
|
2023-08-09 19:07:58 +00:00
|
|
|
if (idx > 1)
|
2023-08-07 12:30:30 +00:00
|
|
|
{
|
|
|
|
idx--;
|
|
|
|
t = 1;
|
|
|
|
}
|
|
|
|
else if (self->flags & GSK_PATH_CLOSED)
|
|
|
|
{
|
|
|
|
idx = self->n_ops - 1;
|
|
|
|
t = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (t == 1 && direction == GSK_PATH_END)
|
|
|
|
{
|
|
|
|
/* Look at the next segment */
|
|
|
|
if (idx < self->n_ops - 1)
|
|
|
|
{
|
|
|
|
idx++;
|
|
|
|
t = 0;
|
|
|
|
}
|
|
|
|
else if (self->flags & GSK_PATH_CLOSED)
|
|
|
|
{
|
2023-08-09 19:07:58 +00:00
|
|
|
idx = 1;
|
2023-08-07 12:30:30 +00:00
|
|
|
t = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gsk_curve_init (&curve, self->ops[idx]);
|
|
|
|
gsk_curve_get_tangent (&curve, t, tangent);
|
2023-07-08 14:15:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static float
|
|
|
|
gsk_standard_contour_get_curvature (const GskContour *contour,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
graphene_point_t *center)
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
GskCurve curve;
|
|
|
|
|
2023-08-08 22:14:27 +00:00
|
|
|
if (G_UNLIKELY (point->idx == 0))
|
2023-07-08 14:15:22 +00:00
|
|
|
return 0;
|
|
|
|
|
2023-08-08 22:14:27 +00:00
|
|
|
gsk_curve_init (&curve, self->ops[point->idx]);
|
|
|
|
return gsk_curve_get_curvature (&curve, point->t, center);
|
2023-07-08 14:15:22 +00:00
|
|
|
}
|
|
|
|
|
2023-08-08 22:45:18 +00:00
|
|
|
static void
|
|
|
|
add_curve (GskCurve *curve,
|
|
|
|
GskPathBuilder *builder,
|
|
|
|
gboolean *emit_move_to)
|
|
|
|
{
|
|
|
|
if (*emit_move_to)
|
|
|
|
{
|
|
|
|
const graphene_point_t *s;
|
|
|
|
|
|
|
|
s = gsk_curve_get_start_point (curve);
|
|
|
|
gsk_path_builder_move_to (builder, s->x, s->y);
|
|
|
|
*emit_move_to = FALSE;
|
|
|
|
}
|
|
|
|
gsk_curve_builder_to (curve, builder);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gsk_standard_contour_add_segment (const GskContour *contour,
|
|
|
|
GskPathBuilder *builder,
|
|
|
|
gboolean emit_move_to,
|
|
|
|
GskRealPathPoint *start,
|
|
|
|
GskRealPathPoint *end)
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
GskCurve c, c1, c2;
|
|
|
|
|
|
|
|
gsk_curve_init (&c, self->ops[start->idx]);
|
|
|
|
|
|
|
|
if (start->idx == end->idx)
|
|
|
|
{
|
|
|
|
gsk_curve_segment (&c, start->t, end->t, &c1);
|
|
|
|
add_curve (&c1, builder, &emit_move_to);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (start->t == 0)
|
|
|
|
{
|
|
|
|
add_curve (&c, builder, &emit_move_to);
|
|
|
|
}
|
|
|
|
else if (start->t < 1)
|
|
|
|
{
|
|
|
|
gsk_curve_split (&c, start->t, &c1, &c2);
|
|
|
|
add_curve (&c2, builder, &emit_move_to);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (gsize i = start->idx + 1; i < end->idx; i++)
|
|
|
|
{
|
|
|
|
gsk_curve_init (&c, self->ops[i]);
|
|
|
|
add_curve (&c, builder, &emit_move_to);
|
|
|
|
}
|
|
|
|
|
|
|
|
gsk_curve_init (&c, self->ops[end->idx]);
|
|
|
|
if (c.op == GSK_PATH_CLOSE)
|
|
|
|
c.op = GSK_PATH_LINE;
|
|
|
|
|
|
|
|
if (end->t == 1)
|
|
|
|
{
|
|
|
|
add_curve (&c, builder, &emit_move_to);
|
|
|
|
}
|
|
|
|
else if (end->t > 0)
|
|
|
|
{
|
|
|
|
gsk_curve_split (&c, end->t, &c1, &c2);
|
2023-08-13 01:30:42 +00:00
|
|
|
add_curve (&c1, builder, &emit_move_to);
|
2023-08-08 22:45:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-08 14:15:22 +00:00
|
|
|
static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
|
|
|
|
{
|
|
|
|
sizeof (GskStandardContour),
|
|
|
|
"GskStandardContour",
|
|
|
|
gsk_standard_contour_copy,
|
|
|
|
gsk_standard_contour_get_size,
|
|
|
|
gsk_standard_contour_get_flags,
|
|
|
|
gsk_standard_contour_print,
|
|
|
|
gsk_standard_contour_get_bounds,
|
2023-07-22 03:35:14 +00:00
|
|
|
gsk_standard_contour_get_stroke_bounds,
|
2023-07-08 14:15:22 +00:00
|
|
|
gsk_standard_contour_get_start_end,
|
|
|
|
gsk_standard_contour_foreach,
|
|
|
|
gsk_standard_contour_reverse,
|
|
|
|
gsk_standard_contour_get_winding,
|
2023-08-08 12:36:27 +00:00
|
|
|
gsk_standard_contour_get_n_points,
|
2023-07-08 14:15:22 +00:00
|
|
|
gsk_standard_contour_get_closest_point,
|
|
|
|
gsk_standard_contour_get_position,
|
|
|
|
gsk_standard_contour_get_tangent,
|
|
|
|
gsk_standard_contour_get_curvature,
|
2023-08-08 22:45:18 +00:00
|
|
|
gsk_standard_contour_add_segment,
|
2023-07-08 14:15:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* You must ensure the contour has enough size allocated,
|
|
|
|
* see gsk_standard_contour_compute_size()
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
gsk_standard_contour_init (GskContour *contour,
|
|
|
|
GskPathFlags flags,
|
|
|
|
const graphene_point_t *points,
|
|
|
|
gsize n_points,
|
|
|
|
const gskpathop *ops,
|
|
|
|
gsize n_ops,
|
|
|
|
gssize offset)
|
|
|
|
|
|
|
|
{
|
|
|
|
GskStandardContour *self = (GskStandardContour *) contour;
|
|
|
|
gsize i;
|
|
|
|
|
|
|
|
self->contour.klass = &GSK_STANDARD_CONTOUR_CLASS;
|
|
|
|
|
|
|
|
self->flags = flags;
|
|
|
|
self->n_ops = n_ops;
|
|
|
|
self->n_points = n_points;
|
|
|
|
self->points = (graphene_point_t *) &self->ops[n_ops];
|
|
|
|
memcpy (self->points, points, sizeof (graphene_point_t) * n_points);
|
|
|
|
|
|
|
|
offset += self->points - points;
|
|
|
|
for (i = 0; i < n_ops; i++)
|
|
|
|
{
|
|
|
|
self->ops[i] = gsk_pathop_encode (gsk_pathop_op (ops[i]),
|
|
|
|
gsk_pathop_points (ops[i]) + offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GskContour *
|
|
|
|
gsk_standard_contour_new (GskPathFlags flags,
|
|
|
|
const graphene_point_t *points,
|
|
|
|
gsize n_points,
|
|
|
|
const gskpathop *ops,
|
|
|
|
gsize n_ops,
|
|
|
|
gssize offset)
|
|
|
|
{
|
|
|
|
GskContour *contour;
|
|
|
|
|
|
|
|
contour = g_malloc0 (gsk_standard_contour_compute_size (n_ops, n_points));
|
|
|
|
|
|
|
|
gsk_standard_contour_init (contour, flags, points, n_points, ops, n_ops, offset);
|
|
|
|
|
|
|
|
return contour;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ API */
|
|
|
|
|
|
|
|
gsize
|
|
|
|
gsk_contour_get_size (const GskContour *self)
|
|
|
|
{
|
|
|
|
return self->klass->get_size (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gsk_contour_copy (GskContour *dest,
|
|
|
|
const GskContour *src)
|
|
|
|
{
|
|
|
|
src->klass->copy (src, dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
GskContour *
|
|
|
|
gsk_contour_dup (const GskContour *src)
|
|
|
|
{
|
|
|
|
GskContour *copy;
|
|
|
|
|
|
|
|
copy = g_malloc0 (gsk_contour_get_size (src));
|
|
|
|
gsk_contour_copy (copy, src);
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
GskContour *
|
|
|
|
gsk_contour_reverse (const GskContour *src)
|
|
|
|
{
|
|
|
|
return src->klass->reverse (src);
|
|
|
|
}
|
|
|
|
|
|
|
|
GskPathFlags
|
|
|
|
gsk_contour_get_flags (const GskContour *self)
|
|
|
|
{
|
|
|
|
return self->klass->get_flags (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gsk_contour_print (const GskContour *self,
|
|
|
|
GString *string)
|
|
|
|
{
|
|
|
|
self->klass->print (self, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gsk_contour_get_bounds (const GskContour *self,
|
|
|
|
GskBoundingBox *bounds)
|
|
|
|
{
|
|
|
|
return self->klass->get_bounds (self, bounds);
|
|
|
|
}
|
|
|
|
|
2023-07-22 03:35:14 +00:00
|
|
|
gboolean
|
|
|
|
gsk_contour_get_stroke_bounds (const GskContour *self,
|
|
|
|
const GskStroke *stroke,
|
|
|
|
GskBoundingBox *bounds)
|
|
|
|
{
|
|
|
|
return self->klass->get_stroke_bounds (self, stroke, bounds);
|
|
|
|
}
|
|
|
|
|
2023-07-08 14:15:22 +00:00
|
|
|
gboolean
|
|
|
|
gsk_contour_foreach (const GskContour *self,
|
|
|
|
float tolerance,
|
|
|
|
GskPathForeachFunc func,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
return self->klass->foreach (self, tolerance, func, user_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gsk_contour_get_start_end (const GskContour *self,
|
|
|
|
graphene_point_t *start,
|
|
|
|
graphene_point_t *end)
|
|
|
|
{
|
|
|
|
self->klass->get_start_end (self, start, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
gsk_contour_get_winding (const GskContour *self,
|
|
|
|
const graphene_point_t *point)
|
|
|
|
{
|
|
|
|
return self->klass->get_winding (self, point);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gsk_contour_get_closest_point (const GskContour *self,
|
|
|
|
const graphene_point_t *point,
|
|
|
|
float threshold,
|
|
|
|
GskRealPathPoint *result,
|
|
|
|
float *out_dist)
|
|
|
|
{
|
|
|
|
return self->klass->get_closest_point (self, point, threshold, result, out_dist);
|
|
|
|
}
|
|
|
|
|
2023-08-08 12:36:27 +00:00
|
|
|
gsize
|
|
|
|
gsk_contour_get_n_points (const GskContour *self)
|
|
|
|
{
|
|
|
|
return self->klass->get_n_points (self);
|
|
|
|
}
|
|
|
|
|
2023-07-08 14:15:22 +00:00
|
|
|
void
|
|
|
|
gsk_contour_get_position (const GskContour *self,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
graphene_point_t *pos)
|
|
|
|
{
|
|
|
|
self->klass->get_position (self, point, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gsk_contour_get_tangent (const GskContour *self,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
GskPathDirection direction,
|
|
|
|
graphene_vec2_t *tangent)
|
|
|
|
{
|
|
|
|
self->klass->get_tangent (self, point, direction, tangent);
|
|
|
|
}
|
|
|
|
|
|
|
|
float
|
|
|
|
gsk_contour_get_curvature (const GskContour *self,
|
|
|
|
GskRealPathPoint *point,
|
|
|
|
graphene_point_t *center)
|
|
|
|
{
|
|
|
|
return self->klass->get_curvature (self, point, center);
|
|
|
|
}
|
|
|
|
|
2023-08-08 22:45:18 +00:00
|
|
|
void
|
|
|
|
gsk_contour_add_segment (const GskContour *self,
|
|
|
|
GskPathBuilder *builder,
|
|
|
|
gboolean emit_move_to,
|
|
|
|
GskRealPathPoint *start,
|
|
|
|
GskRealPathPoint *end)
|
|
|
|
{
|
|
|
|
self->klass->add_segment (self, builder, emit_move_to, start, end);
|
|
|
|
}
|
|
|
|
|
2023-07-08 14:15:22 +00:00
|
|
|
/* }}} */
|
|
|
|
|
|
|
|
/* vim:set foldmethod=marker expandtab: */
|