diff --git a/docs/reference/gsk/gsk4.toml.in b/docs/reference/gsk/gsk4.toml.in
index e451183ba8..813f974c4c 100644
--- a/docs/reference/gsk/gsk4.toml.in
+++ b/docs/reference/gsk/gsk4.toml.in
@@ -36,6 +36,8 @@ content_images = [
"gtk-logo.svg",
"images/caps-dark.png",
"images/caps-light.png",
+ "images/directions-dark.png",
+ "images/directions-light.png",
"images/join-dark.png",
"images/join-light.png",
"images/path-dark.png",
diff --git a/docs/reference/gsk/images/directions-dark.png b/docs/reference/gsk/images/directions-dark.png
new file mode 100644
index 0000000000..77505ee462
Binary files /dev/null and b/docs/reference/gsk/images/directions-dark.png differ
diff --git a/docs/reference/gsk/images/directions-light.png b/docs/reference/gsk/images/directions-light.png
new file mode 100644
index 0000000000..c61840a84f
Binary files /dev/null and b/docs/reference/gsk/images/directions-light.png differ
diff --git a/docs/reference/gsk/images/directions.svg b/docs/reference/gsk/images/directions.svg
new file mode 100644
index 0000000000..bb432a059e
--- /dev/null
+++ b/docs/reference/gsk/images/directions.svg
@@ -0,0 +1,327 @@
+
+
+
+
diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c
index f708e242f6..d76327847a 100644
--- a/gsk/gskcontour.c
+++ b/gsk/gskcontour.c
@@ -509,7 +509,8 @@ gsk_standard_contour_get_tangent (const GskContour *contour,
idx = point->idx;
t = point->t;
- if (t == 0 && direction == GSK_PATH_START)
+ if (t == 0 && (direction == GSK_PATH_FROM_START ||
+ direction == GSK_PATH_TO_START))
{
/* Look at the previous segment */
if (idx > 1)
@@ -523,7 +524,8 @@ gsk_standard_contour_get_tangent (const GskContour *contour,
t = 1;
}
}
- else if (t == 1 && direction == GSK_PATH_END)
+ else if (t == 1 && (direction == GSK_PATH_TO_END ||
+ direction == GSK_PATH_FROM_END))
{
/* Look at the next segment */
if (idx < self->n_ops - 1)
@@ -540,6 +542,9 @@ gsk_standard_contour_get_tangent (const GskContour *contour,
gsk_curve_init (&curve, self->ops[idx]);
gsk_curve_get_tangent (&curve, t, tangent);
+
+ if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_END)
+ graphene_vec2_negate (tangent, tangent);
}
static float
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 9ed0ee077e..fe157ab80b 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -292,17 +292,30 @@ typedef enum {
/**
* GskPathDirection:
- * @GSK_PATH_START: The side that leads to the start of the path
- * @GSK_PATH_END: The side that leads to the end of the path
+ * @GSK_PATH_FROM_START: The tangent in path direction of the incoming side
+ * of the path
+ * @GSK_PATH_TO_START: The tangent against path direction of the incoming side
+ * of the path
+ * @GSK_PATH_TO_END: The tangent in path direction of the outgoing side
+ * of the path
+ * @GSK_PATH_FROM_END: The tangent against path direction of the outgoing
+ * side of the path
*
* The values of the `GskPathDirection` enum are used to pick one
- * of the two sides of the path that at a given point on the path.
+ * of the four tangents at a given point on the path.
+ *
+ *
+ *
+ *
+ *
*
* Since: 4.14
*/
typedef enum {
- GSK_PATH_START,
- GSK_PATH_END
+ GSK_PATH_FROM_START,
+ GSK_PATH_TO_START,
+ GSK_PATH_TO_END,
+ GSK_PATH_FROM_END,
} GskPathDirection;
/**
diff --git a/gsk/gskpathpoint.c b/gsk/gskpathpoint.c
index a55e879b18..d1835743a1 100644
--- a/gsk/gskpathpoint.c
+++ b/gsk/gskpathpoint.c
@@ -19,12 +19,16 @@
#include "config.h"
+#include
+
#include "gskpathpointprivate.h"
#include "gskcontourprivate.h"
#include "gdk/gdkprivate.h"
+#define RAD_TO_DEG(x) ((x) / (G_PI / 180.f))
+
/**
* GskPathPoint:
*
@@ -181,6 +185,10 @@ gsk_path_point_get_position (const GskPathPoint *point,
* point, and the direction coming out of it. The @direction
* argument lets you choose which one to get.
*
+ * If you want to orient something in the direction of the
+ * path, [method@Gsk.PathPoint.get_rotation] may be more
+ * convenient to use.
+ *
* Since: 4.14
*/
void
@@ -201,6 +209,40 @@ gsk_path_point_get_tangent (const GskPathPoint *point,
gsk_contour_get_tangent (contour, self, direction, tangent);
}
+/**
+ * gsk_path_point_get_rotation:
+ * @point: a `GskPathPoint`
+ * @path: the path that @point is on
+ * @direction: the direction for which to return the rotation
+ *
+ * Gets the direction of the tangent at a given point.
+ *
+ * This is a convenience variant of [method@Gsk.PathPoint.get_tangent]
+ * that returns the angle between the tangent and the X axis. The angle
+ * can e.g. be used in [method@Gtk.Snapshot.rotate].
+ *
+ * Returns: the angle between the tangent and the X axis, in degrees
+ *
+ * Since: 4.14
+ */
+float
+gsk_path_point_get_rotation (const GskPathPoint *point,
+ GskPath *path,
+ GskPathDirection direction)
+{
+ GskRealPathPoint *self = (GskRealPathPoint *) point;
+ graphene_vec2_t tangent;
+
+ g_return_val_if_fail (self != NULL, 0);
+ g_return_val_if_fail (path != NULL, 0);
+ g_return_val_if_fail (self->contour < gsk_path_get_n_contours (path), 0);
+
+ gsk_path_point_get_tangent (point, path, direction, &tangent);
+
+ return RAD_TO_DEG (atan2f (graphene_vec2_get_y (&tangent),
+ graphene_vec2_get_x (&tangent)));
+}
+
/**
* gsk_path_point_get_curvature:
* @point: a `GskPathPoint`
diff --git a/gsk/gskpathpoint.h b/gsk/gskpathpoint.h
index 4fdacc797e..56d65cb641 100644
--- a/gsk/gskpathpoint.h
+++ b/gsk/gskpathpoint.h
@@ -67,6 +67,11 @@ void gsk_path_point_get_tangent (const GskPathPoint *poin
GskPathDirection direction,
graphene_vec2_t *tangent);
+GDK_AVAILABLE_IN_4_14
+float gsk_path_point_get_rotation (const GskPathPoint *point,
+ GskPath *path,
+ GskPathDirection direction);
+
GDK_AVAILABLE_IN_4_14
float gsk_path_point_get_curvature (const GskPathPoint *point,
GskPath *path,
diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c
index f55a1b62d6..d838568836 100644
--- a/gtk/gtksnapshot.c
+++ b/gtk/gtksnapshot.c
@@ -1151,6 +1151,9 @@ gtk_snapshot_clear_fill (GtkSnapshotState *state)
*
* The image is recorded until the next call to [method@Gtk.Snapshot.pop].
*
+ * If you want to fill the path with a color, [method@Gtk.Snapshot.append_fill]
+ * may be more convenient.
+ *
* Since: 4.14
*/
void
@@ -1171,6 +1174,35 @@ gtk_snapshot_push_fill (GtkSnapshot *snapshot,
state->data.fill.fill_rule = fill_rule;
}
+/**
+ * gtk_snapshot_append_fill:
+ * @snapshot: a `GtkSnapshot`
+ * @path: The path describing the area to fill
+ * @fill_rule: The fill rule to use
+ * @color: the color to fill the path with
+ *
+ * A convenience method to fill a path with a color.
+ *
+ * See [method@Gtk.Snapshot.push_fill] if you need
+ * to fill a path with more complex content than
+ * a color.
+ *
+ * Since: 4.14
+ */
+void
+gtk_snapshot_append_fill (GtkSnapshot *snapshot,
+ GskPath *path,
+ GskFillRule fill_rule,
+ const GdkRGBA *color)
+{
+ graphene_rect_t bounds;
+
+ gsk_path_get_bounds (path, &bounds);
+ gtk_snapshot_push_fill (snapshot, path, fill_rule);
+ gtk_snapshot_append_color (snapshot, color, &bounds);
+ gtk_snapshot_pop (snapshot);
+}
+
static GskRenderNode *
gtk_snapshot_collect_stroke (GtkSnapshot *snapshot,
GtkSnapshotState *state,
@@ -1222,6 +1254,9 @@ gtk_snapshot_clear_stroke (GtkSnapshotState *state)
* everything else, so uneven scaling will cause horizontal and vertical
* strokes to have different widths.
*
+ * If you want to stroke the path with a color, [method@Gtk.Snapshot.append_stroke]
+ * may be more convenient.
+ *
* Since: 4.14
*/
void
@@ -1265,6 +1300,35 @@ gtk_snapshot_collect_shadow (GtkSnapshot *snapshot,
return shadow_node;
}
+/**
+ * gtk_snapshot_append_stroke:
+ * @snapshot: a `GtkSnapshot`
+ * @path: The path describing the area to fill
+ * @stroke: The stroke attributes
+ * @color: the color to fill the path with
+ *
+ * A convenience method to stroke a path with a color.
+ *
+ * See [method@Gtk.Snapshot.push_stroke] if you need
+ * to stroke a path with more complex content than
+ * a color.
+ *
+ * Since: 4.14
+ */
+void
+gtk_snapshot_append_stroke (GtkSnapshot *snapshot,
+ GskPath *path,
+ const GskStroke *stroke,
+ const GdkRGBA *color)
+{
+ graphene_rect_t bounds;
+
+ gsk_path_get_stroke_bounds (path, stroke, &bounds);
+ gtk_snapshot_push_stroke (snapshot, path, stroke);
+ gtk_snapshot_append_color (snapshot, color, &bounds);
+ gtk_snapshot_pop (snapshot);
+}
+
static void
gtk_snapshot_clear_shadow (GtkSnapshotState *state)
{
diff --git a/gtk/gtksnapshot.h b/gtk/gtksnapshot.h
index faa40b732d..d3d25e8c67 100644
--- a/gtk/gtksnapshot.h
+++ b/gtk/gtksnapshot.h
@@ -240,6 +240,16 @@ void gtk_snapshot_append_layout (GtkSnapshot
PangoLayout *layout,
const GdkRGBA *color);
+GDK_AVAILABLE_IN_4_14
+void gtk_snapshot_append_fill (GtkSnapshot *snapshot,
+ GskPath *path,
+ GskFillRule fill_rule,
+ const GdkRGBA *color);
+GDK_AVAILABLE_IN_4_14
+void gtk_snapshot_append_stroke (GtkSnapshot *snapshot,
+ GskPath *path,
+ const GskStroke *stroke,
+ const GdkRGBA *color);
G_END_DECLS
diff --git a/testsuite/gsk/path-special-cases.c b/testsuite/gsk/path-special-cases.c
index bc83ba9249..927bc578d7 100644
--- a/testsuite/gsk/path-special-cases.c
+++ b/testsuite/gsk/path-special-cases.c
@@ -466,8 +466,8 @@ test_path_point (void)
g_assert_true (rp->t == 1);
gsk_path_point_get_position (&point, path, &pos);
- gsk_path_point_get_tangent (&point, path, GSK_PATH_START, &t1);
- gsk_path_point_get_tangent (&point, path, GSK_PATH_END, &t2);
+ gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_START, &t1);
+ gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_END, &t2);
curvature = gsk_path_point_get_curvature (&point, path, ¢er);
g_assert_true (graphene_point_equal (&pos, &GRAPHENE_POINT_INIT (100, 100)));