Add gsk_path_builder_add_segment

With GskPathPoint, we can subset paths without
a measure object.
This commit is contained in:
Matthias Clasen
2023-08-08 18:45:18 -04:00
parent ded30049db
commit f707185248
4 changed files with 166 additions and 1 deletions

View File

@@ -77,6 +77,11 @@ struct _GskContourClass
float (* get_curvature) (const GskContour *contour,
GskRealPathPoint *point,
graphene_point_t *center);
void (* add_segment) (const GskContour *contour,
GskPathBuilder *builder,
gboolean emit_move_to,
GskRealPathPoint *start,
GskRealPathPoint *end);
};
/* {{{ Utilities */
@@ -551,6 +556,71 @@ gsk_standard_contour_get_curvature (const GskContour *contour,
return gsk_curve_get_curvature (&curve, point->t, center);
}
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);
add_curve (&c, builder, &emit_move_to);
}
}
static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
{
sizeof (GskStandardContour),
@@ -570,6 +640,7 @@ 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_add_segment,
};
/* You must ensure the contour has enough size allocated,
@@ -747,6 +818,16 @@ gsk_contour_get_curvature (const GskContour *self,
return self->klass->get_curvature (self, point, center);
}
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);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -73,5 +73,11 @@ void gsk_contour_get_tangent (const GskContou
float gsk_contour_get_curvature (const GskContour *self,
GskRealPathPoint *point,
graphene_point_t *center);
void gsk_contour_add_segment (const GskContour *self,
GskPathBuilder *builder,
gboolean emit_move_to,
GskRealPathPoint *start,
GskRealPathPoint *end);
G_END_DECLS

View File

@@ -983,3 +983,77 @@ gsk_path_builder_add_layout (GskPathBuilder *self,
cairo_destroy (cr);
cairo_surface_destroy (surface);
}
/**
* gsk_path_builder_add_segment:
* @self: a `GskPathBuilder`
* @path: the `GskPath` to take the segment to
* @start: the point on @path to start at
* @end: the point on @path to end at
*
* Adds to @self the segment of @path from @start to @end.
*
* If @start is equal to or after @end, 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.
*
* Note that this method always adds a path with the given start point
* and end point. To add a closed path, use [method@Gsk.PathBuilder.add_path].
*
* Since: 4.14
*/
void
gsk_path_builder_add_segment (GskPathBuilder *self,
GskPath *path,
const GskPathPoint *start,
const GskPathPoint *end)
{
GskRealPathPoint *s = (GskRealPathPoint *) start;
GskRealPathPoint *e = (GskRealPathPoint *) end;
const GskContour *contour;
gsize n_contours = gsk_path_get_n_contours (path);
g_return_if_fail (self != NULL);
g_return_if_fail (path != NULL);
g_return_if_fail (start != NULL);
g_return_if_fail (end != NULL);
g_return_if_fail (s->contour < n_contours);
g_return_if_fail (e->contour < n_contours);
contour = gsk_path_get_contour (path, s->contour);
if (s->contour == e->contour)
{
if (gsk_path_point_compare (start, end) < 0)
{
gsk_contour_add_segment (contour, self, TRUE, s, e);
goto out;
}
else if (n_contours == 1)
{
gsk_contour_add_segment (contour, self, TRUE,
s,
&(GskRealPathPoint) { s->contour, gsk_contour_get_n_points (contour) - 1, 1 });
gsk_contour_add_segment (contour, self, FALSE,
&(GskRealPathPoint) { s->contour, 1, 0 },
e);
goto out;
}
}
gsk_contour_add_segment (contour, self, TRUE,
s,
&(GskRealPathPoint) { s->contour, gsk_contour_get_n_points (contour) - 1, 1 });
for (gsize i = (s->contour + 1) % n_contours; i != e->contour; i = (i + 1) % n_contours)
gsk_path_builder_add_contour (self, gsk_contour_dup (gsk_path_get_contour (path, i)));
contour = gsk_path_get_contour (path, e->contour);
gsk_contour_add_segment (contour, self, FALSE,
&(GskRealPathPoint) { e->contour, 1, 0 },
e);
out:
gsk_path_builder_end_current (self);
}

View File

@@ -68,7 +68,11 @@ GDK_AVAILABLE_IN_4_14
void gsk_path_builder_add_circle (GskPathBuilder *self,
const graphene_point_t *center,
float radius);
GDK_AVAILABLE_IN_4_14
void gsk_path_builder_add_segment (GskPathBuilder *self,
GskPath *path,
const GskPathPoint *start,
const GskPathPoint *end);
GDK_AVAILABLE_IN_4_14
void gsk_path_builder_move_to (GskPathBuilder *self,
float x,