[gimp/soc-2010-cage-2] Rewrite of the cage tool as a state machine



commit d195769b2399654c8ad5b31d9d9348fb57bc9327
Author: Michael Muré <batolettre gmail com>
Date:   Fri Dec 17 14:49:37 2010 +0100

    Rewrite of the cage tool as a state machine

 app/gegl/gimpcageconfig.c |   76 ++++++++++-
 app/gegl/gimpcageconfig.h |    7 +-
 app/tools/gimpcagetool.c  |  308 ++++++++++++++++++++++++++++++---------------
 app/tools/gimpcagetool.h  |    4 +
 4 files changed, 288 insertions(+), 107 deletions(-)
---
diff --git a/app/gegl/gimpcageconfig.c b/app/gegl/gimpcageconfig.c
index 08201ea..2df9c15 100644
--- a/app/gegl/gimpcageconfig.c
+++ b/app/gegl/gimpcageconfig.c
@@ -377,16 +377,69 @@ gimp_cage_config_reverse_cage_if_needed (GimpCageConfig *gcc)
 }
 
 /**
- * gimp_cage_config_point_inside:
+ * gimp_cage_config_get_cage_point:
  * @gcc: the cage config
- * @x: x coordinate of the point to test
- * @y: y coordinate of the point to test
+ * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM
  *
- * Check if the given point is inside the cage. This test is done in
- * the regard of the topological inside of the cage.
+ * Returns: a copy of the cage's point, for the cage mode provided
+ */
+GimpVector2*
+gimp_cage_config_get_cage_point (GimpCageConfig  *gcc,
+                                 GimpCageMode     mode)
+{
+  gint          x;
+  GimpVector2  *vertices_cage;
+  GimpVector2  *vertices_copy;
+
+  g_return_val_if_fail (GIMP_IS_CAGE_CONFIG (gcc), NULL);
+
+  vertices_copy = g_new (GimpVector2, gcc->n_cage_vertices);
+
+  if (mode == GIMP_CAGE_MODE_CAGE_CHANGE)
+    vertices_cage = gcc->cage_vertices;
+  else
+    vertices_cage = gcc->cage_vertices_d;
+
+  for(x = 0; x < gcc->n_cage_vertices; x++)
+    {
+      vertices_copy[x] = vertices_cage[x];
+    }
+
+  return vertices_copy;
+}
+
+/**
+ * gimp_cage_config_commit_cage_point:
+ * @gcc: the cage config
+ * @mode: the actual mode of the cage, GIMP_CAGE_MODE_CAGE_CHANGE or GIMP_CAGE_MODE_DEFORM
+ * @points: a GimpVector2 array of point of the same length as the number of point in the cage
  *
- * Returns: TRUE if the point is inside, FALSE if not.
+ * This function update the cage's point with the array provided.
  */
+void
+gimp_cage_config_commit_cage_point  (GimpCageConfig  *gcc,
+                                     GimpCageMode     mode,
+                                     GimpVector2     *points)
+{
+  gint          i;
+  GimpVector2  *vertices;
+
+  g_return_if_fail (GIMP_IS_CAGE_CONFIG (gcc));
+
+  if (mode == GIMP_CAGE_MODE_CAGE_CHANGE)
+    vertices = gcc->cage_vertices;
+  else
+    vertices = gcc->cage_vertices_d;
+
+  for(i = 0; i < gcc->n_cage_vertices; i++)
+    {
+      vertices[i] = points[i];
+    }
+
+  gimp_cage_config_compute_scaling_factor (gcc);
+  gimp_cage_config_compute_edge_normal (gcc);
+}
+
 static void
 gimp_cage_config_compute_scaling_factor (GimpCageConfig *gcc)
 {
@@ -434,6 +487,17 @@ gimp_cage_config_compute_edge_normal (GimpCageConfig *gcc)
     }
 }
 
+/**
+ * gimp_cage_config_point_inside:
+ * @gcc: the cage config
+ * @x: x coordinate of the point to test
+ * @y: y coordinate of the point to test
+ *
+ * Check if the given point is inside the cage. This test is done in
+ * the regard of the topological inside of the cage.
+ *
+ * Returns: TRUE if the point is inside, FALSE if not.
+ */
 gboolean
 gimp_cage_config_point_inside (GimpCageConfig *gcc,
                                gfloat          x,
diff --git a/app/gegl/gimpcageconfig.h b/app/gegl/gimpcageconfig.h
index fa5b085..c689fa9 100644
--- a/app/gegl/gimpcageconfig.h
+++ b/app/gegl/gimpcageconfig.h
@@ -72,6 +72,9 @@ void          gimp_cage_config_reverse_cage           (GimpCageConfig  *gcc);
 gboolean      gimp_cage_config_point_inside           (GimpCageConfig  *gcc,
                                                        gfloat           x,
                                                        gfloat           y);
-
-
+GimpVector2*  gimp_cage_config_get_cage_point         (GimpCageConfig  *gcc,
+                                                       GimpCageMode     mode);
+void          gimp_cage_config_commit_cage_point      (GimpCageConfig  *gcc,
+                                                       GimpCageMode     mode,
+                                                       GimpVector2     *points);
 #endif /* __GIMP_CAGE_CONFIG_H__ */
diff --git a/app/tools/gimpcagetool.c b/app/tools/gimpcagetool.c
index 4fbf343..6350377 100644
--- a/app/tools/gimpcagetool.c
+++ b/app/tools/gimpcagetool.c
@@ -107,7 +107,8 @@ static gint       gimp_cage_tool_is_on_handle       (GimpCageConfig        *gcc,
                                                      gdouble                y,
                                                      gint                   handle_size);
 
-static void       gimp_cage_tool_switch_to_deform   (GimpCageTool          *ct);
+static void       gimp_cage_tool_switch_to_deform   (GimpCageTool          *ct,
+                                                     GimpDisplay           *display);
 static void       gimp_cage_tool_remove_last_handle (GimpCageTool          *ct);
 static void       gimp_cage_tool_compute_coef       (GimpCageTool          *ct,
                                                      GimpDisplay           *display);
@@ -115,6 +116,7 @@ static void       gimp_cage_tool_create_image_map   (GimpCageTool          *ct,
                                                      GimpDrawable          *drawable);
 static void       gimp_cage_tool_image_map_flush    (GimpImageMap          *image_map,
                                                      GimpTool              *tool);
+static void       gimp_cage_tool_image_map_update   (GimpCageTool          *ct);
 
 static GeglNode * gimp_cage_tool_create_render_node (GimpCageTool          *ct);
 
@@ -123,6 +125,15 @@ G_DEFINE_TYPE (GimpCageTool, gimp_cage_tool, GIMP_TYPE_DRAW_TOOL)
 
 #define parent_class gimp_cage_tool_parent_class
 
+enum
+{
+  CAGE_STATE_INIT,
+  CAGE_STATE_WAIT,
+  CAGE_STATE_MOVE_HANDLE,
+  CAGE_STATE_CLOSING,
+  DEFORM_STATE_WAIT,
+  DEFORM_STATE_MOVE_HANDLE,
+};
 
 void
 gimp_cage_tool_register (GimpToolRegisterCallback  callback,
@@ -179,9 +190,15 @@ gimp_cage_tool_init (GimpCageTool *self)
   self->hovering_handle = -1;
   self->moving_handle   = -1;
   self->cage_complete   = FALSE;
+  self->tool_state      = CAGE_STATE_INIT;
 
   self->coef            = NULL;
   self->image_map       = NULL;
+  self->cage_backup     = NULL;
+
+  gimp_tool_control_set_wants_click (tool->control, TRUE);
+  gimp_tool_control_set_tool_cursor (tool->control,
+                                     GIMP_TOOL_CURSOR_PERSPECTIVE);
 }
 
 static void
@@ -244,6 +261,7 @@ gimp_cage_tool_start (GimpCageTool *ct,
   ct->hovering_handle = -1;
   ct->moving_handle   = -1;
   ct->cage_complete   = FALSE;
+  ct->tool_state      = CAGE_STATE_INIT;
 
   /* Setting up cage offset to convert the cage point coords to
    * drawable coords
@@ -266,14 +284,14 @@ gimp_cage_tool_key_press (GimpTool    *tool,
   switch (kevent->keyval)
     {
     case GDK_BackSpace:
-      if (! ct->cage_complete)
+      if (! ct->cage_complete && ct->tool_state == CAGE_STATE_WAIT)
         gimp_cage_tool_remove_last_handle (ct);
       return TRUE;
 
     case GDK_Return:
     case GDK_KP_Enter:
     case GDK_ISO_Enter:
-      if (ct->cage_complete)
+      if (ct->tool_state == DEFORM_STATE_WAIT)
         {
           gimp_tool_control_set_preserve (tool->control, TRUE);
 
@@ -307,13 +325,26 @@ gimp_cage_tool_motion (GimpTool         *tool,
                        GdkModifierType   state,
                        GimpDisplay      *display)
 {
-  GimpCageTool *ct = GIMP_CAGE_TOOL (tool);
+  GimpCageTool    *ct       = GIMP_CAGE_TOOL (tool);
+  GimpCageOptions *options  = GIMP_CAGE_TOOL_GET_OPTIONS (ct);
 
   gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
 
   ct->cursor_x = coords->x;
   ct->cursor_y = coords->y;
 
+  switch (ct->tool_state)
+    {
+      case CAGE_STATE_MOVE_HANDLE:
+      case DEFORM_STATE_MOVE_HANDLE:
+        gimp_cage_config_move_cage_point (ct->config,
+                                          options->cage_mode,
+                                          ct->moving_handle,
+                                          ct->cursor_x,
+                                          ct->cursor_y);
+        break;
+    }
+
   gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
 }
 
@@ -356,12 +387,85 @@ gimp_cage_tool_button_press (GimpTool            *tool,
                              GimpButtonPressType  press_type,
                              GimpDisplay         *display)
 {
-  GimpCageTool *ct = GIMP_CAGE_TOOL (tool);
+  GimpCageTool    *ct        = GIMP_CAGE_TOOL (tool);
+  GimpDrawTool    *draw_tool = GIMP_DRAW_TOOL (tool);
+  GimpCageOptions *options   = GIMP_CAGE_TOOL_GET_OPTIONS (ct);
+  gint             handle    = -1;
 
   if (display != tool->display)
     gimp_cage_tool_start (ct, display);
 
-  ct->moving_handle = ct->hovering_handle;
+  if (ct->config)
+      handle = gimp_cage_tool_is_on_handle (ct->config,
+                                            draw_tool,
+                                            display,
+                                            options->cage_mode,
+                                            coords->x,
+                                            coords->y,
+                                            GIMP_TOOL_HANDLE_SIZE_CIRCLE);
+
+  switch (ct->tool_state)
+    {
+      case CAGE_STATE_INIT:
+        /* No handle yet, we add the first one and swith the tool to moving handle state. */
+        gimp_cage_config_add_cage_point (ct->config,
+                                         ct->cursor_x, ct->cursor_y);
+        ct->moving_handle = 0;
+        ct->tool_state = CAGE_STATE_MOVE_HANDLE;
+        break;
+
+      case CAGE_STATE_WAIT:
+        if (ct->cage_complete == FALSE)
+          {
+            if (handle == -1)
+            /* User clicked on the background, we add a new handle and move it */
+              {
+                ct->moving_handle = ct->config->n_cage_vertices;
+                gimp_cage_config_add_cage_point (ct->config,
+                                                 ct->cursor_x, ct->cursor_y);
+                ct->tool_state = CAGE_STATE_MOVE_HANDLE;
+              }
+            if (handle == 0 && ct->config->n_cage_vertices > 2)
+            /* User clicked on the first handle, we wait for release for closing the cage and switching to deform if possible */
+              {
+                ct->tool_state = CAGE_STATE_CLOSING;
+              }
+            if (handle > 0)
+            /* User clicked on a handle, so we move it */
+              {
+                ct->moving_handle = handle;
+                ct->tool_state = CAGE_STATE_MOVE_HANDLE;
+              }
+          }
+        else /* Cage already closed */
+          {
+            if (handle >= 0)
+            /* User clicked on a handle, so we move it */
+              {
+                ct->moving_handle = handle;
+                ct->tool_state = CAGE_STATE_MOVE_HANDLE;
+              }
+          }
+        break;
+
+      case DEFORM_STATE_WAIT:
+        /* keep a copy of cage's point, in case of cancel when release */
+        if (ct->cage_backup)
+          {
+            g_free (ct->cage_backup);
+            ct->cage_backup = NULL;
+          }
+
+        ct->cage_backup = gimp_cage_config_get_cage_point (ct->config, options->cage_mode);
+
+        if (handle >= 0)
+        /* User clicked on a handle, so we move it */
+          {
+            ct->moving_handle = handle;
+            ct->tool_state = DEFORM_STATE_MOVE_HANDLE;
+          }
+        break;
+    }
 }
 
 void
@@ -377,83 +481,52 @@ gimp_cage_tool_button_release (GimpTool              *tool,
 
   gimp_draw_tool_pause (GIMP_DRAW_TOOL (ct));
 
-  if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
-    {
-      /* operation canceled, do nothing */
-    }
-  else if (ct->moving_handle == 0 && release_type == GIMP_BUTTON_RELEASE_CLICK)
-    {
-      /* user clicked on the first handle, we close the cage and
-       * switch to deform mode
-       */
-      if (ct->config->n_cage_vertices > 2 && ! ct->cage_complete)
-        {
-          GimpImage    *image    = gimp_display_get_image (display);
-          GimpDrawable *drawable = gimp_image_get_active_drawable (image);
-
-          ct->cage_complete = TRUE;
-          gimp_cage_tool_switch_to_deform (ct);
-
-          gimp_cage_config_reverse_cage_if_needed (ct->config);
-          gimp_cage_tool_compute_coef (ct, display);
-
-          gimp_cage_tool_create_image_map (ct, drawable);
-
-          gimp_tool_push_status (tool, display, _("Press ENTER to commit the transform"));
-        }
-    }
-  else if (ct->moving_handle == -1)
+  if (state & GDK_BUTTON3_MASK)
     {
-      /* user released outside any handles, add one if the cage is not
-       * complete yet
-       */
-      if (! ct->cage_complete)
+      switch(ct->tool_state)
         {
-          gimp_cage_config_add_cage_point (ct->config,
-                                           ct->cursor_x, ct->cursor_y);
+          case CAGE_STATE_CLOSING:
+            ct->tool_state = CAGE_STATE_WAIT;
+            break;
+
+          case CAGE_STATE_MOVE_HANDLE:
+            gimp_cage_config_remove_last_cage_point (ct->config);
+            ct->tool_state = CAGE_STATE_WAIT;
+            break;
+
+          case DEFORM_STATE_MOVE_HANDLE:
+            gimp_cage_config_commit_cage_point (ct->config,
+                                                options->cage_mode,
+                                                ct->cage_backup);
+            gimp_cage_tool_image_map_update (ct);
+            ct->tool_state = DEFORM_STATE_WAIT;
+            break;
         }
     }
   else
     {
-      /* user moved a handle
-       */
-      gimp_cage_config_move_cage_point (ct->config,
-                                        options->cage_mode,
-                                        ct->moving_handle,
-                                        ct->cursor_x,
-                                        ct->cursor_y);
-
-      if (ct->cage_complete)
+      switch(ct->tool_state)
         {
-          GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
-          GimpItem         *item  = GIMP_ITEM (tool->drawable);
-          gint              x, y;
-          gint              w, h;
-          gint              off_x, off_y;
-          GeglRectangle     visible;
-
-          gimp_display_shell_untransform_viewport (shell, &x, &y, &w, &h);
-
-          gimp_item_get_offset (item, &off_x, &off_y);
-
-          gimp_rectangle_intersect (x, y, w, h,
-                                    off_x,
-                                    off_y,
-                                    gimp_item_get_width  (item),
-                                    gimp_item_get_height (item),
-                                    &visible.x,
-                                    &visible.y,
-                                    &visible.width,
-                                    &visible.height);
-
-          visible.x -= off_x;
-          visible.y -= off_y;
-
-          gimp_image_map_apply (ct->image_map, &visible);
+          case CAGE_STATE_CLOSING:
+            gimp_cage_tool_switch_to_deform (ct, display);
+            break;
+
+          case CAGE_STATE_MOVE_HANDLE:
+            ct->tool_state = CAGE_STATE_WAIT;
+            break;
+
+          case DEFORM_STATE_MOVE_HANDLE:
+            ct->tool_state = DEFORM_STATE_WAIT;
+            gimp_cage_tool_image_map_update (ct);
+            break;
         }
     }
 
-  ct->moving_handle = -1;
+  if (ct->cage_backup)
+    {
+      g_free (ct->cage_backup);
+      ct->cage_backup = NULL;
+    }
 
   gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
 }
@@ -495,10 +568,11 @@ gimp_cage_tool_draw (GimpDrawTool *draw_tool)
   GimpVector2     *vertices;
   gint             n_vertices;
   gint             i;
+  GimpHandleType   handle;
 
   n_vertices = config->n_cage_vertices;
 
-  if (n_vertices < 1)
+  if (ct->tool_state == CAGE_STATE_INIT)
     return;
 
   stroke_group = gimp_draw_tool_add_stroke_group (draw_tool);
@@ -510,8 +584,8 @@ gimp_cage_tool_draw (GimpDrawTool *draw_tool)
 
   gimp_draw_tool_push_group (draw_tool, stroke_group);
 
-  if (! ct->cage_complete && (ct->hovering_handle == -1 ||
-                              (ct->hovering_handle == 0 && n_vertices > 2)))
+  /* If needed, draw ligne to the cursor. */
+  if (ct->tool_state == CAGE_STATE_WAIT || ct->tool_state == CAGE_STATE_MOVE_HANDLE)
     {
       gimp_draw_tool_add_line (draw_tool,
                                vertices[n_vertices - 1].x + ct->config->offset_x,
@@ -522,21 +596,13 @@ gimp_cage_tool_draw (GimpDrawTool *draw_tool)
 
   gimp_draw_tool_pop_group (draw_tool);
 
+  /* Draw the cage with handles. */
   for (i = 0; i < n_vertices; i++)
     {
-      GimpHandleType handle;
       gdouble        x1, y1;
 
-      if (i == ct->moving_handle)
-        {
-          x1 = ct->cursor_x;
-          y1 = ct->cursor_y;
-        }
-      else
-        {
-          x1 = vertices[i].x;
-          y1 = vertices[i].y;
-        }
+      x1 = vertices[i].x;
+      y1 = vertices[i].y;
 
       if (i > 0 || ct->cage_complete)
         {
@@ -548,16 +614,8 @@ gimp_cage_tool_draw (GimpDrawTool *draw_tool)
           else
             point2 = i - 1;
 
-          if (point2 == ct->moving_handle)
-            {
-              x2 = ct->cursor_x;
-              y2 = ct->cursor_y;
-            }
-          else
-            {
-              x2 = vertices[point2].x;
-              y2 = vertices[point2].y;
-            }
+          x2 = vertices[point2].x;
+          y2 = vertices[point2].y;
 
           gimp_draw_tool_push_group (draw_tool, stroke_group);
 
@@ -642,9 +700,24 @@ gimp_cage_tool_remove_last_handle (GimpCageTool *ct)
 }
 
 static void
-gimp_cage_tool_switch_to_deform (GimpCageTool *ct)
+gimp_cage_tool_switch_to_deform (GimpCageTool *ct,
+                                 GimpDisplay  *display)
 {
-  GimpCageOptions *options = GIMP_CAGE_TOOL_GET_OPTIONS (ct);
+  GimpTool        *tool     = GIMP_TOOL (ct);
+  GimpCageOptions *options  = GIMP_CAGE_TOOL_GET_OPTIONS (ct);
+  GimpImage       *image    = gimp_display_get_image (display);
+  GimpDrawable    *drawable = gimp_image_get_active_drawable (image);
+
+  ct->cage_complete = TRUE;
+
+  gimp_cage_config_reverse_cage_if_needed (ct->config);
+  gimp_cage_tool_compute_coef (ct, display);
+
+  gimp_cage_tool_create_image_map (ct, drawable);
+
+  gimp_tool_push_status (tool, display, _("Press ENTER to commit the transform"));
+
+  ct->tool_state = DEFORM_STATE_WAIT;
 
   g_object_set (options, "cage-mode", GIMP_CAGE_MODE_DEFORM, NULL);
 }
@@ -803,6 +876,37 @@ gimp_cage_tool_image_map_flush (GimpImageMap *image_map,
 }
 
 static void
+gimp_cage_tool_image_map_update (GimpCageTool *ct)
+{
+  GimpTool         *tool  = GIMP_TOOL (ct);
+  GimpDisplayShell *shell = gimp_display_get_shell (tool->display);
+  GimpItem         *item  = GIMP_ITEM (tool->drawable);
+  gint              x, y;
+  gint              w, h;
+  gint              off_x, off_y;
+  GeglRectangle     visible;
+
+  gimp_display_shell_untransform_viewport (shell, &x, &y, &w, &h);
+
+  gimp_item_get_offset (item, &off_x, &off_y);
+
+  gimp_rectangle_intersect (x, y, w, h,
+                            off_x,
+                            off_y,
+                            gimp_item_get_width  (item),
+                            gimp_item_get_height (item),
+                            &visible.x,
+                            &visible.y,
+                            &visible.width,
+                            &visible.height);
+
+  visible.x -= off_x;
+  visible.y -= off_y;
+
+  gimp_image_map_apply (ct->image_map, &visible);
+}
+
+static void
 gimp_cage_tool_halt (GimpCageTool *ct)
 {
   GimpTool     *tool      = GIMP_TOOL (ct);
@@ -839,5 +943,11 @@ gimp_cage_tool_halt (GimpCageTool *ct)
       gimp_image_flush (gimp_display_get_image (tool->display));
     }
 
+  if (ct->cage_backup)
+    {
+      g_free (ct->cage_backup);
+      ct->cage_backup = NULL;
+    }
+
   tool->display  = NULL;
 }
diff --git a/app/tools/gimpcagetool.h b/app/tools/gimpcagetool.h
index 249c191..99279cb 100644
--- a/app/tools/gimpcagetool.h
+++ b/app/tools/gimpcagetool.h
@@ -52,7 +52,11 @@ struct _GimpCageTool
 
   GeglBuffer     *coef;
 
+  gint            tool_state;
+
   GimpImageMap   *image_map;
+
+  GimpVector2    *cage_backup;
 };
 
 struct _GimpCageToolClass



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