diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index 6a1b760c4b..2c8f1caf88 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -338,6 +338,7 @@
path_fill.c
path_spinner.c
path_walk.c
+ path_text.c
peg_solitaire.c
pickers.c
printing.c
@@ -427,6 +428,9 @@
path_walk.ui
path_world.txt
+
+ path_text.ui
+
icons/16x16/actions/application-exit.png
icons/16x16/actions/document-new.png
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index dd8b4fd894..fe60493016 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -75,6 +75,7 @@ demos = files([
'path_fill.c',
'path_spinner.c',
'path_walk.c',
+ 'path_text.c',
'peg_solitaire.c',
'pickers.c',
'printing.c',
diff --git a/demos/gtk-demo/path_text.c b/demos/gtk-demo/path_text.c
new file mode 100644
index 0000000000..e6ddd70552
--- /dev/null
+++ b/demos/gtk-demo/path_text.c
@@ -0,0 +1,586 @@
+/* Path/Text
+ *
+ * This demo shows how to use GskPath to transform a path along another path.
+ *
+ * It also demonstrates that paths can be filled with more interesting
+ * content than just plain colors.
+ */
+
+#include
+#include
+
+#define GTK_TYPE_PATH_WIDGET (gtk_path_widget_get_type ())
+G_DECLARE_FINAL_TYPE (GtkPathWidget, gtk_path_widget, GTK, PATH_WIDGET, GtkWidget)
+
+#define POINT_SIZE 8
+
+enum {
+ PROP_0,
+ PROP_TEXT,
+ PROP_EDITABLE,
+ N_PROPS
+};
+
+struct _GtkPathWidget
+{
+ GtkWidget parent_instance;
+
+ char *text;
+ gboolean editable;
+
+ graphene_point_t points[4];
+
+ guint active_point;
+
+ GskPath *line_path;
+ GskPath *text_path;
+
+ GdkPaintable *background;
+};
+
+struct _GtkPathWidgetClass
+{
+ GtkWidgetClass parent_class;
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+G_DEFINE_TYPE (GtkPathWidget, gtk_path_widget, GTK_TYPE_WIDGET)
+
+static GskPath *
+create_path_from_text (GtkWidget *widget,
+ const char *text,
+ graphene_point_t *out_offset)
+{
+ PangoLayout *layout;
+ PangoFontDescription *desc;
+ GskPathBuilder *builder;
+ GskPath *result;
+
+ layout = gtk_widget_create_pango_layout (widget, text);
+ desc = pango_font_description_from_string ("sans bold 36");
+ pango_layout_set_font_description (layout, desc);
+ pango_font_description_free (desc);
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_layout (builder, layout);
+ result = gsk_path_builder_free_to_path (builder);
+
+ if (out_offset)
+ graphene_point_init (out_offset, 0, - pango_layout_get_baseline (layout) / (double) PANGO_SCALE);
+ g_object_unref (layout);
+
+ return result;
+}
+
+typedef struct
+{
+ GskPathMeasure *measure;
+ GskPathBuilder *builder;
+ graphene_point_t offset;
+ double scale;
+} GtkPathTransform;
+
+static void
+gtk_path_transform_point (GskPathMeasure *measure,
+ const graphene_point_t *pt,
+ const graphene_point_t *offset,
+ float scale,
+ graphene_point_t *res)
+{
+ graphene_vec2_t tangent;
+ GskPathPoint point;
+
+ if (gsk_path_measure_get_point (measure, (pt->x + offset->x) * scale, &point))
+ {
+ GskPath *path = gsk_path_measure_get_path (measure);
+
+ gsk_path_point_get_position (&point, path, res);
+ gsk_path_point_get_tangent (&point, path, GSK_PATH_TO_END, &tangent);
+
+ res->x -= (pt->y + offset->y) * scale * graphene_vec2_get_y (&tangent);
+ res->y += (pt->y + offset->y) * scale * graphene_vec2_get_x (&tangent);
+ }
+}
+
+static gboolean
+gtk_path_transform_op (GskPathOperation op,
+ const graphene_point_t *pts,
+ gsize n_pts,
+ float weight,
+ gpointer data)
+{
+ GtkPathTransform *transform = data;
+
+ switch (op)
+ {
+ case GSK_PATH_MOVE:
+ {
+ graphene_point_t res;
+ gtk_path_transform_point (transform->measure, &pts[0], &transform->offset, transform->scale, &res);
+ gsk_path_builder_move_to (transform->builder, res.x, res.y);
+ }
+ break;
+
+ case GSK_PATH_LINE:
+ {
+ graphene_point_t res;
+ gtk_path_transform_point (transform->measure, &pts[1], &transform->offset, transform->scale, &res);
+ gsk_path_builder_line_to (transform->builder, res.x, res.y);
+ }
+ break;
+
+ case GSK_PATH_QUAD:
+ {
+ graphene_point_t res[2];
+ gtk_path_transform_point (transform->measure, &pts[1], &transform->offset, transform->scale, &res[0]);
+ gtk_path_transform_point (transform->measure, &pts[2], &transform->offset, transform->scale, &res[1]);
+ gsk_path_builder_quad_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y);
+ }
+ break;
+
+ case GSK_PATH_CUBIC:
+ {
+ graphene_point_t res[3];
+ gtk_path_transform_point (transform->measure, &pts[1], &transform->offset, transform->scale, &res[0]);
+ gtk_path_transform_point (transform->measure, &pts[2], &transform->offset, transform->scale, &res[1]);
+ gtk_path_transform_point (transform->measure, &pts[3], &transform->offset, transform->scale, &res[2]);
+ gsk_path_builder_cubic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, res[2].x, res[2].y);
+ }
+ break;
+
+ case GSK_PATH_CONIC:
+ {
+ graphene_point_t res[2];
+ gtk_path_transform_point (transform->measure, &pts[1], &transform->offset, transform->scale, &res[0]);
+ gtk_path_transform_point (transform->measure, &pts[3], &transform->offset, transform->scale, &res[1]);
+ gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
+ }
+ break;
+
+ case GSK_PATH_CLOSE:
+ gsk_path_builder_close (transform->builder);
+ break;
+
+ default:
+ g_assert_not_reached();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GskPath *
+gtk_path_transform (GskPath *line_path,
+ GskPath *path,
+ const graphene_point_t *offset)
+{
+ GskPathMeasure *measure = gsk_path_measure_new (line_path);
+ GtkPathTransform transform = { measure, gsk_path_builder_new (), *offset };
+ graphene_rect_t bounds;
+
+ gsk_path_get_bounds (path, &bounds);
+ if (bounds.origin.x + bounds.size.width > 0)
+ transform.scale = gsk_path_measure_get_length (measure) / (bounds.origin.x + bounds.size.width);
+ else
+ transform.scale = 1.0f;
+
+ gsk_path_foreach (path, -1, gtk_path_transform_op, &transform);
+
+ gsk_path_measure_unref (measure);
+
+ return gsk_path_builder_free_to_path (transform.builder);
+}
+
+static void
+gtk_path_widget_clear_text_path (GtkPathWidget *self)
+{
+ g_clear_pointer (&self->text_path, gsk_path_unref);
+}
+
+static void
+gtk_path_widget_clear_paths (GtkPathWidget *self)
+{
+ gtk_path_widget_clear_text_path (self);
+
+ g_clear_pointer (&self->line_path, gsk_path_unref);
+}
+
+static void
+gtk_path_widget_create_text_path (GtkPathWidget *self)
+{
+ GskPath *path;
+ graphene_point_t offset;
+
+ gtk_path_widget_clear_text_path (self);
+
+ path = create_path_from_text (GTK_WIDGET (self), self->text, &offset);
+ self->text_path = gtk_path_transform (self->line_path, path, &offset);
+
+ gsk_path_unref (path);
+}
+
+static void
+gtk_path_widget_create_paths (GtkPathWidget *self)
+{
+ double width = gtk_widget_get_width (GTK_WIDGET (self));
+ double height = gtk_widget_get_height (GTK_WIDGET (self));
+ GskPathBuilder *builder;
+
+ gtk_path_widget_clear_paths (self);
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_move_to (builder,
+ self->points[0].x * width, self->points[0].y * height);
+ gsk_path_builder_cubic_to (builder,
+ self->points[1].x * width, self->points[1].y * height,
+ self->points[2].x * width, self->points[2].y * height,
+ self->points[3].x * width, self->points[3].y * height);
+ self->line_path = gsk_path_builder_free_to_path (builder);
+
+ gtk_path_widget_create_text_path (self);
+}
+
+static void
+gtk_path_widget_allocate (GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ GtkPathWidget *self = GTK_PATH_WIDGET (widget);
+
+ GTK_WIDGET_CLASS (gtk_path_widget_parent_class)->size_allocate (widget, width, height, baseline);
+
+ gtk_path_widget_create_paths (self);
+}
+
+static void
+gtk_path_widget_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ GtkPathWidget *self = GTK_PATH_WIDGET (widget);
+ double width = gtk_widget_get_width (widget);
+ double height = gtk_widget_get_height (widget);
+ GskPath *path;
+ GskStroke *stroke;
+ gsize i;
+
+ /* frosted glass the background */
+ gtk_snapshot_push_blur (snapshot, 100);
+ gdk_paintable_snapshot (self->background, snapshot, width, height);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 0.6 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
+ gtk_snapshot_pop (snapshot);
+
+ /* draw the text */
+ if (self->text_path)
+ {
+ gtk_snapshot_push_fill (snapshot, self->text_path, GSK_FILL_RULE_WINDING);
+ gdk_paintable_snapshot (self->background, snapshot, width, height);
+
+ /* ... with an emboss effect */
+ stroke = gsk_stroke_new (2.0);
+ gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT(1, 1));
+ gtk_snapshot_push_stroke (snapshot, self->text_path, stroke);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 0.2 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
+ gsk_stroke_free (stroke);
+ gtk_snapshot_pop (snapshot);
+
+ gtk_snapshot_pop (snapshot);
+ }
+
+ if (self->editable && self->line_path)
+ {
+ GskPathBuilder *builder;
+
+ /* draw the control line */
+ stroke = gsk_stroke_new (1.0);
+ gtk_snapshot_push_stroke (snapshot, self->line_path, stroke);
+ gsk_stroke_free (stroke);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
+ gtk_snapshot_pop (snapshot);
+
+ /* draw the points */
+ builder = gsk_path_builder_new ();
+ for (i = 0; i < 4; i++)
+ {
+ gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), POINT_SIZE);
+ }
+ path = gsk_path_builder_free_to_path (builder);
+
+ gtk_snapshot_push_fill (snapshot, path, GSK_FILL_RULE_WINDING);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 1, 1, 1, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
+ gtk_snapshot_pop (snapshot);
+
+ stroke = gsk_stroke_new (1.0);
+ gtk_snapshot_push_stroke (snapshot, path, stroke);
+ gsk_stroke_free (stroke);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA) { 0, 0, 0, 1 }, &GRAPHENE_RECT_INIT (0, 0, width, height));
+ gtk_snapshot_pop (snapshot);
+
+ gsk_path_unref (path);
+ }
+}
+
+static void
+gtk_path_widget_set_text (GtkPathWidget *self,
+ const char *text)
+{
+ if (g_strcmp0 (self->text, text) == 0)
+ return;
+
+ g_free (self->text);
+ self->text = g_strdup (text);
+
+ gtk_path_widget_create_paths (self);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEXT]);
+}
+
+static void
+gtk_path_widget_set_editable (GtkPathWidget *self,
+ gboolean editable)
+{
+ if (self->editable == editable)
+ return;
+
+ self->editable = editable;
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EDITABLE]);
+}
+
+static void
+gtk_path_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+
+{
+ GtkPathWidget *self = GTK_PATH_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_TEXT:
+ gtk_path_widget_set_text (self, g_value_get_string (value));
+ break;
+
+ case PROP_EDITABLE:
+ gtk_path_widget_set_editable (self, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_path_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkPathWidget *self = GTK_PATH_WIDGET (object);
+
+ switch (prop_id)
+ {
+ case PROP_TEXT:
+ g_value_set_string (value, self->text);
+ break;
+
+ case PROP_EDITABLE:
+ g_value_set_boolean (value, self->editable);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_path_widget_dispose (GObject *object)
+{
+ GtkPathWidget *self = GTK_PATH_WIDGET (object);
+
+ gtk_path_widget_clear_paths (self);
+
+ G_OBJECT_CLASS (gtk_path_widget_parent_class)->dispose (object);
+}
+
+static void
+gtk_path_widget_class_init (GtkPathWidgetClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gtk_path_widget_dispose;
+ object_class->set_property = gtk_path_widget_set_property;
+ object_class->get_property = gtk_path_widget_get_property;
+
+ widget_class->size_allocate = gtk_path_widget_allocate;
+ widget_class->snapshot = gtk_path_widget_snapshot;
+
+ properties[PROP_TEXT] =
+ g_param_spec_string ("text",
+ "text",
+ "Text transformed along a path",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_EDITABLE] =
+ g_param_spec_boolean ("editable",
+ "editable",
+ "If the path can be edited by the user",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+drag_begin (GtkGestureDrag *gesture,
+ double x,
+ double y,
+ GtkPathWidget *self)
+{
+ graphene_point_t mouse = GRAPHENE_POINT_INIT (x, y);
+ double width = gtk_widget_get_width (GTK_WIDGET (self));
+ double height = gtk_widget_get_height (GTK_WIDGET (self));
+ gsize i;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (graphene_point_distance (&GRAPHENE_POINT_INIT (self->points[i].x * width, self->points[i].y * height), &mouse, NULL, NULL) <= POINT_SIZE)
+ {
+ self->active_point = i;
+ break;
+ }
+ }
+ if (i == 4)
+ {
+ gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
+ return;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+drag_update (GtkGestureDrag *drag,
+ double offset_x,
+ double offset_y,
+ GtkPathWidget *self)
+{
+ double width = gtk_widget_get_width (GTK_WIDGET (self));
+ double height = gtk_widget_get_height (GTK_WIDGET (self));
+ double start_x, start_y;
+
+ gtk_gesture_drag_get_start_point (drag, &start_x, &start_y);
+
+ self->points[self->active_point] = GRAPHENE_POINT_INIT ((start_x + offset_x) / width,
+ (start_y + offset_y) / height);
+ self->points[self->active_point].x = CLAMP (self->points[self->active_point].x, 0, 1);
+ self->points[self->active_point].y = CLAMP (self->points[self->active_point].y, 0, 1);
+
+ gtk_path_widget_create_paths (self);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+pointer_motion (GtkEventControllerMotion *controller,
+ double x,
+ double y,
+ GtkPathWidget *self)
+{
+ GskPathPoint point;
+ graphene_point_t pos;
+
+ if (gsk_path_get_closest_point (self->line_path,
+ &GRAPHENE_POINT_INIT (x, y),
+ INFINITY,
+ &point))
+ {
+ gsk_path_point_get_position (&point, self->line_path, &pos);
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+ }
+}
+
+static void
+pointer_leave (GtkEventControllerMotion *controller,
+ GtkPathWidget *self)
+{
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+gtk_path_widget_init (GtkPathWidget *self)
+{
+ GtkEventController *controller;
+
+ controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
+ g_signal_connect (controller, "drag-begin", G_CALLBACK (drag_begin), self);
+ g_signal_connect (controller, "drag-update", G_CALLBACK (drag_update), self);
+ g_signal_connect (controller, "drag-end", G_CALLBACK (drag_update), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
+
+ controller = GTK_EVENT_CONTROLLER (gtk_event_controller_motion_new ());
+ g_signal_connect (controller, "enter", G_CALLBACK (pointer_motion), self);
+ g_signal_connect (controller, "motion", G_CALLBACK (pointer_motion), self);
+ g_signal_connect (controller, "leave", G_CALLBACK (pointer_leave), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), controller);
+
+ self->points[0] = GRAPHENE_POINT_INIT (0.1, 0.9);
+ self->points[1] = GRAPHENE_POINT_INIT (0.3, 0.1);
+ self->points[2] = GRAPHENE_POINT_INIT (0.7, 0.1);
+ self->points[3] = GRAPHENE_POINT_INIT (0.9, 0.9);
+
+ self->background = GDK_PAINTABLE (gdk_texture_new_from_resource ("/sliding_puzzle/portland-rose.jpg"));
+
+ gtk_path_widget_set_text (self, "It's almost working");
+}
+
+GtkWidget *
+gtk_path_widget_new (void)
+{
+ GtkPathWidget *self;
+
+ self = g_object_new (GTK_TYPE_PATH_WIDGET, NULL);
+
+ return GTK_WIDGET (self);
+}
+
+GtkWidget *
+do_path_text (GtkWidget *do_widget)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkBuilder *builder;
+
+ g_type_ensure (GTK_TYPE_PATH_WIDGET);
+
+ builder = gtk_builder_new_from_resource ("/path_text/path_text.ui");
+ window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
+ gtk_window_set_display (GTK_WINDOW (window),
+ gtk_widget_get_display (do_widget));
+ g_object_add_weak_pointer (G_OBJECT (window), (gpointer *) &window);
+ g_object_unref (builder);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_window_present (GTK_WINDOW (window));
+ else
+ gtk_window_destroy (GTK_WINDOW (window));
+
+ return window;
+}
diff --git a/demos/gtk-demo/path_text.ui b/demos/gtk-demo/path_text.ui
new file mode 100644
index 0000000000..ffd96a7d6b
--- /dev/null
+++ b/demos/gtk-demo/path_text.ui
@@ -0,0 +1,38 @@
+
+
+
+
diff --git a/demos/gtk-demo/path_walk.c b/demos/gtk-demo/path_walk.c
index 84af419413..cd588b0e5d 100644
--- a/demos/gtk-demo/path_walk.c
+++ b/demos/gtk-demo/path_walk.c
@@ -1,6 +1,6 @@
-/* Path/Map
+/* Path/Walk
*
- * This demo shows how to draw a map using paths.
+ * This demo draws a world map and shows how to animate objects along a GskPath.
*
* The world map that is used here is a path with 211 lines and 1569 cubic
* Bėzier segments in 121 contours.
@@ -16,6 +16,7 @@ G_DECLARE_FINAL_TYPE (GtkPathWalk, gtk_path_walk, GTK, PATH_WALK, GtkWidget)
enum {
PROP_0,
+ PROP_N_POINTS,
PROP_PATH,
N_PROPS
};
@@ -25,7 +26,10 @@ struct _GtkPathWalk
GtkWidget parent_instance;
GskPath *path;
+ GskPathMeasure *measure;
graphene_rect_t bounds;
+ GskPath *arrow_path;
+ guint n_points;
};
struct _GtkPathWalkClass
@@ -37,6 +41,74 @@ static GParamSpec *properties[N_PROPS] = { NULL, };
G_DEFINE_TYPE (GtkPathWalk, gtk_path_walk, GTK_TYPE_WIDGET)
+static void
+rgba_init_from_hsla (GdkRGBA *rgba,
+ float hue,
+ float saturation,
+ float lightness,
+ float alpha)
+{
+ float m1, m2;
+
+ if (lightness <= 0.5)
+ m2 = lightness * (1 + saturation);
+ else
+ m2 = lightness + saturation - lightness * saturation;
+ m1 = 2 * lightness - m2;
+
+ rgba->alpha = alpha;
+
+ if (saturation == 0)
+ {
+ rgba->red = lightness;
+ rgba->green = lightness;
+ rgba->blue = lightness;
+ }
+ else
+ {
+ hue = hue + 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ rgba->red = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ rgba->red = m2;
+ else if (hue < 240)
+ rgba->red = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ rgba->red = m1;
+
+ hue -= 120;
+ if (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ rgba->green = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ rgba->green = m2;
+ else if (hue < 240)
+ rgba->green = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ rgba->green = m1;
+
+ hue -= 120;
+ if (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ rgba->blue = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ rgba->blue = m2;
+ else if (hue < 240)
+ rgba->blue = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ rgba->blue = m1;
+ }
+}
+
static void
gtk_path_walk_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
@@ -44,7 +116,9 @@ gtk_path_walk_snapshot (GtkWidget *widget,
GtkPathWalk *self = GTK_PATH_WALK (widget);
double width = gtk_widget_get_width (widget);
double height = gtk_widget_get_height (widget);
+ float length, progress;
GskStroke *stroke;
+ guint i;
if (self->path == NULL)
return;
@@ -57,6 +131,35 @@ gtk_path_walk_snapshot (GtkWidget *widget,
gtk_snapshot_pop (snapshot);
gsk_stroke_free (stroke);
+ length = gsk_path_measure_get_length (self->measure);
+ progress = 25.f * gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget)) / G_USEC_PER_SEC;
+
+ stroke = gsk_stroke_new (1.0);
+ for (i = 0; i < self->n_points; i++)
+ {
+ GskPathPoint point;
+ graphene_point_t position;
+ float angle;
+ GdkRGBA color;
+ float distance;
+
+ distance = i * length / self->n_points;
+ distance = fmod (distance + progress, length);
+
+ gsk_path_measure_get_point (self->measure, distance, &point);
+ gsk_path_point_get_position (&point, self->path, &position);
+ angle = gsk_path_point_get_rotation (&point, self->path, GSK_PATH_FROM_START);
+ rgba_init_from_hsla (&color, 360.f * i / self->n_points, 1, 0.5, 1);
+
+ gtk_snapshot_save (snapshot);
+ gtk_snapshot_translate (snapshot, &position);
+ gtk_snapshot_rotate (snapshot, angle);
+ gtk_snapshot_append_fill (snapshot, self->arrow_path, GSK_FILL_RULE_EVEN_ODD, &color);
+ gtk_snapshot_append_stroke (snapshot, self->arrow_path, stroke, &(GdkRGBA) { 0, 0, 0, 1 });
+ gtk_snapshot_restore (snapshot);
+ }
+
+ gsk_stroke_free (stroke);
gtk_snapshot_restore (snapshot);
}
@@ -77,6 +180,20 @@ gtk_path_walk_measure (GtkWidget *widget,
*minimum = *natural = (int) ceilf (self->bounds.size.height);
}
+static void
+gtk_path_walk_set_n_points (GtkPathWalk *self,
+ gsize n_points)
+{
+ if (self->n_points == n_points)
+ return;
+
+ self->n_points = n_points;
+
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_N_POINTS]);
+}
+
static void
gtk_path_walk_set_path (GtkPathWalk *self,
GskPath *path)
@@ -94,6 +211,7 @@ gtk_path_walk_set_path (GtkPathWalk *self,
stroke = gsk_stroke_new (2.0);
gsk_path_get_stroke_bounds (path, stroke, &self->bounds);
gsk_stroke_free (stroke);
+ self->measure = gsk_path_measure_new (self->path);
}
gtk_widget_queue_resize (GTK_WIDGET (self));
@@ -112,6 +230,10 @@ gtk_path_walk_set_property (GObject *object,
switch (prop_id)
{
+ case PROP_N_POINTS:
+ gtk_path_walk_set_n_points (self, g_value_get_uint (value));
+ break;
+
case PROP_PATH:
gtk_path_walk_set_path (self, g_value_get_boxed (value));
break;
@@ -132,6 +254,10 @@ gtk_path_walk_get_property (GObject *object,
switch (prop_id)
{
+ case PROP_N_POINTS:
+ g_value_set_uint (value, self->n_points);
+ break;
+
case PROP_PATH:
g_value_set_boxed (value, self->path);
break;
@@ -148,6 +274,8 @@ gtk_path_walk_dispose (GObject *object)
GtkPathWalk *self = GTK_PATH_WALK (object);
g_clear_pointer (&self->path, gsk_path_unref);
+ g_clear_pointer (&self->measure, gsk_path_measure_unref);
+ g_clear_pointer (&self->arrow_path, gsk_path_unref);
G_OBJECT_CLASS (gtk_path_walk_parent_class)->dispose (object);
}
@@ -165,6 +293,13 @@ gtk_path_walk_class_init (GtkPathWalkClass *klass)
widget_class->snapshot = gtk_path_walk_snapshot;
widget_class->measure = gtk_path_walk_measure;
+ properties[PROP_N_POINTS] =
+ g_param_spec_uint ("n-points",
+ NULL, NULL,
+ 1, G_MAXUINT,
+ 500,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
properties[PROP_PATH] =
g_param_spec_boxed ("path",
NULL, NULL,
@@ -174,6 +309,16 @@ gtk_path_walk_class_init (GtkPathWalkClass *klass)
g_object_class_install_properties (object_class, N_PROPS, properties);
}
+static gboolean
+tick_tick_tick (GtkWidget *self,
+ GdkFrameClock *frame_clock,
+ gpointer unused)
+{
+ gtk_widget_queue_draw (GTK_WIDGET (self));
+
+ return G_SOURCE_CONTINUE;
+}
+
static void
gtk_path_walk_init (GtkPathWalk *self)
{
@@ -185,6 +330,9 @@ gtk_path_walk_init (GtkPathWalk *self)
g_bytes_unref (data);
gtk_path_walk_set_path (self, path);
gsk_path_unref (path);
+ self->arrow_path = gsk_path_parse ("M 5 0 L 0 -5. 0 -2, -5 -2, -5 2, 0 2, 0 5 Z");
+ self->n_points = 500;
+ gtk_widget_add_tick_callback (GTK_WIDGET (self), tick_tick_tick, NULL, NULL);
}
GtkWidget *
diff --git a/demos/gtk-demo/path_walk.ui b/demos/gtk-demo/path_walk.ui
index 6a382031c2..352b81baa8 100644
--- a/demos/gtk-demo/path_walk.ui
+++ b/demos/gtk-demo/path_walk.ui
@@ -2,16 +2,32 @@
World Map
-
+
+
+
+
vertical
+
true
true
-
+
diff --git a/docs/reference/gsk/paths.md b/docs/reference/gsk/paths.md
index dd672f888b..25df508e74 100644
--- a/docs/reference/gsk/paths.md
+++ b/docs/reference/gsk/paths.md
@@ -115,6 +115,14 @@ When paths are rendered as part of an interactive interface, it is sometimes
necessary to determine whether the mouse points is over the path. GSK provides
[method@Gsk.Path.in_fill] for this purpose.
+## Path length
+
+An important property of paths is their **_length_**. Computing it efficiently
+requires caching, therefore GSK provides a separate [struct@Gsk.PathMeasure] object
+to deal with path lengths. After constructing a `GskPathMeasure` object for a path,
+it can be used to determine the length of the path with [method@Gsk.PathMeasure.get_length]
+and locate points at a given distance into the path with [method@Gsk.PathMeasure.get_point].
+
## Other Path APIs
Paths have uses beyond rendering, for example as trajectories in animations.
@@ -127,11 +135,26 @@ You can query properties of a path at certain point once you have a
`GskPathPoint` structs can be compared for equality with [method@Gsk.PathPoint.equal]
and ordered wrt. to which one comes first, using [method@Gsk.PathPoint.compare].
-To obtain a `GskPathPoint`, use [method@Gsk.Path.get_closest_point], [method@Gsk.Path.get_start_point] or [method@Gsk.Path.get_end_point].
+To obtain a `GskPathPoint`, use [method@Gsk.Path.get_closest_point],
+[method@Gsk.Path.get_start_point], [method@Gsk.Path.get_end_point] or
+[method@Gsk.PathMeasure.get_point].
To query properties of the path at a point, use [method@Gsk.PathPoint.get_position],
-[method@Gsk.PathPoint.get_tangent], [method@Gsk.PathPoint.get_rotation] and
-[method@Gsk.PathPoint.get_curvature].
+[method@Gsk.PathPoint.get_tangent], [method@Gsk.PathPoint.get_rotation],
+[method@Gsk.PathPoint.get_curvature] and [method@Gsk.PathPoint.get_distance].
+
+Some of the properties can have different values for the path going into
+the point and the path leaving the point, typically at points where the
+path takes sharp turns. Examples for this are tangents (which can have up
+to 4 different values) and curvatures (which can have two different values).
+
+
## Going beyond GskPath
@@ -139,6 +162,7 @@ Lots of powerful functionality can be implemented for paths:
- Finding intersections
- Offsetting curves
+- Turning stroke outlines into paths
- Molding curves (making them pass through a given point)
GSK does not provide API for all of these, but it does offer a way to get at
diff --git a/gsk/gsk.h b/gsk/gsk.h
index 90c4d1c259..9f2ee451dd 100644
--- a/gsk/gsk.h
+++ b/gsk/gsk.h
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c
index faf0847d75..617c277ad4 100644
--- a/gsk/gskcontour.c
+++ b/gsk/gskcontour.c
@@ -83,6 +83,18 @@ struct _GskContourClass
gboolean emit_move_to,
GskRealPathPoint *start,
GskRealPathPoint *end);
+ gpointer (* init_measure) (const GskContour *contour,
+ float tolerance,
+ float *out_length);
+ void (* free_measure) (const GskContour *contour,
+ gpointer measure_data);
+ void (* get_point) (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ GskRealPathPoint *result);
+ float (* get_distance) (const GskContour *contour,
+ GskRealPathPoint *point,
+ gpointer measure_data);
};
/* {{{ Utilities */
@@ -632,6 +644,252 @@ gsk_standard_contour_add_segment (const GskContour *contour,
}
}
+typedef struct
+{
+ gsize idx;
+ float t;
+ float length;
+} CurvePoint;
+
+static void
+add_measure (const GskCurve *curve,
+ float length,
+ float tolerance,
+ float t1,
+ float l1,
+ GArray *array)
+{
+ GskCurve c;
+ float ll, l0;
+ float t0;
+ float tt, ll0;
+ CurvePoint *p = &g_array_index (array, CurvePoint, array->len - 1);
+ gsize idx = p->idx;
+
+ /* Check if we can add (t1, length + l1) without further
+ * splitting. We check two things:
+ * - Is the curve close to a straight line (length-wise) ?
+ * - Does the roundtrip length<>t not deviate too much ?
+ */
+
+ if (curve->op == GSK_PATH_LINE ||
+ curve->op == GSK_PATH_CLOSE)
+ goto done;
+
+ t0 = (p->t + t1) / 2;
+ if (t0 == p->t || t0 == t1)
+ goto done;
+
+ gsk_curve_split (curve, t0, &c, NULL);
+ l0 = gsk_curve_get_length (&c);
+ ll = (p->length + length + l1) / 2;
+
+ tt = gsk_curve_at_length (curve, l0, 0.001);
+ gsk_curve_split (curve, tt, &c, NULL);
+ ll0 = gsk_curve_get_length (&c);
+
+ if (fabsf (length + l0 - ll) < tolerance &&
+ fabsf (ll0 - l0) < tolerance)
+ {
+done:
+ g_array_append_val (array, ((CurvePoint){ idx, t1, length + l1 }));
+ }
+ else
+ {
+ add_measure (curve, length, tolerance, t0, l0, array);
+ add_measure (curve, length, tolerance, t1, l1, array);
+ }
+}
+
+static int
+cmpfloat (const void *p1, const void *p2)
+{
+ const float *f1 = p1;
+ const float *f2 = p2;
+ return *f1 < *f2 ? -1 : (*f1 > *f2 ? 1 : 0);
+}
+
+static gpointer
+gsk_standard_contour_init_measure (const GskContour *contour,
+ float tolerance,
+ float *out_length)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+ GArray *array;
+ float length;
+
+ array = g_array_new (FALSE, FALSE, sizeof (CurvePoint));
+
+ length = 0;
+
+ for (gsize i = 1; i < self->n_ops; i++)
+ {
+ GskCurve curve;
+ float l;
+ float t[3];
+ int n;
+
+ gsk_curve_init (&curve, self->ops[i]);
+
+ g_array_append_val (array, ((CurvePoint) { i, 0, length }));
+
+ n = gsk_curve_get_curvature_points (&curve, t);
+ qsort (t, n, sizeof (float), cmpfloat);
+
+ for (int j = 0; j < n; j++)
+ {
+ l = gsk_curve_get_length_to (&curve, t[j]);
+ add_measure (&curve, length, tolerance, t[j], l, array);
+ }
+
+ l = gsk_curve_get_length (&curve);
+ add_measure (&curve, length, tolerance, 1, l, array);
+
+ length += l;
+ }
+
+ *out_length = length;
+
+#if 0
+ g_print ("%lu ops, %u measure points\n", self->n_ops, array->len);
+ for (gsize i = 0; i < array->len; i++)
+ {
+ CurvePoint *pp = &g_array_index (array, CurvePoint, i);
+ const char *opname[] = { "M", "Z", "L", "Q", "C" };
+ GskPathOperation op = gsk_pathop_op (self->ops[pp->idx]);
+
+ g_print ("%lu %s %g -> %g\n", pp->idx, opname[op], pp->t, pp->length);
+ }
+#endif
+
+ return array;
+}
+
+static void
+gsk_standard_contour_free_measure (const GskContour *contour,
+ gpointer data)
+{
+ g_array_free (data, TRUE);
+}
+
+static void
+gsk_standard_contour_get_point (const GskContour *contour,
+ gpointer measure_data,
+ float distance,
+ GskRealPathPoint *result)
+{
+ const GskStandardContour *self = (const GskStandardContour *) contour;
+ GArray *array = measure_data;
+ gsize i0, i1;
+ CurvePoint *p0, *p1;
+
+ if (self->n_ops == 1)
+ {
+ result->idx = 0;
+ result->t = 1;
+ return;
+ }
+
+ i0 = 0;
+ i1 = array->len - 1;
+ while (i0 + 1 < i1)
+ {
+ gsize i = (i0 + i1) / 2;
+ CurvePoint *p = &g_array_index (array, CurvePoint, i);
+
+ if (p->length < distance)
+ i0 = i;
+ else if (p->length > distance)
+ i1 = i;
+ else
+ {
+ result->idx = p->idx;
+ result->t = p->t;
+ return;
+ }
+ }
+
+ p0 = &g_array_index (array, CurvePoint, i0);
+ p1 = &g_array_index (array, CurvePoint, i1);
+
+ if (distance >= p1->length)
+ {
+ if (p1->idx == self->n_ops - 1)
+ {
+ result->idx = p1->idx;
+ result->t = 1;
+ }
+ else
+ {
+ result->idx = p1->idx + 1;
+ result->t = 0;
+ }
+ }
+ else
+ {
+ float fraction, t0;
+
+ g_assert (p0->idx == p1->idx || p0->t == 1);
+
+ t0 = p0->idx == p1->idx ? p0->t : 0;
+
+ result->idx = p1->idx;
+
+ fraction = (distance - p0->length) / (p1->length - p0->length);
+ g_assert (fraction >= 0.f && fraction <= 1.f);
+ result->t = t0 * (1 - fraction) + p1->t * fraction;
+ g_assert (result->t >= 0.f && result->t <= 1.f);
+ }
+}
+
+static float
+gsk_standard_contour_get_distance (const GskContour *contour,
+ GskRealPathPoint *point,
+ gpointer measure_data)
+{
+ GArray *array = measure_data;
+ gsize i0, i1;
+ CurvePoint *p0, *p1;
+ float fraction, t0;
+
+ if (G_UNLIKELY (point->idx == 0))
+ return 0;
+
+ i0 = 0;
+ i1 = array->len - 1;
+ while (i0 + 1 < i1)
+ {
+ gsize i = (i0 + i1) / 2;
+ CurvePoint *p = &g_array_index (array, CurvePoint, i);
+
+ if (p->idx > point->idx)
+ i1 = i;
+ else if (p->idx < point->idx)
+ i0 = i;
+ else if (p->t > point->t)
+ i1 = i;
+ else if (p->t < point->t)
+ i0 = i;
+ else
+ return p->length;
+ }
+
+ p0 = &g_array_index (array, CurvePoint, i0);
+ p1 = &g_array_index (array, CurvePoint, i1);
+
+ g_assert (p0->idx == p1->idx || p0->t == 1);
+
+ t0 = p0->idx == p1->idx ? p0->t : 0;
+
+ g_assert (p1->idx == point->idx);
+ g_assert (t0 <= point->t && point->t <= p1->t);
+
+ fraction = (point->t - t0) / (p1->t - t0);
+ g_assert (fraction >= 0.f && fraction <= 1.f);
+
+ return p0->length * (1 - fraction) + p1->length * fraction;
+}
+
static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
{
sizeof (GskStandardContour),
@@ -652,6 +910,10 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS =
gsk_standard_contour_get_tangent,
gsk_standard_contour_get_curvature,
gsk_standard_contour_add_segment,
+ gsk_standard_contour_init_measure,
+ gsk_standard_contour_free_measure,
+ gsk_standard_contour_get_point,
+ gsk_standard_contour_get_distance,
};
/* You must ensure the contour has enough size allocated,
@@ -841,6 +1103,38 @@ gsk_contour_add_segment (const GskContour *self,
self->klass->add_segment (self, builder, emit_move_to, start, end);
}
+gpointer
+gsk_contour_init_measure (const GskContour *self,
+ float tolerance,
+ float *out_length)
+{
+ return self->klass->init_measure (self, tolerance, out_length);
+}
+
+void
+gsk_contour_free_measure (const GskContour *self,
+ gpointer data)
+{
+ self->klass->free_measure (self, data);
+}
+
+void
+gsk_contour_get_point (const GskContour *self,
+ gpointer measure_data,
+ float distance,
+ GskRealPathPoint *result)
+{
+ self->klass->get_point (self, measure_data, distance, result);
+}
+
+float
+gsk_contour_get_distance (const GskContour *self,
+ GskRealPathPoint *point,
+ gpointer measure_data)
+{
+ return self->klass->get_distance (self, point, measure_data);
+}
+
/* }}} */
/* vim:set foldmethod=marker expandtab: */
diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h
index 7193e4bf1f..25415a99c6 100644
--- a/gsk/gskcontourprivate.h
+++ b/gsk/gskcontourprivate.h
@@ -80,5 +80,17 @@ void gsk_contour_add_segment (const GskContou
GskRealPathPoint *start,
GskRealPathPoint *end);
+gpointer gsk_contour_init_measure (const GskContour *self,
+ float tolerance,
+ float *out_length);
+void gsk_contour_free_measure (const GskContour *self,
+ gpointer data);
+void gsk_contour_get_point (const GskContour *self,
+ gpointer measure_data,
+ float distance,
+ GskRealPathPoint *result);
+float gsk_contour_get_distance (const GskContour *self,
+ GskRealPathPoint *point,
+ gpointer measure_data);
G_END_DECLS
diff --git a/gsk/gskcurve-ct-values.c b/gsk/gskcurve-ct-values.c
new file mode 100644
index 0000000000..1158242139
--- /dev/null
+++ b/gsk/gskcurve-ct-values.c
@@ -0,0 +1,213 @@
+/* Legendre-Gauss values,
+ * see https://pomax.github.io/bezierinfo/legendre-gauss.html
+ */
+
+#if 0
+/* n = 64 */
+static const double T[] = {
+ -0.0243502926634244325089558428537156614268871093149758091634531663960566965166295288529853061657116894882370493013671717560479926679408068852617342586968190919443025679363843727751902756254975073084367002129407854253246662805532069172532219089321005870178809284335033318073251039701073379759,
+ 0.0243502926634244325089558428537156614268871093149758091634531663960566965166295288529853061657116894882370493013671717560479926679408068852617342586968190919443025679363843727751902756254975073084367002129407854253246662805532069172532219089321005870178809284335033318073251039701073379759,
+ -0.0729931217877990394495429419403374932441261816687788533563163323377395217254429050181833064967505478802134768007678458612956459126148837496307967995621683597067400860540057918571357609346700883624064782909888895547912499697295335516804810990011835717296819569741981551097569810739977931249,
+ 0.0729931217877990394495429419403374932441261816687788533563163323377395217254429050181833064967505478802134768007678458612956459126148837496307967995621683597067400860540057918571357609346700883624064782909888895547912499697295335516804810990011835717296819569741981551097569810739977931249,
+ -0.1214628192961205544703764634922478782186836383371912940423495826006931832245070944213952236889690237679661122848626352437115925113582515415979746598665681268376919737373113667247142315234607397986222184384307059013512155412722263090766858403726100735651684437098923088469949297570988091677,
+ 0.1214628192961205544703764634922478782186836383371912940423495826006931832245070944213952236889690237679661122848626352437115925113582515415979746598665681268376919737373113667247142315234607397986222184384307059013512155412722263090766858403726100735651684437098923088469949297570988091677,
+ -0.1696444204239928180373136297482698441999902667343778505894178746884342357796505591325925801106834127602396624627746208498838190598644711533868179088757129652678801285453336384132061358206434768209251779433993367981112053126660575920785488821886662635718276505127786854167528795165050389903,
+ 0.1696444204239928180373136297482698441999902667343778505894178746884342357796505591325925801106834127602396624627746208498838190598644711533868179088757129652678801285453336384132061358206434768209251779433993367981112053126660575920785488821886662635718276505127786854167528795165050389903,
+ -0.217423643740007084149648748988822617578485831141222348630380401885689634737659235537163737740243604800759921292790013642836998201691226098978544332296437547642195961469059807833597096166848933098833151166287901339013986496737408125314858259377210847115061960167857239951500335854820530586,
+ 0.217423643740007084149648748988822617578485831141222348630380401885689634737659235537163737740243604800759921292790013642836998201691226098978544332296437547642195961469059807833597096166848933098833151166287901339013986496737408125314858259377210847115061960167857239951500335854820530586,
+ -0.2646871622087674163739641725100201179804131362950932439559895448126206429452852982016450901649805445999078728714943692622330016257776575354105370883948495294882935681426154386660616476411740312465060150591301869544672050088454083442813632094277160007745547301572849406353682760306061929911,
+ 0.2646871622087674163739641725100201179804131362950932439559895448126206429452852982016450901649805445999078728714943692622330016257776575354105370883948495294882935681426154386660616476411740312465060150591301869544672050088454083442813632094277160007745547301572849406353682760306061929911,
+ -0.3113228719902109561575126985601568835577153578680501269954571709858169098868398268719654999460149709757804582988077747605532896065842023340674450299515989484487746153929299031759475919924980452933324186984188982046762542556035347023744560814177013801414889023264804693830155588690576492164,
+ 0.3113228719902109561575126985601568835577153578680501269954571709858169098868398268719654999460149709757804582988077747605532896065842023340674450299515989484487746153929299031759475919924980452933324186984188982046762542556035347023744560814177013801414889023264804693830155588690576492164,
+ -0.357220158337668115950442615046202531626264464640909112021237019340099177403802509741325589540743874845093675632547750287037622834793938695456980400958079292460482315821150714268593539935795231095157602025909339384694681190969656053235824652679875951093689200190014853543993102190088381483,
+ 0.357220158337668115950442615046202531626264464640909112021237019340099177403802509741325589540743874845093675632547750287037622834793938695456980400958079292460482315821150714268593539935795231095157602025909339384694681190969656053235824652679875951093689200190014853543993102190088381483,
+ -0.4022701579639916036957667712601588487132652056150208082760431843129087214967261515969669708970990221669508217089555714806012046537594438323569293594638517933840725639831594134038262580440842200076281605641993773325072728778928440394419613403725280285705765326861533477990551765453978736181,
+ 0.4022701579639916036957667712601588487132652056150208082760431843129087214967261515969669708970990221669508217089555714806012046537594438323569293594638517933840725639831594134038262580440842200076281605641993773325072728778928440394419613403725280285705765326861533477990551765453978736181,
+ -0.4463660172534640879849477147589151892067507578262501763082606820212937626970791295735937384813941473610238854736863966831464694923749954564921955859791688348936235671762050333408576202492209167272366373825152067680845198006626563761196191045700093968519269790165913301841545609485952718504,
+ 0.4463660172534640879849477147589151892067507578262501763082606820212937626970791295735937384813941473610238854736863966831464694923749954564921955859791688348936235671762050333408576202492209167272366373825152067680845198006626563761196191045700093968519269790165913301841545609485952718504,
+ -0.4894031457070529574785263070219213908493732974637398373316540793240315585537584844752851087115581833443158831657759501916744927211636581386025171070582998790865902140901838128045602667002106847665927788023098753138400106615804847725751247952878407027140260429761863258319891988431055536872,
+ 0.4894031457070529574785263070219213908493732974637398373316540793240315585537584844752851087115581833443158831657759501916744927211636581386025171070582998790865902140901838128045602667002106847665927788023098753138400106615804847725751247952878407027140260429761863258319891988431055536872,
+ -0.531279464019894545658013903544455247408525588734180238053268047166041778398245121448843253296460411619816073385211875151397248937264089998182375345915413219579220233566173902955487674957069948591213673456625912506280248298229907928060620469290406581396192419570799497688513058132396498814,
+ 0.531279464019894545658013903544455247408525588734180238053268047166041778398245121448843253296460411619816073385211875151397248937264089998182375345915413219579220233566173902955487674957069948591213673456625912506280248298229907928060620469290406581396192419570799497688513058132396498814,
+ -0.5718956462026340342838781166591886431831910060912509932273284719418912212643223327597417735844776972648163821225207266145263395898251858906124801356522395225326954546582593857056725545247314092886133249957455688586118199388064447508712958637376347457406936802691416300157802889354368128467,
+ 0.5718956462026340342838781166591886431831910060912509932273284719418912212643223327597417735844776972648163821225207266145263395898251858906124801356522395225326954546582593857056725545247314092886133249957455688586118199388064447508712958637376347457406936802691416300157802889354368128467,
+ -0.6111553551723932502488529710185489186961245593079718443367976666933088374650288148448667879830703867726577720666491772560110368248450475818132595468834493579434418252468978282181164008820769765174450056817468275783966537351796625224747700783378315186174632657840114512887287754747902924865,
+ 0.6111553551723932502488529710185489186961245593079718443367976666933088374650288148448667879830703867726577720666491772560110368248450475818132595468834493579434418252468978282181164008820769765174450056817468275783966537351796625224747700783378315186174632657840114512887287754747902924865,
+ -0.6489654712546573398577612319934048855296904334732011728792502624366057598865738239773166627826358871699142531853930525866830933399401844502541092962631127742267449897116125748014270680393024011359139031312062573520509858894743036198986443014969748157931850178889912972291202354657382925509,
+ 0.6489654712546573398577612319934048855296904334732011728792502624366057598865738239773166627826358871699142531853930525866830933399401844502541092962631127742267449897116125748014270680393024011359139031312062573520509858894743036198986443014969748157931850178889912972291202354657382925509,
+ -0.6852363130542332425635583710313763019356410785396718681324042749913611976548967332647625541234155413035075739348863233240851709341392873173633850612006618690218164007761541855237208605116527909791956398350719463021018362527198358286721239529091637248252469435642287693207506339068528700205,
+ 0.6852363130542332425635583710313763019356410785396718681324042749913611976548967332647625541234155413035075739348863233240851709341392873173633850612006618690218164007761541855237208605116527909791956398350719463021018362527198358286721239529091637248252469435642287693207506339068528700205,
+ -0.7198818501716108268489402178319472447581380033149019526220473151184468592486433646042300919262498902882179891494724046747921544602557246455427317830132976771174504209146835854012577372395960646854355024460204286901035470812684876587693044068989973704915078171158689213412715251485203442313,
+ 0.7198818501716108268489402178319472447581380033149019526220473151184468592486433646042300919262498902882179891494724046747921544602557246455427317830132976771174504209146835854012577372395960646854355024460204286901035470812684876587693044068989973704915078171158689213412715251485203442313,
+ -0.7528199072605318966118637748856939855517142713220871932461987761167722639636968539390413583009467924995905147153923286347693864945784890119950315095740891764880991728646942920208355501445550654259850385377192973526980795946453961077798744753892199929235882097232836682421729944934586281945,
+ 0.7528199072605318966118637748856939855517142713220871932461987761167722639636968539390413583009467924995905147153923286347693864945784890119950315095740891764880991728646942920208355501445550654259850385377192973526980795946453961077798744753892199929235882097232836682421729944934586281945,
+ -0.7839723589433414076102205252137682840564141249898259334132759617476816578705098509357116190608002325895348207611752987335385494893726027026038902508685496606160441965948835252795524014713290879877269643684102005605450140247125536147801312017065532602835003540212221564314236937509990182173,
+ 0.7839723589433414076102205252137682840564141249898259334132759617476816578705098509357116190608002325895348207611752987335385494893726027026038902508685496606160441965948835252795524014713290879877269643684102005605450140247125536147801312017065532602835003540212221564314236937509990182173,
+ -0.8132653151227975597419233380863033406981418225655972166956485149356586346082019870309280128411412936411423614767918756843380999442447282903502051218203273573634847203121451086379808399639198510674436238195505371716160648058477202993836014352158139813219612968106205248494087577632573534973,
+ 0.8132653151227975597419233380863033406981418225655972166956485149356586346082019870309280128411412936411423614767918756843380999442447282903502051218203273573634847203121451086379808399639198510674436238195505371716160648058477202993836014352158139813219612968106205248494087577632573534973,
+ -0.840629296252580362751691544695873302982489823801755353928202683075593465893922171840726147868117503717663799561956411215937924134571068943700343442753760445948626598735504632170407243376224222403038093781056024445977626666740664628412660960413062370047183186652885532589557452614451434048,
+ 0.840629296252580362751691544695873302982489823801755353928202683075593465893922171840726147868117503717663799561956411215937924134571068943700343442753760445948626598735504632170407243376224222403038093781056024445977626666740664628412660960413062370047183186652885532589557452614451434048,
+ -0.8659993981540928197607833850701575024125019187582496425664279511808356713122188567857456842034906362573453815878913951040194915987006979015304835979058725276345799813088989383312475641092775164460639450521468294104011206574786429237252678172922104036725327539940502197291939132802457917836,
+ 0.8659993981540928197607833850701575024125019187582496425664279511808356713122188567857456842034906362573453815878913951040194915987006979015304835979058725276345799813088989383312475641092775164460639450521468294104011206574786429237252678172922104036725327539940502197291939132802457917836,
+ -0.8893154459951141058534040382728516224291944615104521893194744566084811090577722526400445910623711480590529533188832105988657269430913287263821624762648137092066620632787986348052306840101775313644572400860845559833367997001666659907951051347410546710134120144598833115095140475669485797579,
+ 0.8893154459951141058534040382728516224291944615104521893194744566084811090577722526400445910623711480590529533188832105988657269430913287263821624762648137092066620632787986348052306840101775313644572400860845559833367997001666659907951051347410546710134120144598833115095140475669485797579,
+ -0.9105221370785028057563806680083298610134880848883640292531723714467102234556291968179018775780308458024302103848451312741663820589200520720207891653884985710130867134073520525932445557074805974235006810370309087879564826639263972805682465506594098949560288847385983395160311034445386606259,
+ 0.9105221370785028057563806680083298610134880848883640292531723714467102234556291968179018775780308458024302103848451312741663820589200520720207891653884985710130867134073520525932445557074805974235006810370309087879564826639263972805682465506594098949560288847385983395160311034445386606259,
+ -0.9295691721319395758214901545592256073474270144297154975928116833612430986265594515998834499355844736686512805129688214992047597092114291955925880175797899765980745854426738149516325837607227287481909072315347776012991222301207304052068204069335766550173941103055407746774520789612843561385,
+ 0.9295691721319395758214901545592256073474270144297154975928116833612430986265594515998834499355844736686512805129688214992047597092114291955925880175797899765980745854426738149516325837607227287481909072315347776012991222301207304052068204069335766550173941103055407746774520789612843561385,
+ -0.9464113748584028160624814913472647952793949717952331902317789712973664402149436591260928179188420533516264142755452159723722786167537167514691534968355366202934342465086995943893699962972237343218079763936958985487264411542890941861254842843026890160131607678957282346112697993618567018237,
+ 0.9464113748584028160624814913472647952793949717952331902317789712973664402149436591260928179188420533516264142755452159723722786167537167514691534968355366202934342465086995943893699962972237343218079763936958985487264411542890941861254842843026890160131607678957282346112697993618567018237,
+ -0.9610087996520537189186141218971572067621146110378459494461586158623919945488992563976780806866203786216001498788310714552847469661399216303755820947005848739467276644122915754949838610353627723679982220628115164983443994552616161584523205789167087822341423097206088828267065770404672828066,
+ 0.9610087996520537189186141218971572067621146110378459494461586158623919945488992563976780806866203786216001498788310714552847469661399216303755820947005848739467276644122915754949838610353627723679982220628115164983443994552616161584523205789167087822341423097206088828267065770404672828066,
+ -0.9733268277899109637418535073522726680261452944551741758819139781978152256958453749994966038154125547612207903105020176172420237675899907788807087542221018040460410464083361271842759039530092449625891215101984663282728542290395875313124045226564547294745437773482395329023327909760431499638,
+ 0.9733268277899109637418535073522726680261452944551741758819139781978152256958453749994966038154125547612207903105020176172420237675899907788807087542221018040460410464083361271842759039530092449625891215101984663282728542290395875313124045226564547294745437773482395329023327909760431499638,
+ -0.9833362538846259569312993021568311169452475066237403837464872131233426128415470535606559721330818003585532628124845662897410684694651251174207713020897795837892725294581710205598344576799985346970638130204876060998657059283079767876980544166132523941283823202290746667358872631036031924711,
+ 0.9833362538846259569312993021568311169452475066237403837464872131233426128415470535606559721330818003585532628124845662897410684694651251174207713020897795837892725294581710205598344576799985346970638130204876060998657059283079767876980544166132523941283823202290746667358872631036031924711,
+ -0.9910133714767443207393823834433031136413494453907904852225427459378131658644129997345108950133770434340330151289100150097018332483423277136039914249575686591612502752158650205954671083696496347591169012794322303027309768195334920157669446268175983954105533989275308193580349506657360682085,
+ 0.9910133714767443207393823834433031136413494453907904852225427459378131658644129997345108950133770434340330151289100150097018332483423277136039914249575686591612502752158650205954671083696496347591169012794322303027309768195334920157669446268175983954105533989275308193580349506657360682085,
+ -0.9963401167719552793469245006763991232098575063402266121352522199507030568202208530946066801021703916301511794658310735397567341036554686814952726523955953805437164277655915410358813984246580862850974195805395101678543649116458555272523253307828290553873260588621490898443701779725568118502,
+ 0.9963401167719552793469245006763991232098575063402266121352522199507030568202208530946066801021703916301511794658310735397567341036554686814952726523955953805437164277655915410358813984246580862850974195805395101678543649116458555272523253307828290553873260588621490898443701779725568118502,
+ -0.9993050417357721394569056243456363119697121916756087760628072954617646543505331997843242376462639434945376776512170265314011232493020401570891594274831367800115383317335285468800574240152992751785027563437707875403545865305271045717258142571193695943317890367167086616955235477529427992282,
+ 0.9993050417357721394569056243456363119697121916756087760628072954617646543505331997843242376462639434945376776512170265314011232493020401570891594274831367800115383317335285468800574240152992751785027563437707875403545865305271045717258142571193695943317890367167086616955235477529427992282
+};
+
+static const double C[] = {
+ 0.0486909570091397203833653907347499124426286922838743305086688042456914190998246107310291565645676057401607079939845156005172257043376703767287395573765236401039685866479381075274920900511719320271157129622463682509122641788910270632229694394595885032921037399187298767076084601033342936131,
+ 0.0486909570091397203833653907347499124426286922838743305086688042456914190998246107310291565645676057401607079939845156005172257043376703767287395573765236401039685866479381075274920900511719320271157129622463682509122641788910270632229694394595885032921037399187298767076084601033342936131,
+ 0.0485754674415034269347990667839781136875565447049294857111516761025158193093697039229163427633930410186232149083923688162761488505704450697417589593116703853157329164894580165517236877241308351214870169600093357854651930986960906313726182992933363325614247750209880050786299287510692780499,
+ 0.0485754674415034269347990667839781136875565447049294857111516761025158193093697039229163427633930410186232149083923688162761488505704450697417589593116703853157329164894580165517236877241308351214870169600093357854651930986960906313726182992933363325614247750209880050786299287510692780499,
+ 0.048344762234802957169769527158017809703692550609501080629442201445249828946429202156764153264348308119169811534137999799779908820312744765416129733427088646813066886130539178187597540312913636916139844188190193872629488730769015964208394624398401975043997268903006190530430762197842013971,
+ 0.048344762234802957169769527158017809703692550609501080629442201445249828946429202156764153264348308119169811534137999799779908820312744765416129733427088646813066886130539178187597540312913636916139844188190193872629488730769015964208394624398401975043997268903006190530430762197842013971,
+ 0.0479993885964583077281261798713460699543167134714936209446323930933335214619650277588138568504103427609283146728470455041360837549685364869161566863222680599110109210456299588352028330169041000166382937545505655464884266691630625402297821494221827392164049587946530563778771030124675514431,
+ 0.0479993885964583077281261798713460699543167134714936209446323930933335214619650277588138568504103427609283146728470455041360837549685364869161566863222680599110109210456299588352028330169041000166382937545505655464884266691630625402297821494221827392164049587946530563778771030124675514431,
+ 0.0475401657148303086622822069442231716408252512625387521584740318784735191312349586041971325618543660076682369564304738487584849740943805934034367382833518752314207901993991333786062812015195073547884746598535775062676699885664167707011249029305697669004958515813436770491520105115843742005,
+ 0.0475401657148303086622822069442231716408252512625387521584740318784735191312349586041971325618543660076682369564304738487584849740943805934034367382833518752314207901993991333786062812015195073547884746598535775062676699885664167707011249029305697669004958515813436770491520105115843742005,
+ 0.0469681828162100173253262857545810751998975284738125649829240886861900500181800807437012381630302198876925642461830694029139318555787845567143614289552410495903601238284556145544858090965965782916339169651505119399637862876053945518410353459767034026687936026945199383607112976484520939933,
+ 0.0469681828162100173253262857545810751998975284738125649829240886861900500181800807437012381630302198876925642461830694029139318555787845567143614289552410495903601238284556145544858090965965782916339169651505119399637862876053945518410353459767034026687936026945199383607112976484520939933,
+ 0.0462847965813144172959532492322611849696503075324468007778340818364698861774606986244241539105685321088517142947579291476238551538798963436740600968513359005801910700069462154098456091711311098901749803777735222026075473081311483686560830539773763176758567914860207820170792365910140063798,
+ 0.0462847965813144172959532492322611849696503075324468007778340818364698861774606986244241539105685321088517142947579291476238551538798963436740600968513359005801910700069462154098456091711311098901749803777735222026075473081311483686560830539773763176758567914860207820170792365910140063798,
+ 0.0454916279274181444797709969712690588873234618023998968168834081606504637618082102750954507142497706775055424364453740562113890878382679420378787427100982909191308430750899201141096789461078632697297091763378573830284133736378128577579722120264252594541491899441765769262904055702701625378,
+ 0.0454916279274181444797709969712690588873234618023998968168834081606504637618082102750954507142497706775055424364453740562113890878382679420378787427100982909191308430750899201141096789461078632697297091763378573830284133736378128577579722120264252594541491899441765769262904055702701625378,
+ 0.0445905581637565630601347100309448432940237999912217256432193286861948363377761089569585678875932857237669096941854082976565514031401996407675401022860761183118504326746863327792604337217763335682212515058414863183914930810334329596384915832703655935958010948424747251920190851700662833367,
+ 0.0445905581637565630601347100309448432940237999912217256432193286861948363377761089569585678875932857237669096941854082976565514031401996407675401022860761183118504326746863327792604337217763335682212515058414863183914930810334329596384915832703655935958010948424747251920190851700662833367,
+ 0.0435837245293234533768278609737374809227888974971180150532193925502569499020021803936448815937567079991401855477391110804568848623412043870399620479222000249538880795788245633051476595555730388360811011823841525667998427392843673284072004068821750061964976796287623004834501604656318714989,
+ 0.0435837245293234533768278609737374809227888974971180150532193925502569499020021803936448815937567079991401855477391110804568848623412043870399620479222000249538880795788245633051476595555730388360811011823841525667998427392843673284072004068821750061964976796287623004834501604656318714989,
+ 0.0424735151236535890073397679088173661655466481806496697314607722055245433487169327182398988553670128358787507582463602377168227019625334754497484024668087975720049504975593281010888062806587161032924284354938115463233015024659299046001504100674918329532481611571863222497170398830691222425,
+ 0.0424735151236535890073397679088173661655466481806496697314607722055245433487169327182398988553670128358787507582463602377168227019625334754497484024668087975720049504975593281010888062806587161032924284354938115463233015024659299046001504100674918329532481611571863222497170398830691222425,
+ 0.0412625632426235286101562974736380477399306355305474105429034779122704951178045914463267035032832336161816547420067160277921114474557623647771372636679857599931025531633255548770293397336318597716427093310378312957479805159734598610664983115148350548735211568465338522875618805992499897174,
+ 0.0412625632426235286101562974736380477399306355305474105429034779122704951178045914463267035032832336161816547420067160277921114474557623647771372636679857599931025531633255548770293397336318597716427093310378312957479805159734598610664983115148350548735211568465338522875618805992499897174,
+ 0.0399537411327203413866569261283360739186769506703336301114037026981570543670430333260307390357287606111017588757685176701688554806178713759519003171090525332423003042251947304213502522332118258365256241174986409729902714098049024753746340158430732115642207673265332738358717839602955875715,
+ 0.0399537411327203413866569261283360739186769506703336301114037026981570543670430333260307390357287606111017588757685176701688554806178713759519003171090525332423003042251947304213502522332118258365256241174986409729902714098049024753746340158430732115642207673265332738358717839602955875715,
+ 0.0385501531786156291289624969468089910127871122017180319662378854088005271323682681394418540442928363090545214563022868422017877042243007014244875098498616146404178795110038170109976252865902624380463581094085479557660525450020049773872343621719025128277593787164021147974906095237533202082,
+ 0.0385501531786156291289624969468089910127871122017180319662378854088005271323682681394418540442928363090545214563022868422017877042243007014244875098498616146404178795110038170109976252865902624380463581094085479557660525450020049773872343621719025128277593787164021147974906095237533202082,
+ 0.0370551285402400460404151018095833750834649453056563021747536272028091562122966687178302646649066832960609370472485057031765338738734008482025086366647963664178752038995704175623165041724901843573087856883034472545386037691055680911138721623610172486110313241291773258491882452773847899443,
+ 0.0370551285402400460404151018095833750834649453056563021747536272028091562122966687178302646649066832960609370472485057031765338738734008482025086366647963664178752038995704175623165041724901843573087856883034472545386037691055680911138721623610172486110313241291773258491882452773847899443,
+ 0.0354722132568823838106931467152459479480946310024100946926514848199381113651392962399922996268087884509143420993419937430515415557908457195618550238075571721209638845910166697234073788332647695349442265578792857058786796417110738673392400570019770741873271724201517438135222598792344040215,
+ 0.0354722132568823838106931467152459479480946310024100946926514848199381113651392962399922996268087884509143420993419937430515415557908457195618550238075571721209638845910166697234073788332647695349442265578792857058786796417110738673392400570019770741873271724201517438135222598792344040215,
+ 0.0338051618371416093915654821107254310210499263140045346675500650400323727745785853730452808963944098691936344225349051741060036935288424090581463711756382878498537611980973238606529148664990420534952057130296232922368792280098852092993207644225150541876980292972087619863453425206929192216,
+ 0.0338051618371416093915654821107254310210499263140045346675500650400323727745785853730452808963944098691936344225349051741060036935288424090581463711756382878498537611980973238606529148664990420534952057130296232922368792280098852092993207644225150541876980292972087619863453425206929192216,
+ 0.032057928354851553585467504347898716966221573881398062250169407854535275399124366530227987935629046729162364779969274126431870966979526186907589490002269660893281421728773647001279141626157958271220102615163092206489916992120482595587916535390136003611498634162765724522022671474313619317,
+ 0.032057928354851553585467504347898716966221573881398062250169407854535275399124366530227987935629046729162364779969274126431870966979526186907589490002269660893281421728773647001279141626157958271220102615163092206489916992120482595587916535390136003611498634162765724522022671474313619317,
+ 0.030234657072402478867974059819548659158281397768481241636026542045969161851838118212761980885178641520596873511042783163461341979185470882574743804555268086640389062237383427702813367624714014426121485626242067362445894463989335423458464954799181190120473168677930333898873084606011285311,
+ 0.030234657072402478867974059819548659158281397768481241636026542045969161851838118212761980885178641520596873511042783163461341979185470882574743804555268086640389062237383427702813367624714014426121485626242067362445894463989335423458464954799181190120473168677930333898873084606011285311,
+ 0.0283396726142594832275113052002373519812075841257543359907955185084500175712880712901834579816476269393013386531176072296695948860841466158639973753393323262188023471133258509422081952937349849822864752636994881600343083839805990853930436233762729622213044478376753949590318846038229829528,
+ 0.0283396726142594832275113052002373519812075841257543359907955185084500175712880712901834579816476269393013386531176072296695948860841466158639973753393323262188023471133258509422081952937349849822864752636994881600343083839805990853930436233762729622213044478376753949590318846038229829528,
+ 0.0263774697150546586716917926252251856755993308422457184496156736853021592428967790284780487213653480867620409279447766944383920384284787790772384251090745670478105870527396429136326932261251511732466974897397268573168068852344129736214469830280087710575094607457344820944885011053938108899,
+ 0.0263774697150546586716917926252251856755993308422457184496156736853021592428967790284780487213653480867620409279447766944383920384284787790772384251090745670478105870527396429136326932261251511732466974897397268573168068852344129736214469830280087710575094607457344820944885011053938108899,
+ 0.0243527025687108733381775504090689876499784155133784119819985685535536787083770723737264828464464223276155821319330210193549896426801083040150047332857692873011433649334477370145389017577189505240415125600908800786897201425473757275187332157593198572919772969833130729981971352463730545469,
+ 0.0243527025687108733381775504090689876499784155133784119819985685535536787083770723737264828464464223276155821319330210193549896426801083040150047332857692873011433649334477370145389017577189505240415125600908800786897201425473757275187332157593198572919772969833130729981971352463730545469,
+ 0.0222701738083832541592983303841550024229592905997594051455205429744914460867081990116647982811451138592401156680063927909718825845915896692701716212710541472344073624315399429951255221519263275095347974129106415903376085208797420439500915674568159744176912567285070988940509294826076696882,
+ 0.0222701738083832541592983303841550024229592905997594051455205429744914460867081990116647982811451138592401156680063927909718825845915896692701716212710541472344073624315399429951255221519263275095347974129106415903376085208797420439500915674568159744176912567285070988940509294826076696882,
+ 0.0201348231535302093723403167285438970895266801007919519220072343276769828211923597982299498416998597995443052252531684909219367615574440281549241161294448697202959593344989612626641188010558013085389491205901106884167596038790695150496733123662891637942237462337673353651179115491957031948,
+ 0.0201348231535302093723403167285438970895266801007919519220072343276769828211923597982299498416998597995443052252531684909219367615574440281549241161294448697202959593344989612626641188010558013085389491205901106884167596038790695150496733123662891637942237462337673353651179115491957031948,
+ 0.0179517157756973430850453020011193688971673570364158572977184273569247295870620984743089140579199272107974903016785911970727080884655646148340637373001805876560334052431930062983734905886704331100259778249929425439377011315288821865303197904492848823994202996722656114004109123107733596987,
+ 0.0179517157756973430850453020011193688971673570364158572977184273569247295870620984743089140579199272107974903016785911970727080884655646148340637373001805876560334052431930062983734905886704331100259778249929425439377011315288821865303197904492848823994202996722656114004109123107733596987,
+ 0.0157260304760247193219659952975397944260290098431565121528943932284210502164124556525745628476326997189475680077625258949765335021586482683126547283634704087193102431454662772463321304938516661086261262080252305539171654570677889578063634007609097342035360186636479612243231917699790225637,
+ 0.0157260304760247193219659952975397944260290098431565121528943932284210502164124556525745628476326997189475680077625258949765335021586482683126547283634704087193102431454662772463321304938516661086261262080252305539171654570677889578063634007609097342035360186636479612243231917699790225637,
+ 0.0134630478967186425980607666859556841084257719773496708184682785221983598894666268489697837056105038485845901773961664652581563686185523959473293683490869846700009741156668864960127745507806046701586435579547632680339906665338521813319281296935586498194608460412423723103161161922347608637,
+ 0.0134630478967186425980607666859556841084257719773496708184682785221983598894666268489697837056105038485845901773961664652581563686185523959473293683490869846700009741156668864960127745507806046701586435579547632680339906665338521813319281296935586498194608460412423723103161161922347608637,
+ 0.011168139460131128818590493019208135072778797816827287215251362273969701224836131369547661822970774719521543690039908073147476182135228738610704246958518755712518444434075738269866120460156365855324768445411463643114925829148750923090201475035559533993035986264487097245733097728698218563,
+ 0.011168139460131128818590493019208135072778797816827287215251362273969701224836131369547661822970774719521543690039908073147476182135228738610704246958518755712518444434075738269866120460156365855324768445411463643114925829148750923090201475035559533993035986264487097245733097728698218563,
+ 0.0088467598263639477230309146597306476951762660792204997984715769296110380005985367341694286322550520156167431790573509593010611842062630262878798782558974712042810219159674181580449655112696028911646066461502678711637780164986283350190669684468398617127841853445303466680698660632269500149,
+ 0.0088467598263639477230309146597306476951762660792204997984715769296110380005985367341694286322550520156167431790573509593010611842062630262878798782558974712042810219159674181580449655112696028911646066461502678711637780164986283350190669684468398617127841853445303466680698660632269500149,
+ 0.0065044579689783628561173603999812667711317610549523400952448792575685125717613068203530526491113296049409911387320826711045787146267036866881961532403342811327869183281273743976710008917886491097375367147212074243884772614562628844975421736416404173672075979097191581386023407454532945934,
+ 0.0065044579689783628561173603999812667711317610549523400952448792575685125717613068203530526491113296049409911387320826711045787146267036866881961532403342811327869183281273743976710008917886491097375367147212074243884772614562628844975421736416404173672075979097191581386023407454532945934,
+ 0.0041470332605624676352875357285514153133028192536848024628763661431834776690157393776820933106187137592011723199002845429836606307797425496666456172753165824787973801175029578301513761259541022471768825518482406145696380621686627285992715643614469568410535180218496973657001203470470418364,
+ 0.0041470332605624676352875357285514153133028192536848024628763661431834776690157393776820933106187137592011723199002845429836606307797425496666456172753165824787973801175029578301513761259541022471768825518482406145696380621686627285992715643614469568410535180218496973657001203470470418364,
+ 0.0017832807216964329472960791449719331799593472719279556695308063655858546954239803486698215802150348282744786016134857283616955449868451969230490863774274598030023211055562492709717566919237924255297982774711177411074145151155610163293142044147991553384925940046957893721166251082473659733,
+ 0.0017832807216964329472960791449719331799593472719279556695308063655858546954239803486698215802150348282744786016134857283616955449868451969230490863774274598030023211055562492709717566919237924255297982774711177411074145151155610163293142044147991553384925940046957893721166251082473659733
+};
+#else
+/* n = 32 */
+
+static double T[] = {
+ -0.0483076656877383162348125704405021636908472517308488971677937345463685926042778777794060365911173780988289503411375793689757446357461295741679964108035347980667582792392651327368009453047606446744575790523465655622949909588624860214137051585425884056992683442137333250625173849291299678673,
+ 0.0483076656877383162348125704405021636908472517308488971677937345463685926042778777794060365911173780988289503411375793689757446357461295741679964108035347980667582792392651327368009453047606446744575790523465655622949909588624860214137051585425884056992683442137333250625173849291299678673,
+ -0.1444719615827964934851863735988106522038459913156355521379528938242184438164519731102406769974924713989580220758441301598578946580142268413547299935841673092513202403499286272686350814272974392746706128556678811982653393383080797337231702069432462445053984587997153683967433095128570624414,
+ 0.1444719615827964934851863735988106522038459913156355521379528938242184438164519731102406769974924713989580220758441301598578946580142268413547299935841673092513202403499286272686350814272974392746706128556678811982653393383080797337231702069432462445053984587997153683967433095128570624414,
+ -0.2392873622521370745446032091655015206088554219602530155470960995597029133039943915553593695844147813728958071901224632260145752503694970545640339873418480550362677768010887468668377893757173424222709744116861683634989914911762187599464033126988486345234374380695224452457957624756811128321,
+ 0.2392873622521370745446032091655015206088554219602530155470960995597029133039943915553593695844147813728958071901224632260145752503694970545640339873418480550362677768010887468668377893757173424222709744116861683634989914911762187599464033126988486345234374380695224452457957624756811128321,
+ -0.3318686022821276497799168057301879961957751368050598360182296306285376829657438169809731852312743263005943551508559377834274303920771100489026913715847854727626540340157368609696698131829681988642689780208633461925468064919389286805624602715005948661328152252049795463242055567997437182143,
+ 0.3318686022821276497799168057301879961957751368050598360182296306285376829657438169809731852312743263005943551508559377834274303920771100489026913715847854727626540340157368609696698131829681988642689780208633461925468064919389286805624602715005948661328152252049795463242055567997437182143,
+ -0.4213512761306353453641194361724264783358772886324433305416613404557190462549837315607633055675740638739884093394574651160978879545562247406839036854173715776910866941643197988581928900702286425821151586000969947406313405310082646561917980302543820974679501841964453794193724645925031841919,
+ 0.4213512761306353453641194361724264783358772886324433305416613404557190462549837315607633055675740638739884093394574651160978879545562247406839036854173715776910866941643197988581928900702286425821151586000969947406313405310082646561917980302543820974679501841964453794193724645925031841919,
+ -0.5068999089322293900237474743778212301802836995994354639743662809707712640478764442266190213124522047999876916596854537447047905434649918210338296049592120273725464263651562560829050004258268002241145951271730860506703690843719936432852920782304931272053564539127514959875734718036950073563,
+ 0.5068999089322293900237474743778212301802836995994354639743662809707712640478764442266190213124522047999876916596854537447047905434649918210338296049592120273725464263651562560829050004258268002241145951271730860506703690843719936432852920782304931272053564539127514959875734718036950073563,
+ -0.5877157572407623290407454764018268584509401154544205727031788473129228586684474311408145102018661764979429510790747919023774933113319119601088669936958908618326367715806216053155906936017362413244183150445492317940727345571648726363597097311647731726438279098059670236086983675374932643925,
+ 0.5877157572407623290407454764018268584509401154544205727031788473129228586684474311408145102018661764979429510790747919023774933113319119601088669936958908618326367715806216053155906936017362413244183150445492317940727345571648726363597097311647731726438279098059670236086983675374932643925,
+ -0.6630442669302152009751151686632383689770222859605053010170834964924461749232229404368981536611965356686820332804126742949900731319113817214392193185613161549689934301410316417342588149871686184296988807305719690974644891055567340650986465615021143958920599684258616066247948224049997371166,
+ 0.6630442669302152009751151686632383689770222859605053010170834964924461749232229404368981536611965356686820332804126742949900731319113817214392193185613161549689934301410316417342588149871686184296988807305719690974644891055567340650986465615021143958920599684258616066247948224049997371166,
+ -0.732182118740289680387426665091267146630270483506629100821139573270385253587797727611292298988652560055905228466313310601075333829094630570926240639601009902567982815376254840388565733846030450161774620971196087756484387383432502715118096615117242484073636640563609696801484680439912327302,
+ 0.732182118740289680387426665091267146630270483506629100821139573270385253587797727611292298988652560055905228466313310601075333829094630570926240639601009902567982815376254840388565733846030450161774620971196087756484387383432502715118096615117242484073636640563609696801484680439912327302,
+ -0.7944837959679424069630972989704289020954794016388354532507582449720593922816426654241878967890821228397041480126630294067578180914548706957761322921470535094589673860419616615738928385807346185892317514562489971543238450942224396667500582904031225063621511429185567036727089257387570529468,
+ 0.7944837959679424069630972989704289020954794016388354532507582449720593922816426654241878967890821228397041480126630294067578180914548706957761322921470535094589673860419616615738928385807346185892317514562489971543238450942224396667500582904031225063621511429185567036727089257387570529468,
+ -0.849367613732569970133693004967742538954886793049759233100219598613724656141562558741881463752754991143937635778596582088915769685796612254240615386941355933272723068952531445772190363422003834495043219316062885999846179078139659341918527603834809670576387535564876596379488780285979062125,
+ 0.849367613732569970133693004967742538954886793049759233100219598613724656141562558741881463752754991143937635778596582088915769685796612254240615386941355933272723068952531445772190363422003834495043219316062885999846179078139659341918527603834809670576387535564876596379488780285979062125,
+ -0.8963211557660521239653072437192122684789964967957595765636154129650249794910409173494503783167666654202705333374285522819507600044591355080910768854012859468015827508424619812224062460791781333400979810176198916239783226706506012473250929962326307746466256167673927887144428859779028909399,
+ 0.8963211557660521239653072437192122684789964967957595765636154129650249794910409173494503783167666654202705333374285522819507600044591355080910768854012859468015827508424619812224062460791781333400979810176198916239783226706506012473250929962326307746466256167673927887144428859779028909399,
+ -0.9349060759377396891709191348354093255286714322828372184584037398118161947182932855418880831417927728359606280450921427988850058691931014887248988124656348299653052688344696135840215712191162135178273756415771123010111796122671724143565383396162107206772781551029308751511942924942333859805,
+ 0.9349060759377396891709191348354093255286714322828372184584037398118161947182932855418880831417927728359606280450921427988850058691931014887248988124656348299653052688344696135840215712191162135178273756415771123010111796122671724143565383396162107206772781551029308751511942924942333859805,
+ -0.9647622555875064307738119281182749603888952204430187193220113218370995254867038008243801877562227002840740910741483519987441236283464394249183812395373150090695515823078220949436846111682404866338388944248976976566275875721000356873959697266702651250019105084704924793016185368873243713355,
+ 0.9647622555875064307738119281182749603888952204430187193220113218370995254867038008243801877562227002840740910741483519987441236283464394249183812395373150090695515823078220949436846111682404866338388944248976976566275875721000356873959697266702651250019105084704924793016185368873243713355,
+ -0.9856115115452683354001750446309019786323957143358063182107821705820305847193755946663846485510970266115353839862364606643634021712823093784875255943834038377710426488328772047833289470320023596895438028281274741367781028592272459887917924171204666683239464005128153533797603112851826904814,
+ 0.9856115115452683354001750446309019786323957143358063182107821705820305847193755946663846485510970266115353839862364606643634021712823093784875255943834038377710426488328772047833289470320023596895438028281274741367781028592272459887917924171204666683239464005128153533797603112851826904814,
+ -0.9972638618494815635449811286650407271385376637294611593011185457862359083917418520130456693085426416474280482200936551645510686196373231416035137741332968299789863385253514914078766236061488136738023162574655835389902337937054326098485227311719825229066712510246574949376367552421728646398,
+ 0.9972638618494815635449811286650407271385376637294611593011185457862359083917418520130456693085426416474280482200936551645510686196373231416035137741332968299789863385253514914078766236061488136738023162574655835389902337937054326098485227311719825229066712510246574949376367552421728646398
+};
+
+static double C[] = {
+ 0.0965400885147278005667648300635757947368606312355700687323182099577497758679466512968173871061464644599963197828969869820251559172455698832434930732077927850876632725829187045819145660710266452161095406358159608874152584850413283587913891015545638518881205600825069096855488296437485836866,
+ 0.0965400885147278005667648300635757947368606312355700687323182099577497758679466512968173871061464644599963197828969869820251559172455698832434930732077927850876632725829187045819145660710266452161095406358159608874152584850413283587913891015545638518881205600825069096855488296437485836866,
+ 0.0956387200792748594190820022041311005948905081620055509529898509437067444366006256133614167190847508238474888230077112990752876436158047205555474265705582078453283640212465537132165041268773645168746774530146140911679782502276289938840330631903789120176765314495900053061764438990021439069,
+ 0.0956387200792748594190820022041311005948905081620055509529898509437067444366006256133614167190847508238474888230077112990752876436158047205555474265705582078453283640212465537132165041268773645168746774530146140911679782502276289938840330631903789120176765314495900053061764438990021439069,
+ 0.0938443990808045656391802376681172600361000757462364500506275696355695118623098075097804207682530277555307864917078828352419853248607668520631751470962234105835015158485760721979732297206950719908744248285672032436598213262204039212897239890934116841559005147755270269705682414708355646603,
+ 0.0938443990808045656391802376681172600361000757462364500506275696355695118623098075097804207682530277555307864917078828352419853248607668520631751470962234105835015158485760721979732297206950719908744248285672032436598213262204039212897239890934116841559005147755270269705682414708355646603,
+ 0.0911738786957638847128685771116370625448614132753900053231278739777031520613017513597426417145878622654027367650308019870251963114683369110451524174258161390823876554910693202594383388549640738095422966058367070348943662290656339592299608558384147559830707904449930677260444604329157917977,
+ 0.0911738786957638847128685771116370625448614132753900053231278739777031520613017513597426417145878622654027367650308019870251963114683369110451524174258161390823876554910693202594383388549640738095422966058367070348943662290656339592299608558384147559830707904449930677260444604329157917977,
+ 0.0876520930044038111427714627518022875484497217017572223192228034747061150211380239263021665771581379364685191248848158059408000065275041643745927401342920150588893827207354226012701872322225514682178439577327346929209121046816487338309068375228210705166692551938339727096609740531893725675,
+ 0.0876520930044038111427714627518022875484497217017572223192228034747061150211380239263021665771581379364685191248848158059408000065275041643745927401342920150588893827207354226012701872322225514682178439577327346929209121046816487338309068375228210705166692551938339727096609740531893725675,
+ 0.0833119242269467552221990746043486115387468839428344598401864047287594069244380966536255650452315042012372905572506028852130723585016898197140339352228963465326746426938359210160503509807644396182380868089959855742801355208471205261406307895519604387550841954817025499019984032594036141439,
+ 0.0833119242269467552221990746043486115387468839428344598401864047287594069244380966536255650452315042012372905572506028852130723585016898197140339352228963465326746426938359210160503509807644396182380868089959855742801355208471205261406307895519604387550841954817025499019984032594036141439,
+ 0.078193895787070306471740918828306671039786798482159190307481553869493700115196435401943819761440851294456424770323467367505109006517482028994114252939401250416132320553639542341400437522236191275346323130525969269563653003188829786549728825182082678498917784036375053244425839341945385297,
+ 0.078193895787070306471740918828306671039786798482159190307481553869493700115196435401943819761440851294456424770323467367505109006517482028994114252939401250416132320553639542341400437522236191275346323130525969269563653003188829786549728825182082678498917784036375053244425839341945385297,
+ 0.0723457941088485062253993564784877916043369833018248707397632823511765345816800402874475958591657429073027694582930574378890633404841054620298756279975430795706338162404545590689277985270140590721779502609564199074051863640176937117952488466002340085264819537808079947788437998042296495822,
+ 0.0723457941088485062253993564784877916043369833018248707397632823511765345816800402874475958591657429073027694582930574378890633404841054620298756279975430795706338162404545590689277985270140590721779502609564199074051863640176937117952488466002340085264819537808079947788437998042296495822,
+ 0.0658222227763618468376500637069387728775364473732465153710916696852412442018627316280044447764609054151761388378861151807154113495715653711918644796313239555117970398473141615070299152284100887258072240524028885129828725430021172354299810423059697133688823072212214503334259555369485963074,
+ 0.0658222227763618468376500637069387728775364473732465153710916696852412442018627316280044447764609054151761388378861151807154113495715653711918644796313239555117970398473141615070299152284100887258072240524028885129828725430021172354299810423059697133688823072212214503334259555369485963074,
+ 0.0586840934785355471452836373001708867501204674575467587150032786132877518019090643743123653437052116901895704813134467814193905269714480573030647540887991405215103758723074481312705449946311993670933802369300463315125015975216910705047901943865293781921122370996257470349807212516159332678,
+ 0.0586840934785355471452836373001708867501204674575467587150032786132877518019090643743123653437052116901895704813134467814193905269714480573030647540887991405215103758723074481312705449946311993670933802369300463315125015975216910705047901943865293781921122370996257470349807212516159332678,
+ 0.0509980592623761761961632446895216952601847767397628437069071236525030510385137821267442193868358292147899714519363571211100873456269865150186456681043804358654826791768545393024953758025593924464295555854744882720755747096079325496814455853004350452095212995888025282619932613606999567133,
+ 0.0509980592623761761961632446895216952601847767397628437069071236525030510385137821267442193868358292147899714519363571211100873456269865150186456681043804358654826791768545393024953758025593924464295555854744882720755747096079325496814455853004350452095212995888025282619932613606999567133,
+ 0.0428358980222266806568786466061255284928108575989407395620219408911043916962572261359138025961596979511472539467367407419206021900868371610612953162236233351132214438513203223655531564777278515080476421262443325932320214191168239648611793958596884827086182431203349730049744697408543115307,
+ 0.0428358980222266806568786466061255284928108575989407395620219408911043916962572261359138025961596979511472539467367407419206021900868371610612953162236233351132214438513203223655531564777278515080476421262443325932320214191168239648611793958596884827086182431203349730049744697408543115307,
+ 0.0342738629130214331026877322523727069948402029116274337814057454192310522168984446294442724624445760666244242305266023810860790282088335398182296698622433517061843276344829146573593201201081743714879684153735672789104567624853712011151505225193933019375481618760594889854480408562043658635,
+ 0.0342738629130214331026877322523727069948402029116274337814057454192310522168984446294442724624445760666244242305266023810860790282088335398182296698622433517061843276344829146573593201201081743714879684153735672789104567624853712011151505225193933019375481618760594889854480408562043658635,
+ 0.0253920653092620594557525897892240292875540475469487209362512822192154788532376645960457016338988332029324531233401833547954942765653767672102838323550828207273795044402516181251040411735351747299230615776597356956641506445501689924551185923348003766988424170511157069264716719906995309826,
+ 0.0253920653092620594557525897892240292875540475469487209362512822192154788532376645960457016338988332029324531233401833547954942765653767672102838323550828207273795044402516181251040411735351747299230615776597356956641506445501689924551185923348003766988424170511157069264716719906995309826,
+ 0.0162743947309056706051705622063866181795429637952095664295931749613369651752917857651844425586692833071042366002861684552859449530958901379260437604156888337987656773068694383447504913457771896770689760342192010638946676879735404121702279005140285599424477022083127753774756520463311689155,
+ 0.0162743947309056706051705622063866181795429637952095664295931749613369651752917857651844425586692833071042366002861684552859449530958901379260437604156888337987656773068694383447504913457771896770689760342192010638946676879735404121702279005140285599424477022083127753774756520463311689155,
+ 0.0070186100094700966004070637388531825133772207289396032320082356192151241454178686953297376907573215077936155545790593837513204206518026084505878987243348925784479817181234617862457418214505322067610482902501455504204433524520665822704844582452877416001060465891907497519632353148380799619,
+ 0.0070186100094700966004070637388531825133772207289396032320082356192151241454178686953297376907573215077936155545790593837513204206518026084505878987243348925784479817181234617862457418214505322067610482902501455504204433524520665822704844582452877416001060465891907497519632353148380799619
+};
+
+#endif
diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c
index d37bb5bc71..ac1cb3320d 100644
--- a/gsk/gskcurve.c
+++ b/gsk/gskcurve.c
@@ -82,6 +82,8 @@ struct _GskCurveClass
graphene_point_t *value);
int (* get_crossing) (const GskCurve *curve,
const graphene_point_t *point);
+ float (* get_length_to) (const GskCurve *curve,
+ float t);
};
/* {{{ Utilities */
@@ -182,6 +184,30 @@ gsk_curve_elevate (const GskCurve *curve,
g_assert_not_reached ();
}
+/* Compute arclength by using Gauss quadrature on
+ *
+ * \int_0^z \sqrt{ (dx/dt)^2 + (dy/dt)^2 } dt
+ */
+
+#include "gskcurve-ct-values.c"
+
+static float
+get_length_by_approximation (const GskCurve *curve,
+ float t)
+{
+ double z = t / 2;
+ double sum = 0;
+ graphene_point_t d;
+
+ for (unsigned int i = 0; i < G_N_ELEMENTS (T); i++)
+ {
+ gsk_curve_get_derivative_at (curve, z * T[i] + z, &d);
+ sum += C[i] * sqrt (d.x * d.x + d.y * d.y);
+ }
+
+ return z * sum;
+}
+
/* }}} */
/* {{{ Line */
@@ -325,12 +351,13 @@ gsk_line_curve_segment (const GskCurve *curve,
GskCurve *segment)
{
const GskLineCurve *self = &curve->line;
- graphene_point_t start_point, end_point;
+ const graphene_point_t *pts = self->points;
+ graphene_point_t p0, p1;
- graphene_point_interpolate (&self->points[0], &self->points[1], start, &start_point);
- graphene_point_interpolate (&self->points[0], &self->points[1], end, &end_point);
+ graphene_point_interpolate (&pts[0], &pts[1], start, &p0);
+ graphene_point_interpolate (&pts[0], &pts[1], end, &p1);
- gsk_line_curve_init_from_points (&segment->line, GSK_PATH_LINE, &start_point, &end_point);
+ gsk_line_curve_init_from_points (&segment->line, GSK_PATH_LINE, &p0, &p1);
}
static gboolean
@@ -340,8 +367,9 @@ gsk_line_curve_decompose (const GskCurve *curve,
gpointer user_data)
{
const GskLineCurve *self = &curve->line;
+ const graphene_point_t *pts = self->points;
- return add_line_func (&self->points[0], &self->points[1], 0.0f, 1.0f, GSK_CURVE_LINE_REASON_STRAIGHT, user_data);
+ return add_line_func (&pts[0], &pts[1], 0.f, 1.f, GSK_CURVE_LINE_REASON_STRAIGHT, user_data);
}
static gboolean
@@ -372,16 +400,30 @@ gsk_line_curve_get_derivative_at (const GskCurve *curve,
graphene_point_t *value)
{
const GskLineCurve *self = &curve->line;
+ const graphene_point_t *pts = self->points;
- value->x = self->points[1].x - self->points[0].x;
- value->y = self->points[1].y - self->points[0].y;
+ value->x = pts[1].x - pts[0].x;
+ value->y = pts[1].y - pts[0].y;
}
static int
gsk_line_curve_get_crossing (const GskCurve *curve,
const graphene_point_t *point)
{
- return line_get_crossing (point, gsk_curve_get_start_point (curve), gsk_curve_get_end_point (curve));
+ const GskLineCurve *self = &curve->line;
+ const graphene_point_t *pts = self->points;
+
+ return line_get_crossing (point, &pts[0], &pts[1]);
+}
+
+static float
+gsk_line_curve_get_length_to (const GskCurve *curve,
+ float t)
+{
+ const GskLineCurve *self = &curve->line;
+ const graphene_point_t *pts = self->points;
+
+ return t * graphene_point_distance (&pts[0], &pts[1], NULL, NULL);
}
static const GskCurveClass GSK_LINE_CURVE_CLASS = {
@@ -405,6 +447,7 @@ static const GskCurveClass GSK_LINE_CURVE_CLASS = {
gsk_line_curve_get_bounds,
gsk_line_curve_get_derivative_at,
gsk_line_curve_get_crossing,
+ gsk_line_curve_get_length_to,
};
/* }}} */
@@ -778,6 +821,13 @@ gsk_quad_curve_get_crossing (const GskCurve *curve,
return get_crossing_by_bisection (curve, point);
}
+static float
+gsk_quad_curve_get_length_to (const GskCurve *curve,
+ float t)
+{
+ return get_length_by_approximation (curve, t);
+}
+
static const GskCurveClass GSK_QUAD_CURVE_CLASS = {
gsk_quad_curve_init,
gsk_quad_curve_init_foreach,
@@ -799,9 +849,10 @@ static const GskCurveClass GSK_QUAD_CURVE_CLASS = {
gsk_quad_curve_get_tight_bounds,
gsk_quad_curve_get_derivative_at,
gsk_quad_curve_get_crossing,
+ gsk_quad_curve_get_length_to,
};
-/* }}} */
+/* }}} */
/* {{{ Cubic */
static void
@@ -1240,6 +1291,13 @@ gsk_cubic_curve_get_crossing (const GskCurve *curve,
return get_crossing_by_bisection (curve, point);
}
+static float
+gsk_cubic_curve_get_length_to (const GskCurve *curve,
+ float t)
+{
+ return get_length_by_approximation (curve, t);
+}
+
static const GskCurveClass GSK_CUBIC_CURVE_CLASS = {
gsk_cubic_curve_init,
gsk_cubic_curve_init_foreach,
@@ -1261,9 +1319,10 @@ static const GskCurveClass GSK_CUBIC_CURVE_CLASS = {
gsk_cubic_curve_get_tight_bounds,
gsk_cubic_curve_get_derivative_at,
gsk_cubic_curve_get_crossing,
+ gsk_cubic_curve_get_length_to,
};
- /* }}} */
+ /* }}} */
/* {{{ Conic */
static inline float
@@ -1926,6 +1985,13 @@ gsk_conic_curve_get_crossing (const GskCurve *curve,
return get_crossing_by_bisection (curve, point);
}
+static float
+gsk_conic_curve_get_length_to (const GskCurve *curve,
+ float t)
+{
+ return get_length_by_approximation (curve, t);
+}
+
static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
gsk_conic_curve_init,
gsk_conic_curve_init_foreach,
@@ -1947,9 +2013,10 @@ static const GskCurveClass GSK_CONIC_CURVE_CLASS = {
gsk_conic_curve_get_tight_bounds,
gsk_conic_curve_get_derivative_at,
gsk_conic_curve_get_crossing,
+ gsk_conic_curve_get_length_to,
};
-/* }}} */
+/* }}} */
/* {{{ API */
static const GskCurveClass *
@@ -2275,6 +2342,161 @@ gsk_curve_get_closest_point (const GskCurve *curve,
return find_closest_point (curve, point, threshold, 0, 1, out_dist, out_t);
}
+float
+gsk_curve_get_length_to (const GskCurve *curve,
+ float t)
+{
+ return get_class (curve->op)->get_length_to (curve, t);
+}
+
+float
+gsk_curve_get_length (const GskCurve *curve)
+{
+ return gsk_curve_get_length_to (curve, 1);
+}
+
+/* Compute the inverse of the arclength using bisection,
+ * to a given precision
+ */
+float
+gsk_curve_at_length (const GskCurve *curve,
+ float length,
+ float epsilon)
+{
+ float t1, t2, t, l;
+ GskCurve c1;
+
+ g_assert (epsilon >= FLT_EPSILON);
+
+ t1 = 0;
+ t2 = 1;
+
+ while (t1 < t2)
+ {
+ t = (t1 + t2) / 2;
+ if (t == t1 || t == t2)
+ break;
+
+ gsk_curve_split (curve, t, &c1, NULL);
+
+ l = gsk_curve_get_length (&c1);
+ if (fabsf (length - l) < epsilon)
+ break;
+ else if (l < length)
+ t1 = t;
+ else
+ t2 = t;
+ }
+
+ return t;
+}
+
+static inline void
+_sincosf (float angle,
+ float *out_s,
+ float *out_c)
+{
+#ifdef HAVE_SINCOSF
+ sincosf (angle, out_s, out_c);
+#else
+ *out_s = sinf (angle);
+ *out_c = cosf (angle);
+#endif
+}
+
+static void
+align_points (const graphene_point_t *p,
+ const graphene_point_t *a,
+ const graphene_point_t *b,
+ graphene_point_t *q,
+ int n)
+{
+ graphene_vec2_t n1;
+ float angle;
+ float s, c;
+
+ get_tangent (a, b, &n1);
+ angle = - atan2f (graphene_vec2_get_y (&n1), graphene_vec2_get_x (&n1));
+ _sincosf (angle, &s, &c);
+
+ for (int i = 0; i < n; i++)
+ {
+ q[i].x = (p[i].x - a->x) * c - (p[i].y - a->y) * s;
+ q[i].y = (p[i].x - a->x) * s + (p[i].y - a->y) * c;
+ }
+}
+
+static int
+filter_allowable (float t[3],
+ int n)
+{
+ float g[3];
+ int j = 0;
+
+ for (int i = 0; i < n; i++)
+ if (0 < t[i] && t[i] < 1)
+ g[j++] = t[i];
+ for (int i = 0; i < j; i++)
+ t[i] = g[i];
+ return j;
+}
+
+/* find solutions for at^2 + bt + c = 0 */
+static int
+solve_quadratic (float a, float b, float c, float t[2])
+{
+ float d;
+ int n = 0;
+
+ if (fabsf (a) > 0.0001)
+ {
+ if (b*b > 4*a*c)
+ {
+ d = sqrtf (b*b - 4*a*c);
+ t[n++] = (-b + d)/(2*a);
+ t[n++] = (-b - d)/(2*a);
+ }
+ else
+ {
+ t[n++] = -b / (2*a);
+ }
+ }
+ else if (fabsf (b) > 0.0001)
+ {
+ t[n++] = -c / b;
+ }
+
+ return n;
+}
+
+int
+gsk_curve_get_curvature_points (const GskCurve *curve,
+ float t[3])
+{
+ const graphene_point_t *pts = curve->cubic.points;
+ graphene_point_t p[4];
+ float a, b, c, d;
+ float x, y, z;
+ int n;
+
+ if (curve->op != GSK_PATH_CUBIC)
+ return 0; /* FIXME */
+
+ align_points (pts, &pts[0], &pts[3], p, 4);
+
+ a = p[2].x * p[1].y;
+ b = p[3].x * p[1].y;
+ c = p[1].x * p[2].y;
+ d = p[3].x * p[2].y;
+
+ x = - 3*a + 2*b + 3*c - d;
+ y = 3*a - b - 3*c;
+ z = c - a;
+
+ n = solve_quadratic (x, y, z, t);
+ return filter_allowable (t, n);
+}
+
/* }}} */
/* vim:set foldmethod=marker expandtab: */
diff --git a/gsk/gskcurveprivate.h b/gsk/gskcurveprivate.h
index 6655ec7864..c03d6c9720 100644
--- a/gsk/gskcurveprivate.h
+++ b/gsk/gskcurveprivate.h
@@ -174,6 +174,16 @@ gboolean gsk_curve_get_closest_point (const GskCurve
float threshold,
float *out_dist,
float *out_t);
+float gsk_curve_get_length (const GskCurve *curve);
+float gsk_curve_get_length_to (const GskCurve *curve,
+ float t);
+float gsk_curve_at_length (const GskCurve *curve,
+ float distance,
+ float epsilon);
+
+int gsk_curve_get_curvature_points (const GskCurve * curve,
+ float t[3]);
+
G_END_DECLS
diff --git a/gsk/gskpathmeasure.c b/gsk/gskpathmeasure.c
new file mode 100644
index 0000000000..2242adf0e3
--- /dev/null
+++ b/gsk/gskpathmeasure.c
@@ -0,0 +1,327 @@
+/*
+ * 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 .
+ *
+ * Authors: Benjamin Otte
+ */
+
+#include "config.h"
+
+#include "gskpathmeasure.h"
+
+#include "gskpathbuilder.h"
+#include "gskpathpointprivate.h"
+#include "gskpathprivate.h"
+
+/**
+ * GskPathMeasure:
+ *
+ * `GskPathMeasure` is an object that allows measurements
+ * on `GskPath`s such as determining the length of the path.
+ *
+ * Many measuring operations require sampling the path length
+ * at intermediate points. Therefore, a `GskPathMeasure` has
+ * a tolerance that determines what precision 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
+ *
+ * Since: 4.14
+ */
+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
+ *
+ * Since: 4.14
+ */
+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`.
+ *
+ * Since: 4.14
+ */
+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.
+ *
+ * Since: 4.14
+ */
+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
+ *
+ * Since: 4.14
+ */
+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
+ *
+ * Since: 4.14
+ */
+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
+ *
+ * Since: 4.14
+ */
+float
+gsk_path_measure_get_length (GskPathMeasure *self)
+{
+ g_return_val_if_fail (self != NULL, 0);
+
+ return self->length;
+}
+
+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_get_point:
+ * @self: a `GskPathMeasure`
+ * @distance: the distance
+ * @result: (out caller-allocates): return location for the result
+ *
+ * Sets @result to the point at the given distance into the path.
+ *
+ * An empty path has no points, so `FALSE` is returned in that case.
+ *
+ * Returns: `TRUE` if @result was set
+ *
+ * Since: 4.14
+ */
+gboolean
+gsk_path_measure_get_point (GskPathMeasure *self,
+ float distance,
+ GskPathPoint *result)
+{
+ GskRealPathPoint *res = (GskRealPathPoint *) result;
+ gsize i;
+ const GskContour *contour;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (result != NULL, FALSE);
+
+ if (self->n_contours == 0)
+ return FALSE;
+
+ distance = gsk_path_measure_clamp_distance (self, distance);
+
+ for (i = 0; i < self->n_contours - 1; i++)
+ {
+ if (distance < self->measures[i].length)
+ break;
+
+ distance -= self->measures[i].length;
+ }
+
+ g_assert (0 <= i && i < self->n_contours);
+
+ distance = CLAMP (distance, 0, self->measures[i].length);
+
+ contour = gsk_path_get_contour (self->path, i);
+
+ gsk_contour_get_point (contour, self->measures[i].contour_data, distance, res);
+ res->contour = i;
+
+ return TRUE;
+}
+
+/**
+ * gsk_path_point_get_distance:
+ * @point: a `GskPathPoint on the path
+ * @measure: a `GskPathMeasure` for the path
+ *
+ * Returns the distance from the beginning of the path
+ * to @point.
+ *
+ * Returns: the distance of @point
+ *
+ * Since: 4.14
+ */
+float
+gsk_path_point_get_distance (const GskPathPoint *point,
+ GskPathMeasure *measure)
+{
+ GskRealPathPoint *p = (GskRealPathPoint *)point;
+ const GskContour *contour;
+ float contour_offset = 0;
+
+ g_return_val_if_fail (point != NULL, 0);
+ g_return_val_if_fail (measure != NULL, 0);
+ g_return_val_if_fail (p->contour < measure->n_contours, 0);
+
+ contour = gsk_path_get_contour (measure->path, p->contour);
+
+ for (gsize i = 0; i < measure->n_contours; i++)
+ {
+ if (contour == gsk_path_get_contour (measure->path, i))
+ return contour_offset + gsk_contour_get_distance (contour,
+ p,
+ measure->measures[i].contour_data);
+
+ contour_offset += measure->measures[i].length;
+ }
+
+ g_return_val_if_reached (0);
+}
diff --git a/gsk/gskpathmeasure.h b/gsk/gskpathmeasure.h
new file mode 100644
index 0000000000..1f72abb726
--- /dev/null
+++ b/gsk/gskpathmeasure.h
@@ -0,0 +1,62 @@
+/*
+ * 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 .
+ *
+ * Authors: Benjamin Otte
+ */
+
+#pragma once
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_PATH_MEASURE (gsk_path_measure_get_type ())
+
+GDK_AVAILABLE_IN_4_14
+GType gsk_path_measure_get_type (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_4_14
+GskPathMeasure * gsk_path_measure_new (GskPath *path);
+GDK_AVAILABLE_IN_4_14
+GskPathMeasure * gsk_path_measure_new_with_tolerance (GskPath *path,
+ float tolerance);
+
+GDK_AVAILABLE_IN_4_14
+GskPathMeasure * gsk_path_measure_ref (GskPathMeasure *self);
+GDK_AVAILABLE_IN_4_14
+void gsk_path_measure_unref (GskPathMeasure *self);
+
+GDK_AVAILABLE_IN_4_14
+GskPath * gsk_path_measure_get_path (GskPathMeasure *self) G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_14
+float gsk_path_measure_get_tolerance (GskPathMeasure *self) G_GNUC_PURE;
+
+GDK_AVAILABLE_IN_4_14
+float gsk_path_measure_get_length (GskPathMeasure *self);
+
+GDK_AVAILABLE_IN_4_14
+gboolean gsk_path_measure_get_point (GskPathMeasure *self,
+ float distance,
+ GskPathPoint *result);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskPathMeasure, gsk_path_measure_unref)
+
+G_END_DECLS
diff --git a/gsk/gskpathpoint.h b/gsk/gskpathpoint.h
index 13711b768a..e88128dbb6 100644
--- a/gsk/gskpathpoint.h
+++ b/gsk/gskpathpoint.h
@@ -78,5 +78,8 @@ float gsk_path_point_get_curvature (const GskPathPoint *poin
GskPathDirection direction,
graphene_point_t *center);
+GDK_AVAILABLE_IN_4_14
+float gsk_path_point_get_distance (const GskPathPoint *point,
+ GskPathMeasure *measure);
G_END_DECLS
diff --git a/gsk/gsktypes.h b/gsk/gsktypes.h
index 234f8fe1b0..f9554bc886 100644
--- a/gsk/gsktypes.h
+++ b/gsk/gsktypes.h
@@ -27,6 +27,7 @@
typedef struct _GskPath GskPath;
typedef struct _GskPathBuilder GskPathBuilder;
+typedef struct _GskPathMeasure GskPathMeasure;
typedef struct _GskPathPoint GskPathPoint;
typedef struct _GskRenderer GskRenderer;
typedef struct _GskRenderNode GskRenderNode;
diff --git a/gsk/meson.build b/gsk/meson.build
index bd9807e55f..5cb2e14e56 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -28,6 +28,7 @@ gsk_public_sources = files([
'gskglshader.c',
'gskpath.c',
'gskpathbuilder.c',
+ 'gskpathmeasure.c',
'gskpathpoint.c',
'gskrenderer.c',
'gskrendernode.c',
@@ -75,6 +76,7 @@ gsk_public_headers = files([
'gskglshader.h',
'gskpath.h',
'gskpathbuilder.h',
+ 'gskpathmeasure.h',
'gskpathpoint.h',
'gskrenderer.h',
'gskrendernode.h',
diff --git a/testsuite/gsk/curve-special-cases.c b/testsuite/gsk/curve-special-cases.c
index 16df5fffa3..a381866b4e 100644
--- a/testsuite/gsk/curve-special-cases.c
+++ b/testsuite/gsk/curve-special-cases.c
@@ -165,6 +165,8 @@ test_circle (void)
gsk_curve_get_end_tangent (&c, &tangent);
g_assert_true (graphene_vec2_equal (&tangent, graphene_vec2_init (&tangent2, -1, 0)));
+ g_assert_cmpfloat_with_epsilon (gsk_curve_get_length (&c), M_PI_2, 0.001);
+
for (int i = 1; i < 10; i++)
{
float t = i / 10.f;
@@ -180,6 +182,25 @@ test_circle (void)
}
}
+static void
+test_curve_length (void)
+{
+ GskCurve c, c1, c2;
+ float l, l1, l2, l1a;
+
+ parse_curve (&c, "M 1462.632080 -1593.118896 C 751.533630 -74.179169 -914.280090 956.537720 -83.091866 207.213776");
+
+ gsk_curve_split (&c, 0.5, &c1, &c2);
+
+ l = gsk_curve_get_length (&c);
+ l1a = gsk_curve_get_length_to (&c, 0.5);
+ l1 = gsk_curve_get_length (&c1);
+ l2 = gsk_curve_get_length (&c2);
+
+ g_assert_cmpfloat_with_epsilon (l1, l1a, 0.1);
+ g_assert_cmpfloat_with_epsilon (l, l1 + l2, 0.5);
+}
+
int
main (int argc,
char *argv[])
@@ -190,6 +211,7 @@ main (int argc,
g_test_add_func ("/curve/special/degenerate-tangents", test_curve_degenerate_tangents);
g_test_add_func ("/curve/special/crossing", test_curve_crossing);
g_test_add_func ("/curve/special/circle", test_circle);
+ g_test_add_func ("/curve/special/length", test_curve_length);
return g_test_run ();
}
diff --git a/testsuite/gsk/curve.c b/testsuite/gsk/curve.c
index 610041b77b..171b8a3b3a 100644
--- a/testsuite/gsk/curve.c
+++ b/testsuite/gsk/curve.c
@@ -197,8 +197,8 @@ test_curve_decompose (void)
gsk_curve_get_point (&c, (pol->t + last->t) / 2, &p);
/* The decomposer does this cheaper Manhattan distance test,
* so graphene_point_near() does not work */
- g_assert_cmpfloat (fabs (mid.x - p.x), <=, tolerance);
- g_assert_cmpfloat (fabs (mid.y - p.y), <=, tolerance);
+ g_assert_cmpfloat (fabs (mid.x - p.x), <=, tolerance + 0.0002);
+ g_assert_cmpfloat (fabs (mid.y - p.y), <=, tolerance + 0.0002);
}
}
}
@@ -300,42 +300,56 @@ test_curve_decompose_into_cubic (void)
static void
test_curve_split (void)
{
- for (int i = 0; i < 100; i++)
+ for (int i = 0; i < 20; i++)
{
GskCurve c;
- GskCurve c1, c2;
- graphene_point_t p;
- graphene_vec2_t t, t1, t2;
init_random_curve (&c);
- gsk_curve_split (&c, 0.5, &c1, &c2);
+ for (int j = 0; j < 20; j++)
+ {
+ GskCurve c1, c2;
+ graphene_point_t p;
+ graphene_vec2_t t, t1, t2;
+ float split;
- g_assert_true (c1.op == c.op);
- g_assert_true (c2.op == c.op);
+ split = g_test_rand_double_range (0.1, 0.9);
- g_assert_true (graphene_point_near (gsk_curve_get_start_point (&c),
- gsk_curve_get_start_point (&c1), 0.005));
- g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c1),
- gsk_curve_get_start_point (&c2), 0.005));
- g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c),
- gsk_curve_get_end_point (&c2), 0.005));
- gsk_curve_get_point (&c, 0.5, &p);
- gsk_curve_get_tangent (&c, 0.5, &t);
- g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c1), &p, 0.005));
- g_assert_true (graphene_point_near (gsk_curve_get_start_point (&c2), &p, 0.005));
+ gsk_curve_split (&c, split, &c1, &c2);
- gsk_curve_get_start_tangent (&c, &t1);
- gsk_curve_get_start_tangent (&c1, &t2);
- g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));
- gsk_curve_get_end_tangent (&c1, &t1);
- gsk_curve_get_start_tangent (&c2, &t2);
- g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));
- g_assert_true (graphene_vec2_near (&t, &t1, 0.005));
- g_assert_true (graphene_vec2_near (&t, &t2, 0.005));
- gsk_curve_get_end_tangent (&c, &t1);
- gsk_curve_get_end_tangent (&c2, &t2);
- g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));
+ g_assert_true (c1.op == c.op);
+ g_assert_true (c2.op == c.op);
+
+ g_assert_true (graphene_point_near (gsk_curve_get_start_point (&c),
+ gsk_curve_get_start_point (&c1), 0.005));
+ g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c1),
+ gsk_curve_get_start_point (&c2), 0.005));
+ g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c),
+ gsk_curve_get_end_point (&c2), 0.005));
+ gsk_curve_get_point (&c, split, &p);
+ gsk_curve_get_tangent (&c, split, &t);
+ g_assert_true (graphene_point_near (gsk_curve_get_end_point (&c1), &p, 0.005));
+ g_assert_true (graphene_point_near (gsk_curve_get_start_point (&c2), &p, 0.005));
+
+ gsk_curve_get_start_tangent (&c, &t1);
+ gsk_curve_get_start_tangent (&c1, &t2);
+ g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));
+ gsk_curve_get_end_tangent (&c1, &t1);
+ gsk_curve_get_start_tangent (&c2, &t2);
+ g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));
+ g_assert_true (graphene_vec2_near (&t, &t1, 0.005));
+ g_assert_true (graphene_vec2_near (&t, &t2, 0.005));
+ gsk_curve_get_end_tangent (&c, &t1);
+ gsk_curve_get_end_tangent (&c2, &t2);
+ g_assert_true (graphene_vec2_near (&t1, &t2, 0.005));
+
+#if 0
+ /* hard to guarantee this for totally random random curves */
+ g_assert_cmpfloat_with_epsilon (gsk_curve_get_length (&c),
+ gsk_curve_get_length (&c1) + gsk_curve_get_length (&c2),
+ 1);
+#endif
+ }
}
}
@@ -363,6 +377,26 @@ test_curve_derivative (void)
}
}
+static void
+test_curve_length (void)
+{
+ GskCurve c;
+ float l, l0;
+
+ for (int i = 0; i < 1000; i++)
+ {
+ init_random_curve (&c);
+
+ l = gsk_curve_get_length (&c);
+ l0 = graphene_point_distance (gsk_curve_get_start_point (&c),
+ gsk_curve_get_end_point (&c),
+ NULL, NULL);
+ g_assert_true (l >= l0 - 0.001);
+ if (c.op == GSK_PATH_LINE)
+ g_assert_cmpfloat_with_epsilon (l, l0, 0.001);
+ }
+}
+
int
main (int argc, char *argv[])
{
@@ -376,6 +410,7 @@ main (int argc, char *argv[])
g_test_add_func ("/curve/decompose-cubic", test_curve_decompose_into_cubic);
g_test_add_func ("/curve/split", test_curve_split);
g_test_add_func ("/curve/derivative", test_curve_derivative);
+ g_test_add_func ("/curve/length", test_curve_length);
return g_test_run ();
}
diff --git a/testsuite/gsk/path-special-cases.c b/testsuite/gsk/path-special-cases.c
index 05c4370026..3671503cbe 100644
--- a/testsuite/gsk/path-special-cases.c
+++ b/testsuite/gsk/path-special-cases.c
@@ -809,6 +809,72 @@ test_rounded_rect (void)
gsk_path_unref (path);
}
+static void
+test_circle (void)
+{
+ GskPathBuilder *builder;
+ GskPath *path;
+ GskPathMeasure *measure;
+ float length;
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 1);
+ path = gsk_path_builder_free_to_path (builder);
+
+ measure = gsk_path_measure_new (path);
+ length = gsk_path_measure_get_length (measure);
+
+ g_assert_cmpfloat_with_epsilon (length, 2 * M_PI, 0.001);
+
+ gsk_path_measure_unref (measure);
+ gsk_path_unref (path);
+}
+
+static void
+test_length (void)
+{
+ GskPath *path, *path1, *path2;
+ GskPathMeasure *measure, *measure1, *measure2;
+ GskPathBuilder *builder;
+ GskPathPoint point, start, end;
+ float length, length1, length2;
+ float distance;
+ float tolerance = 0.1;
+
+ path = gsk_path_parse ("M 0 0 Q 0 0 5 5");
+ measure = gsk_path_measure_new_with_tolerance (path, tolerance);
+ length = gsk_path_measure_get_length (measure);
+
+ gsk_path_get_start_point (path, &start);
+ gsk_path_get_end_point (path, &end);
+ gsk_path_measure_get_point (measure, length / 2, &point);
+ distance = gsk_path_point_get_distance (&point, measure);
+
+ g_assert_cmpfloat_with_epsilon (length / 2, distance, 0.1);
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_segment (builder, path, &start, &point);
+ path1 = gsk_path_builder_free_to_path (builder);
+ measure1 = gsk_path_measure_new_with_tolerance (path1, tolerance);
+ length1 = gsk_path_measure_get_length (measure1);
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_segment (builder, path, &point, &end);
+ path2 = gsk_path_builder_free_to_path (builder);
+ measure2 = gsk_path_measure_new_with_tolerance (path2, tolerance);
+ length2 = gsk_path_measure_get_length (measure2);
+
+ g_assert_cmpfloat_with_epsilon (length, length1 + length2, tolerance);
+
+ gsk_path_unref (path);
+ gsk_path_unref (path1);
+ gsk_path_unref (path2);
+
+ gsk_path_measure_unref (measure);
+ gsk_path_measure_unref (measure1);
+ gsk_path_measure_unref (measure2);
+}
+
int
main (int argc, char *argv[])
{
@@ -825,6 +891,8 @@ main (int argc, char *argv[])
g_test_add_func ("/path/builder/add", test_path_builder_add);
g_test_add_func ("/path/rotated-arc", test_rotated_arc);
g_test_add_func ("/path/rounded-rect", test_rounded_rect);
+ g_test_add_func ("/path/circle", test_circle);
+ g_test_add_func ("/path/length", test_length);
return g_test_run ();
}
diff --git a/testsuite/gsk/path.c b/testsuite/gsk/path.c
index 213af4cfbb..89a8e2826c 100644
--- a/testsuite/gsk/path.c
+++ b/testsuite/gsk/path.c
@@ -780,6 +780,199 @@ test_in_fill_rotated (void)
#undef N_FILL_RULES
}
+static void
+test_split (void)
+{
+ GskPath *path, *path1, *path2;
+ GskPathMeasure *measure, *measure1, *measure2;
+ float length, length1, length2;
+ GskPathBuilder *builder;
+ float split, epsilon;
+ GskPathPoint point0, point1, point2;
+ float tolerance = 0.5;
+
+ for (int i = 0; i < 100; i++)
+ {
+ if (g_test_verbose ())
+ g_test_message ("path %u", i);
+
+ path = create_random_path (1);
+ measure = gsk_path_measure_new_with_tolerance (path, tolerance);
+
+ length = gsk_path_measure_get_length (measure);
+ /* chosen high enough to stop the testsuite from failing */
+ epsilon = MAX (length / 1000, 1.f / 1024);
+
+ split = g_test_rand_double_range (0, length);
+
+ if (!gsk_path_get_start_point (path, &point0) ||
+ !gsk_path_measure_get_point (measure, split, &point1) ||
+ !gsk_path_get_end_point (path, &point2))
+ {
+ gsk_path_unref (path);
+ gsk_path_measure_unref (measure);
+ continue;
+ }
+
+ if (gsk_path_point_equal (&point0, &point1) ||
+ gsk_path_point_equal (&point1, &point2))
+ {
+ gsk_path_unref (path);
+ gsk_path_measure_unref (measure);
+ continue;
+ }
+
+ g_assert_true (gsk_path_point_compare (&point0, &point1) < 0);
+ g_assert_true (gsk_path_point_compare (&point1, &point2) < 0);
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_segment (builder, path, &point0, &point1);
+ path1 = gsk_path_builder_free_to_path (builder);
+ measure1 = gsk_path_measure_new_with_tolerance (path1, tolerance);
+ length1 = gsk_path_measure_get_length (measure1);
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_segment (builder, path, &point1, &point2);
+ path2 = gsk_path_builder_free_to_path (builder);
+ measure2 = gsk_path_measure_new_with_tolerance (path2, tolerance);
+ length2 = gsk_path_measure_get_length (measure2);
+
+ g_assert_cmpfloat_with_epsilon (length, length1 + length2, epsilon);
+
+ gsk_path_unref (path2);
+ gsk_path_unref (path1);
+ gsk_path_unref (path);
+
+ gsk_path_measure_unref (measure2);
+ gsk_path_measure_unref (measure1);
+ gsk_path_measure_unref (measure);
+ }
+}
+
+static void
+test_roundtrip (void)
+{
+ GskPath *path;
+ GskPathMeasure *measure;
+ float length;
+ float split, epsilon;
+ GskPathPoint point;
+ float distance;
+ float tolerance = 0.5;
+
+ for (int i = 0; i < 100; i++)
+ {
+ if (g_test_verbose ())
+ g_test_message ("path %u", i);
+
+ path = create_random_path (1);
+ measure = gsk_path_measure_new_with_tolerance (path, tolerance);
+
+ length = gsk_path_measure_get_length (measure);
+ /* chosen high enough to stop the testsuite from failing */
+ epsilon = MAX (length / 1000, 1.f / 1024);
+
+ split = g_test_rand_double_range (0, length);
+
+ if (!gsk_path_measure_get_point (measure, split, &point))
+ {
+ gsk_path_unref (path);
+ gsk_path_measure_unref (measure);
+ continue;
+ }
+
+ distance = gsk_path_point_get_distance (&point, measure);
+
+ g_assert_cmpfloat_with_epsilon (split, distance, epsilon);
+
+ gsk_path_unref (path);
+ gsk_path_measure_unref (measure);
+ }
+}
+
+static void
+test_segment (void)
+{
+ GskPath *path, *path1, *path2, *path3;
+ GskPathMeasure *measure, *measure1, *measure2, *measure3;
+ GskPathPoint point0, point1, point2, point3;
+ float length, length1, length2, length3;
+ GskPathBuilder *builder;
+ float split1, split2, epsilon;
+ float tolerance = 0.5;
+
+ for (int i = 0; i < 100; i++)
+ {
+ if (g_test_verbose ())
+ g_test_message ("path %u", i);
+
+ path = create_random_path (G_MAXUINT);
+ measure = gsk_path_measure_new_with_tolerance (path, tolerance);
+ length = gsk_path_measure_get_length (measure);
+
+ /* We are accumulating both the split error and the roundtrip error
+ * here (on both ends, for the middle segment). So we should expect
+ * the epsilon here to be at least 4 times the epsilon we can use
+ * in the split and roundtrip tests.
+ */
+ epsilon = MAX (length / 200, 1.f / 1024);
+
+ split1 = g_test_rand_double_range (0, length);
+ split2 = g_test_rand_double_range (split1, length);
+
+ if (!gsk_path_get_start_point (path, &point0) ||
+ !gsk_path_measure_get_point (measure, split1, &point1) ||
+ !gsk_path_measure_get_point (measure, split2, &point2) ||
+ !gsk_path_get_end_point (path, &point3))
+ {
+ gsk_path_unref (path);
+ gsk_path_measure_unref (measure);
+ continue;
+ }
+
+ if (gsk_path_point_equal (&point0, &point1) ||
+ gsk_path_point_equal (&point1, &point2) ||
+ gsk_path_point_equal (&point2, &point3))
+ {
+ gsk_path_unref (path);
+ gsk_path_measure_unref (measure);
+ continue;
+ }
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_segment (builder, path, &point0, &point1);
+ path1 = gsk_path_builder_free_to_path (builder);
+ measure1 = gsk_path_measure_new_with_tolerance (path1, tolerance);
+ length1 = gsk_path_measure_get_length (measure1);
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_segment (builder, path, &point1, &point2);
+ path2 = gsk_path_builder_free_to_path (builder);
+ measure2 = gsk_path_measure_new_with_tolerance (path2, tolerance);
+ length2 = gsk_path_measure_get_length (measure2);
+
+ builder = gsk_path_builder_new ();
+ gsk_path_builder_add_segment (builder, path, &point2, &point3);
+ path3 = gsk_path_builder_free_to_path (builder);
+ measure3 = gsk_path_measure_new_with_tolerance (path3, tolerance);
+ length3 = gsk_path_measure_get_length (measure3);
+
+ g_assert_cmpfloat_with_epsilon (split1, length1, epsilon);
+ g_assert_cmpfloat_with_epsilon (split2, length1 + length2, epsilon);
+ g_assert_cmpfloat_with_epsilon (length, length1 + length2 + length3, epsilon);
+
+ gsk_path_unref (path3);
+ gsk_path_unref (path2);
+ gsk_path_unref (path1);
+ gsk_path_unref (path);
+
+ gsk_path_measure_unref (measure3);
+ gsk_path_measure_unref (measure2);
+ gsk_path_measure_unref (measure1);
+ gsk_path_measure_unref (measure);
+ }
+}
+
int
main (int argc,
char *argv[])
@@ -790,6 +983,9 @@ main (int argc,
g_test_add_func ("/path/parse", test_parse);
g_test_add_func ("/path/in-fill-union", test_in_fill_union);
g_test_add_func ("/path/in-fill-rotated", test_in_fill_rotated);
+ g_test_add_func ("/path/measure/split", test_split);
+ g_test_add_func ("/path/measure/roundtrip", test_roundtrip);
+ g_test_add_func ("/path/measure/segment", test_segment);
return g_test_run ();
}