[gimp/soc-2010-cage-2] Rewrite of the cage tool as a state machine
- From: Michael Muré <mmure src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/soc-2010-cage-2] Rewrite of the cage tool as a state machine
- Date: Thu, 30 Dec 2010 18:15:07 +0000 (UTC)
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]