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 @@ + + + + Text along a Path + + + + + document-edit-symbolic + + + + + + + vertical + + + + + + Through the looking glass + + + + + + + + + true + true + + + + + + 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 - + + + + + + + 0 + 5000 + 500 + + + + + + + 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). + +
+ + + Path Tangents + +
Path Tangents
+
## 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 (); }