[gtk/wip/matthiasc/lottie-stroke: 2/4] Add an interactive test for stroking




commit 5ff52553b014e5cafc8e3ad67266e76b6a684c23
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Nov 28 13:30:35 2020 -0500

    Add an interactive test for stroking
    
    Give the curve test a full complement of stroke
    parameters to play with and make it useful for
    debugging various aspects of path functionality
    and in particular path stroking.

 tests/curve2.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 193 insertions(+), 30 deletions(-)
---
diff --git a/tests/curve2.c b/tests/curve2.c
index c4be5cf8f0..c3178d11e7 100644
--- a/tests/curve2.c
+++ b/tests/curve2.c
@@ -18,6 +18,13 @@ struct _DemoWidget
   gboolean track;
   gboolean show_bounding_box;
   GtkWidget *label;
+
+  gboolean do_stroke;
+  GskPathMeasure *stroke_measure;
+  GskPath *stroke_path;
+  gboolean inside;
+  GskFillRule fill_rule;
+  GskStroke *stroke;
 };
 
 struct _DemoWidgetClass
@@ -33,29 +40,43 @@ motion (GtkEventControllerMotion *controller,
         double                    y,
         DemoWidget               *self)
 {
-  float distance;
-  char *text;
-  float t;
+  if (self->track)
+    {
+      float distance;
+      char *text;
+      float t;
 
-  if (!self->track)
-    return;
+      self->x = x;
+      self->y = y;
 
-  self->x = x;
-  self->y = y;
-  gsk_path_measure_get_closest_point_full (self->measure,
-                                           &GRAPHENE_POINT_INIT (x, y),
-                                           FLT_MAX,
-                                           &distance,
-                                           &self->point,
-                                           &t,
-                                           &self->tangent);
+      gsk_path_measure_get_closest_point_full (self->measure,
+                                               &GRAPHENE_POINT_INIT (x, y),
+                                               INFINITY,
+                                               &distance,
+                                               &self->point,
+                                               &t,
+                                               &self->tangent);
 
-  gsk_path_measure_get_point (self->measure, t, &self->point2, NULL);
+      gsk_path_measure_get_point (self->measure, t, &self->point2, NULL);
 
-  text = g_strdup_printf ("%.1f", distance);
-  gtk_label_set_label (GTK_LABEL (self->label), text);
+      text = g_strdup_printf ("%.1f", distance);
+      gtk_label_set_label (GTK_LABEL (self->label), text);
 
-  gtk_widget_queue_draw (GTK_WIDGET (self));
+      gtk_widget_queue_draw (GTK_WIDGET (self));
+    }
+
+  if (self->do_stroke)
+    {
+      gboolean inside = TRUE;
+
+      inside = gsk_path_measure_in_fill (self->stroke_measure, &GRAPHENE_POINT_INIT (x, y), self->fill_rule);
+
+      if (self->inside != inside)
+        {
+          self->inside = inside;
+          gtk_widget_queue_draw (GTK_WIDGET (self));
+        }
+    }
 }
 
 static void
@@ -85,7 +106,6 @@ demo_widget_snapshot (GtkWidget   *widget,
   GskStroke *stroke;
   GskPathBuilder *builder;
   GskPath *path;
-  graphene_point_t p;
 
   if (!self->path)
     return;
@@ -93,21 +113,55 @@ demo_widget_snapshot (GtkWidget   *widget,
   width = gtk_widget_get_width (widget);
   height = gtk_widget_get_width (widget);
 
-  stroke = gsk_stroke_new (1.0);
-  gtk_snapshot_push_stroke (snapshot, self->path, stroke);
-  gsk_stroke_free (stroke);
+  if (self->do_stroke)
+    {
+      if (self->inside)
+        {
+           gtk_snapshot_push_fill (snapshot, self->stroke_path, self->fill_rule);
+
+           gtk_snapshot_append_color (snapshot,
+                                      &(GdkRGBA){ 1, 0, 1, 0.3},
+                                      &GRAPHENE_RECT_INIT (0, 0, width, height ));
 
-  gtk_snapshot_append_color (snapshot,
-                             &(GdkRGBA){ 0, 0, 0, 1},
-                             &GRAPHENE_RECT_INIT (0, 0, width, height ));
+           gtk_snapshot_pop (snapshot);
+        }
 
-  gtk_snapshot_pop (snapshot);
+      stroke = gsk_stroke_new (1.0);
+      gtk_snapshot_push_stroke (snapshot, self->stroke_path, stroke);
+
+      gtk_snapshot_append_color (snapshot,
+                                 &(GdkRGBA){ 0, 0, 0, 1},
+                                 &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+      gtk_snapshot_pop (snapshot);
+
+      gtk_snapshot_push_stroke (snapshot, self->path, stroke);
+
+      gtk_snapshot_append_color (snapshot,
+                                 &(GdkRGBA){ 0, 0, 0, 0.3},
+                                 &GRAPHENE_RECT_INIT (0, 0, width, height ));
+
+      gtk_snapshot_pop (snapshot);
+      gsk_stroke_free (stroke);
+    }
+  else
+    {
+      stroke = gsk_stroke_new (1.0);
+      gtk_snapshot_push_stroke (snapshot, self->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);
+    }
 
   if (self->show_bounding_box)
     {
       graphene_rect_t bounds;
 
-      if (gsk_path_get_bounds (self->path, &bounds))
+      if (gsk_path_get_bounds (self->do_stroke ? self->stroke_path : self->path, &bounds))
         {
           builder = gsk_path_builder_new ();
 
@@ -131,6 +185,8 @@ demo_widget_snapshot (GtkWidget   *widget,
 
   if (self->track)
     {
+      graphene_point_t p;
+
       p.x = self->point.x + graphene_vec2_get_x (&self->tangent) * 40;
       p.y = self->point.y + graphene_vec2_get_y (&self->tangent) * 40;
 
@@ -190,6 +246,9 @@ demo_widget_dispose (GObject *object)
 
   g_clear_pointer (&self->path, gsk_path_unref);
   g_clear_pointer (&self->measure, gsk_path_measure_unref);
+  g_clear_pointer (&self->stroke_path, gsk_path_unref);
+  g_clear_pointer (&self->stroke_measure, gsk_path_measure_unref);
+  g_clear_pointer (&self->stroke, gsk_stroke_free);
   g_clear_pointer (&self->label, gtk_widget_unparent);
 
   G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
@@ -214,6 +273,21 @@ demo_widget_new (void)
   return g_object_new (DEMO_TYPE_WIDGET, NULL);
 }
 
+static void
+update_stroke_path (DemoWidget *self)
+{
+  g_clear_pointer (&self->stroke_path, gsk_path_unref);
+  g_clear_pointer (&self->stroke_measure, gsk_path_measure_unref);
+
+  if (self->do_stroke)
+    {
+      self->stroke_path = gsk_path_to_stroke (self->path, self->stroke);
+      self->stroke_measure = gsk_path_measure_new (self->stroke_path);
+    }
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
 static void
 update_path (DemoWidget *self)
 {
@@ -238,6 +312,8 @@ update_path (DemoWidget *self)
 
   self->measure = gsk_path_measure_new (self->path);
 
+  update_stroke_path (self);
+
   gtk_widget_queue_draw (GTK_WIDGET (self));
 }
 
@@ -287,6 +363,8 @@ init_demo (DemoWidget  *demo,
   gtk_editable_set_text (editable, string);
   g_free (string);
   gsk_path_unref (path);
+
+  demo->stroke = gsk_stroke_new (20);
 }
 
 static void
@@ -305,6 +383,15 @@ bb_toggled (GtkCheckButton *button,
   gtk_widget_queue_draw (GTK_WIDGET (self));
 }
 
+static void
+stroke_toggled (GtkCheckButton *button,
+                DemoWidget      *self)
+{
+  self->do_stroke = gtk_check_button_get_active (button);
+  update_stroke_path (self);
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
 static GtkWidget *start_scale;
 static GtkWidget *end_scale;
 
@@ -333,12 +420,55 @@ range_changed (GtkRange   *range,
   update_path (self);
 }
 
+static void
+fill_rule_changed (GtkDropDown *combo,
+                   GParamSpec  *pspec,
+                   DemoWidget  *self)
+{
+  self->fill_rule = (GskFillRule)gtk_drop_down_get_selected (combo);
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+cap_changed (GtkDropDown *combo,
+             GParamSpec  *pspec,
+             DemoWidget  *self)
+{
+  gsk_stroke_set_line_cap (self->stroke, (GskLineCap)gtk_drop_down_get_selected (combo));
+  update_stroke_path (self);
+}
+
+static void
+join_changed (GtkDropDown *combo,
+              GParamSpec  *pspec,
+              DemoWidget  *self)
+{
+  gsk_stroke_set_line_join (self->stroke, (GskLineJoin)gtk_drop_down_get_selected (combo));
+  update_stroke_path (self);
+}
+
+static void
+limit_changed (GtkSpinButton *spin,
+               DemoWidget    *self)
+{
+  gsk_stroke_set_miter_limit (self->stroke, gtk_spin_button_get_value (spin));
+  update_stroke_path (self);
+}
+
+static void
+width_changed (GtkSpinButton *spin,
+               DemoWidget    *self)
+{
+  gsk_stroke_set_line_width (self->stroke, gtk_spin_button_get_value (spin));
+  update_stroke_path (self);
+}
+
 int
 main (int argc, char *argv[])
 {
   GtkWidget *window, *box, *demo, *entry;
   GtkWidget *popover, *button, *grid;
-  GtkWidget *header, *toggle;
+  GtkWidget *header, *toggle, *combo, *spin;
 
   gtk_init ();
 
@@ -368,11 +498,44 @@ main (int argc, char *argv[])
 
   toggle = gtk_check_button_new_with_label ("Show closest point");
   g_signal_connect (toggle, "toggled", G_CALLBACK (track_toggled), demo);
-  gtk_grid_attach (GTK_GRID (grid), toggle, 0, 0, 1, 1);
+  gtk_grid_attach (GTK_GRID (grid), toggle, 1, 0, 1, 1);
 
   toggle = gtk_check_button_new_with_label ("Show bounding box");
   g_signal_connect (toggle, "toggled", G_CALLBACK (bb_toggled), demo);
-  gtk_grid_attach (GTK_GRID (grid), toggle, 0, 1, 1, 1);
+  gtk_grid_attach (GTK_GRID (grid), toggle, 1, 1, 1, 1);
+
+  toggle = gtk_check_button_new_with_label ("Stroke path");
+  g_signal_connect (toggle, "toggled", G_CALLBACK (stroke_toggled), demo);
+  gtk_grid_attach (GTK_GRID (grid), toggle, 1, 2, 1, 1);
+
+  gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Fill rule"), 0, 3, 1, 1);
+
+  combo = gtk_drop_down_new_from_strings ((const char *[]){"Winding", "Even-Odd", NULL });
+  g_signal_connect (combo, "notify::selected", G_CALLBACK (fill_rule_changed), demo);
+  gtk_grid_attach (GTK_GRID (grid), combo, 1, 3, 1, 1);
+
+  gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line cap:"), 0, 4, 1, 1);
+  combo = gtk_drop_down_new_from_strings ((const char *[]){"Butt", "Round", "Square", NULL});
+  g_signal_connect (combo, "notify::selected", G_CALLBACK (cap_changed), demo);
+  gtk_grid_attach (GTK_GRID (grid), combo, 1, 4, 1, 1);
+
+  gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line join:"), 0, 5, 1, 1);
+  combo = gtk_drop_down_new_from_strings ((const char *[]){"Miter", "Miter-clip", "Round", "Bevel", NULL});
+  g_signal_connect (combo, "notify::selected", G_CALLBACK (join_changed), demo);
+  gtk_grid_attach (GTK_GRID (grid), combo, 1, 5, 1, 1);
+
+  gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Miter limit:"), 0, 6, 1, 1);
+  spin = gtk_spin_button_new_with_range (0, 10, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 4);
+  g_signal_connect (spin, "value-changed", G_CALLBACK (limit_changed), demo);
+  gtk_grid_attach (GTK_GRID (grid), spin, 1, 6, 1, 1);
+
+  gtk_grid_attach (GTK_GRID (grid), gtk_label_new ("Line width:"), 0, 7, 1, 1);
+  spin = gtk_spin_button_new_with_range (1, 40, 1);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 20);
+  g_signal_connect (spin, "value-changed", G_CALLBACK (width_changed), demo);
+  gtk_grid_attach (GTK_GRID (grid), spin, 1, 7, 1, 1);
+
 
   entry = gtk_entry_new ();
   g_signal_connect (entry, "activate", G_CALLBACK (activate), demo);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]