[gtk/matthiasc/lottie] Rename to CurveEditor



commit 971f637b0264963173305db93b085f6085f28fa7
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Nov 20 19:12:04 2020 -0500

    Rename to CurveEditor
    
    And start separating the widget from the demo
    with an api.

 tests/curve.c | 351 ++++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 267 insertions(+), 84 deletions(-)
---
diff --git a/tests/curve.c b/tests/curve.c
index 7ec1a5834d..d143e4eac7 100644
--- a/tests/curve.c
+++ b/tests/curve.c
@@ -1,7 +1,5 @@
 /* TODO
  * - point insert/remove
- * - rename to CurveEditor
- * - add properties
  */
 
 #include <gtk/gtk.h>
@@ -26,6 +24,19 @@ closest_point (const graphene_point_t *p,
   q->y = a->y + t * (b->y - a->y);
 }
 
+/* Determine if p is on the line through a and b */
+static gboolean
+collinear (const graphene_point_t *p,
+           const graphene_point_t *a,
+           const graphene_point_t *b)
+{
+  graphene_point_t q;
+
+  closest_point (p, a, b, &q);
+
+  return graphene_point_near (p, &q, 0.0001);
+}
+
 /* Set q to the point on the line through p and a that is
  * at a distance of d from p, on the opposite side
  */
@@ -48,7 +59,7 @@ opposite_point (const graphene_point_t *p,
 
 #define RADIUS 5
 
-G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
+G_DECLARE_FINAL_TYPE (CurveEditor, curve_editor, CURVE, EDITOR, GtkWidget)
 
 typedef enum
 {
@@ -93,7 +104,7 @@ typedef struct
   gboolean smooth;
 } PointData;
 
-struct _DemoWidget
+struct _CurveEditor
 {
   GtkWidget parent_instance;
   graphene_point_t *points;
@@ -108,12 +119,12 @@ struct _DemoWidget
   GActionMap *actions;
 };
 
-struct _DemoWidgetClass
+struct _CurveEditorClass
 {
   GtkWidgetClass parent_class;
 };
 
-G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
+G_DEFINE_TYPE (CurveEditor, curve_editor, GTK_TYPE_WIDGET)
 
 static float
 dist (graphene_point_t *a, graphene_point_t *b)
@@ -128,7 +139,7 @@ static void
 drag_begin (GtkGestureDrag *gesture,
             double          start_x,
             double          start_y,
-            DemoWidget     *self)
+            CurveEditor     *self)
 {
   int i;
   graphene_point_t p = GRAPHENE_POINT_INIT (start_x, start_y);
@@ -153,7 +164,7 @@ static void
 drag_update (GtkGestureDrag *gesture,
              double          offset_x,
              double          offset_y,
-             DemoWidget     *self)
+             CurveEditor     *self)
 {
   double x, y;
   double dx, dy;
@@ -348,7 +359,7 @@ static void
 drag_end (GtkGestureDrag *gesture,
           double          offset_x,
           double          offset_y,
-          DemoWidget     *self)
+          CurveEditor     *self)
 {
   drag_update (gesture, offset_x, offset_y, self);
   self->dragged = -1;
@@ -356,7 +367,7 @@ drag_end (GtkGestureDrag *gesture,
 }
 
 static void
-maintain_smoothness (DemoWidget *self,
+maintain_smoothness (CurveEditor *self,
                      int         point)
 {
   gboolean smooth;
@@ -414,7 +425,7 @@ toggle_smooth (GSimpleAction *action,
                GVariant      *value,
                gpointer       data)
 {
-  DemoWidget *self = DEMO_WIDGET (data);
+  CurveEditor *self = CURVE_EDITOR (data);
 
   self->point_data[self->context / 3].smooth = g_variant_get_boolean (value);
 
@@ -428,7 +439,7 @@ set_operation (GSimpleAction *action,
                GVariant      *value,
                gpointer       data)
 {
-  DemoWidget *self = DEMO_WIDGET (data);
+  CurveEditor *self = CURVE_EDITOR (data);
 
   self->point_data[self->context / 3].op = op_from_string (g_variant_get_string (value, NULL));
 
@@ -443,7 +454,7 @@ pressed (GtkGestureClick *gesture,
          int              n_press,
          double           x,
          double           y,
-         DemoWidget      *self)
+         CurveEditor      *self)
 {
   graphene_point_t m = GRAPHENE_POINT_INIT (x, y);
   int i;
@@ -486,7 +497,7 @@ released (GtkGestureClick *gesture,
           int              n_press,
           double           x,
           double           y,
-          DemoWidget      *self)
+          CurveEditor      *self)
 {
   graphene_point_t m = GRAPHENE_POINT_INIT (x, y);
   int i;
@@ -528,52 +539,7 @@ released (GtkGestureClick *gesture,
 }
 
 static void
-init_points (DemoWidget *self)
-{
-  float w = 200;
-  float h = 200;
-  float cx = w / 2;
-  float cy = h / 2;
-  float pad = 20;
-  float r = (w - 2 * pad) / 2;
-  float k = 0.55228;
-  float kr = k  * r;
-  int i;
-
-  g_free (self->points);
-  g_free (self->point_data);
-
-  self->n_points = 12;
-  self->points = g_new (graphene_point_t, self->n_points);
-  self->point_data = g_new (PointData, self->n_points / 3);
-
-
-  self->points[0] = GRAPHENE_POINT_INIT (cx, pad);
-  self->points[1] = GRAPHENE_POINT_INIT (cx + kr, pad);
-  self->points[2] = GRAPHENE_POINT_INIT (w - pad, cy - kr);
-
-  self->points[3] = GRAPHENE_POINT_INIT (w - pad, cy);
-  self->points[4] = GRAPHENE_POINT_INIT (w - pad, cy + kr);
-  self->points[5] = GRAPHENE_POINT_INIT (cx + kr, h - pad);
-
-  self->points[6] = GRAPHENE_POINT_INIT (cx, h - pad);
-  self->points[7] = GRAPHENE_POINT_INIT (cx - kr, h - pad);
-  self->points[8] = GRAPHENE_POINT_INIT (pad, cy + kr);
- 
-  self->points[9] = GRAPHENE_POINT_INIT (pad, cy);
-  self->points[10] = GRAPHENE_POINT_INIT (pad, cy - kr);
-  self->points[11] = GRAPHENE_POINT_INIT (cx - kr, pad);
-
-  for (i = 0; i < self->n_points / 3; i++)
-    {
-      self->point_data[i].edit = FALSE;
-      self->point_data[i].smooth = TRUE;
-      self->point_data[i].op = CURVE;
-    }
-}
-
-static void
-demo_widget_init (DemoWidget *self)
+curve_editor_init (CurveEditor *self)
 {
   GtkGesture *gesture;
   GMenu *menu;
@@ -597,7 +563,9 @@ demo_widget_init (DemoWidget *self)
   g_signal_connect (gesture, "released", G_CALLBACK (released), self);
   gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
 
-  init_points (self);
+  self->points = NULL;
+  self->point_data = NULL;
+  self->n_points = 0;
 
   self->actions = G_ACTION_MAP (g_simple_action_group_new ());
 
@@ -642,10 +610,10 @@ demo_widget_init (DemoWidget *self)
 }
 
 static void
-demo_widget_snapshot (GtkWidget   *widget,
+curve_editor_snapshot (GtkWidget   *widget,
                       GtkSnapshot *snapshot)
 {
-  DemoWidget *self = (DemoWidget *)widget;
+  CurveEditor *self = (CurveEditor *)widget;
   GskPathBuilder *builder;
   GskPath *path;
   GskStroke *stroke;
@@ -653,6 +621,9 @@ demo_widget_snapshot (GtkWidget   *widget,
   float width;
   float height;
 
+  if (self->n_points == 0)
+    return;
+
   width = gtk_widget_get_width (widget);
   height = gtk_widget_get_width (widget);
 
@@ -808,7 +779,7 @@ demo_widget_snapshot (GtkWidget   *widget,
 }
 
 static void
-demo_widget_measure (GtkWidget      *widget,
+curve_editor_measure (GtkWidget      *widget,
                      GtkOrientation  orientation,
                      int             for_size,
                      int            *minimum_size,
@@ -821,71 +792,281 @@ demo_widget_measure (GtkWidget      *widget,
 }
 
 static void
-demo_widget_size_allocate (GtkWidget *widget,
+curve_editor_size_allocate (GtkWidget *widget,
                            int        width,
                            int        height,
                            int        baseline)
 {
-  DemoWidget *self = DEMO_WIDGET (widget);
+  CurveEditor *self = CURVE_EDITOR (widget);
 
   gtk_native_check_resize (GTK_NATIVE (self->menu));
 }
 
 static void
-demo_widget_dispose (GObject *object)
+curve_editor_dispose (GObject *object)
 {
-  DemoWidget *self = DEMO_WIDGET (object);
+  CurveEditor *self = CURVE_EDITOR (object);
 
   g_clear_pointer (&self->points, g_free);
   g_clear_pointer (&self->point_data, g_free);
   g_clear_pointer (&self->menu, gtk_widget_unparent);
 
-  G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
+  G_OBJECT_CLASS (curve_editor_parent_class)->dispose (object);
 }
 
 static void
-demo_widget_class_init (DemoWidgetClass *class)
+curve_editor_class_init (CurveEditorClass *class)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (class);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
 
-  object_class->dispose = demo_widget_dispose;
+  object_class->dispose = curve_editor_dispose;
 
-  widget_class->snapshot = demo_widget_snapshot;
-  widget_class->measure = demo_widget_measure;
-  widget_class->size_allocate = demo_widget_size_allocate;
+  widget_class->snapshot = curve_editor_snapshot;
+  widget_class->measure = curve_editor_measure;
+  widget_class->size_allocate = curve_editor_size_allocate;
 }
 
 static GtkWidget *
-demo_widget_new (void)
+curve_editor_new (void)
 {
-  return g_object_new (demo_widget_get_type (), NULL);
+  return g_object_new (curve_editor_get_type (), NULL);
 }
 
 static void
-edit_changed (GtkToggleButton *button,
-              GParamSpec      *pspec,
-              DemoWidget      *self)
+curve_editor_set_edit (CurveEditor *self,
+                       gboolean     edit)
 {
   int i;
 
-  self->edit = gtk_toggle_button_get_active (button);
+  self->edit = edit;
   if (!self->edit)
     {
       for (i = 0; i < self->n_points / 3; i++)
         self->point_data[i].edit = FALSE;
     }
+
   gtk_widget_queue_draw (GTK_WIDGET (self));
 }
 
+typedef struct
+{
+  int count;
+  graphene_point_t first;
+  graphene_point_t last;
+  gboolean has_close;
+  gboolean has_initial_move;
+} CountSegmentData;
+
+static gboolean
+count_segments (GskPathOperation        op,
+                const graphene_point_t *pts,
+                gsize                   n_pts,
+                gpointer                data)
+{
+  CountSegmentData *d = data;
+
+  if (d->count == 0)
+    {
+      d->first = pts[0];
+      if (op == GSK_PATH_MOVE)
+        d->has_initial_move = TRUE;
+    }
+
+  d->last = pts[n_pts - 1];
+  d->count++;
+
+  if (op == GSK_PATH_CLOSE)
+    d->has_close = TRUE;
+
+  return TRUE;
+}
+
+typedef struct
+{
+  CurveEditor *editor;
+  int pos;
+} CopySegmentData;
+
+static gboolean
+copy_segments (GskPathOperation        op,
+               const graphene_point_t *pts,
+               gsize                   n_pts,
+               gpointer                data)
+{
+  CopySegmentData *d = data;
+  int i;
+
+  switch (op)
+    {
+    case GSK_PATH_MOVE:
+      if (d->pos == 0)
+        {
+          d->editor->points[d->pos++] = pts[0];
+        }
+      else
+        {
+          d->editor->point_data[d->pos / 3].op = MOVE;
+          d->editor->point_data[d->pos / 3].smooth = FALSE;
+
+          d->editor->points[d->pos++] = pts[0];
+          d->editor->points[d->pos++] = pts[0];
+          d->editor->points[d->pos++] = pts[0];
+        }
+      break;
+    case GSK_PATH_CLOSE:
+      break;
+    case GSK_PATH_LINE:
+      d->editor->point_data[d->pos / 3].op = LINE;
+      d->editor->point_data[d->pos / 3].smooth = FALSE;
+
+      if (d->pos == 0)
+        d->editor->points[d->pos++] = pts[0];
+
+      d->editor->points[d->pos++] = pts[1];
+      d->editor->points[d->pos++] = pts[1];
+      d->editor->points[d->pos++] = pts[1];
+      break;
+    case GSK_PATH_CURVE:
+      d->editor->point_data[d->pos / 3].op = CURVE;
+      d->editor->point_data[d->pos / 3].smooth = FALSE;
+
+      if (d->pos == 0)
+        d->editor->points[d->pos++] = pts[0];
+
+      for (i = 1; i < n_pts; i++)
+        d->editor->points[d->pos++] = pts[i];
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  return TRUE;
+}
+
+/* Check if the points arount point currently satisy
+ * smoothness conditions. Set PointData.smooth accordingly.
+ */
+static void
+update_smoothness (CurveEditor *self,
+                   int          point)
+{
+  Operation op, op1;
+  graphene_point_t *p, *p2, *p1;
+
+  p = &self->points[point];
+  op = self->point_data[point / 3].op;
+  op1 = self->point_data[((point - 1 + self->n_points) % self->n_points) / 3].op;
+
+  if (op == CURVE)
+    p2 = &self->points[(point + 1) % self->n_points];
+  else if (op == LINE)
+    p2 = &self->points[(point + 3) % self->n_points];
+  else
+    p2 = NULL;
+
+  if (op1 == CURVE)
+    p1 = &self->points[(point - 1 + self->n_points) % self->n_points];
+  else if (op1 == LINE)
+    p1 = &self->points[(point - 3 + self->n_points) % self->n_points];
+  else
+    p1 = NULL;
+
+  if (p1 && p2)
+    self->point_data[point / 3].smooth = collinear (p, p1, p2);
+  else
+    self->point_data[point / 3].smooth = TRUE;
+}
+
 static void
-reset (GtkButton  *button,
-       DemoWidget *self)
+curve_editor_set_path (CurveEditor *self,
+                       GskPath     *path)
 {
-  init_points (self);
+  CountSegmentData data;
+  CopySegmentData data2;
+  int i;
+
+  g_clear_pointer (&self->points, g_free);
+  g_clear_pointer (&self->point_data, g_free);
+  self->n_points = 0;
+
+  data.count = 0;
+  data.has_close = FALSE;
+  gsk_path_foreach (path, count_segments, &data);
+
+  if (data.has_initial_move)
+    data.count--;
+
+  if (!graphene_point_near (&data.first, &data.last, 0.0001) && !data.has_close)
+    data.count++;
+
+  self->n_points = data.count * 3;
+  self->points = g_new0 (graphene_point_t, self->n_points);
+  self->point_data = g_new0 (PointData, data.count);
+
+  data2.editor = self;
+  data2.pos = 0;
+  gsk_path_foreach (path, copy_segments, &data2);
+
+  for (i = 0; i < self->n_points; i += 3)
+    update_smoothness (self, i);
+
   gtk_widget_queue_draw (GTK_WIDGET (self));
 }
 
+/* -------------------- */
+
+static GskPath *
+make_circle_path (void)
+{
+  float w = 200;
+  float h = 200;
+  float cx = w / 2;
+  float cy = h / 2;
+  float pad = 20;
+  float r = (w - 2 * pad) / 2;
+  float k = 0.55228;
+  float kr = k  * r;
+  GskPathBuilder *builder;
+
+  builder = gsk_path_builder_new ();
+
+  gsk_path_builder_move_to (builder,  cx, pad);
+  gsk_path_builder_curve_to (builder, cx + kr, pad,
+                                      w - pad, cy - kr,
+                                      w - pad, cy);
+  gsk_path_builder_curve_to (builder, w - pad, cy + kr,
+                                      cx + kr, h - pad,
+                                      cx, h - pad);
+  gsk_path_builder_curve_to (builder, cx - kr, h - pad,
+                                      pad, cy + kr,
+                                      pad, cy);
+  gsk_path_builder_curve_to (builder, pad, cy - kr,
+                                      cx - kr, pad,
+                                      cx, pad);
+
+  return gsk_path_builder_free_to_path (builder);
+}
+
+static void
+edit_changed (GtkToggleButton *button,
+              GParamSpec      *pspec,
+              CurveEditor     *editor)
+{
+  curve_editor_set_edit (editor, gtk_toggle_button_get_active (button));
+}
+
+static void
+reset (GtkButton   *button,
+       CurveEditor *editor)
+{
+  GskPath *path;
+
+  path = make_circle_path ();
+  curve_editor_set_path (editor, path);
+  gsk_path_unref (path);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -911,11 +1092,13 @@ main (int argc, char *argv[])
 
   gtk_window_set_titlebar (GTK_WINDOW (window), titlebar);
 
-  demo = demo_widget_new ();
+  demo = curve_editor_new ();
 
   g_signal_connect (edit_toggle, "notify::active", G_CALLBACK (edit_changed), demo);
   g_signal_connect (reset_button, "clicked", G_CALLBACK (reset), demo);
 
+  reset (NULL, CURVE_EDITOR (demo));
+
   gtk_window_set_child (window, demo);
 
   gtk_window_present (window);


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