gimp r25693 - in trunk: . app/tools
- From: martinn svn gnome org
- To: svn-commits-list gnome org
- Subject: gimp r25693 - in trunk: . app/tools
- Date: Sun, 18 May 2008 11:39:17 +0000 (UTC)
Author: martinn
Date: Sun May 18 11:39:17 2008
New Revision: 25693
URL: http://svn.gnome.org/viewvc/gimp?rev=25693&view=rev
Log:
2008-05-18 Martin Nordholts <martinn svn gnome org>
Merged the Polygon Select Tool capabilities with the Free Select
Tool. Among the things still to do is improved help texts, more
suitable graphics, and cursor mangement, but the core
functionality is in place. Thanks to Alexia Death for initial
testing. It will also be neccesary to do some work to adapt the
Foreground Select Tool to the new Free Select Tool implementation.
Quick instructions on how the tool works:
o A click adds a polygonal segment, a drag adds a free-hand
segment
o Return-key commits, Escape-key cancels, Backspace-key removes
last segment
o You can grab and move segment vertices
o You can cancel both a move, and the creation of a segment
* app/tools/gimpfreeselecttool.[ch]: More or less
reimplemented. We keep a sequential array of all the points in the
polygon (including the free segments), and we have another array
with point indices that we use to represent the segments. On top
of this we have a few helper functions that tries to abstract away
the pretty raw nature of the implementation.
* app/tools/gimpforegroundselecttool.[ch]: Keep track on its own
last_coord, and adjust to the new implementation of the Free
Select Tool. Still needs some work, for example handling that the
Free Select Tool now resets GimpTool::display.
(gimp_foreground_select_tool_key_press): Pass on key event to
parent class when appropriate. (Bails out too early though...)
Modified:
trunk/ChangeLog
trunk/app/tools/gimpforegroundselecttool.c
trunk/app/tools/gimpforegroundselecttool.h
trunk/app/tools/gimpfreeselecttool.c
trunk/app/tools/gimpfreeselecttool.h
Modified: trunk/app/tools/gimpforegroundselecttool.c
==============================================================================
--- trunk/app/tools/gimpforegroundselecttool.c (original)
+++ trunk/app/tools/gimpforegroundselecttool.c Sun May 18 11:39:17 2008
@@ -292,7 +292,7 @@
if (fg_select->mask && gimp_draw_tool_is_active (draw_tool))
gimp_draw_tool_stop (draw_tool);
- GIMP_FREE_SELECT_TOOL (tool)->last_coords = *coords;
+ fg_select->last_coords = *coords;
GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
display);
@@ -396,20 +396,29 @@
if (display != tool->display)
return FALSE;
- switch (kevent->keyval)
+ if (fg_select->state)
{
- case GDK_Return:
- case GDK_KP_Enter:
- case GDK_ISO_Enter:
- gimp_foreground_select_tool_apply (fg_select, display);
- return TRUE;
-
- case GDK_Escape:
- gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
- return TRUE;
+ switch (kevent->keyval)
+ {
+ case GDK_Return:
+ case GDK_KP_Enter:
+ case GDK_ISO_Enter:
+ gimp_foreground_select_tool_apply (fg_select, display);
+ return TRUE;
+
+ case GDK_Escape:
+ gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, display);
+ return TRUE;
- default:
- return FALSE;
+ default:
+ return FALSE;
+ }
+ }
+ else
+ {
+ return GIMP_TOOL_CLASS (parent_class)->key_press (tool,
+ kevent,
+ display);
}
}
@@ -434,7 +443,7 @@
gimp_tool_control_activate (tool->control);
- GIMP_FREE_SELECT_TOOL (tool)->last_coords = *coords;
+ fg_select->last_coords = *coords;
g_return_if_fail (fg_select->stroke == NULL);
fg_select->stroke = g_array_new (FALSE, FALSE, sizeof (GimpVector2));
@@ -505,7 +514,7 @@
gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
- GIMP_FREE_SELECT_TOOL (tool)->last_coords = *coords;
+ fg_select->last_coords = *coords;
if (last->x != (gint) coords->x || last->y != (gint) coords->y)
{
@@ -567,10 +576,9 @@
if (fg_select->mask)
{
- GimpFreeSelectTool *sel = GIMP_FREE_SELECT_TOOL (tool);
GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (draw_tool->display->shell);
- gint x = sel->last_coords.x;
- gint y = sel->last_coords.y;
+ gint x = fg_select->last_coords.x;
+ gint y = fg_select->last_coords.y;
gdouble radius;
radius = (options->stroke_width / shell->scale_y) / 2;
@@ -630,7 +638,9 @@
scan_convert = gimp_scan_convert_new ();
gimp_scan_convert_add_polyline (scan_convert,
- free_sel->num_points, free_sel->points, TRUE);
+ free_sel->n_points,
+ free_sel->points,
+ TRUE);
mask = gimp_channel_new (image,
gimp_image_get_width (image),
Modified: trunk/app/tools/gimpforegroundselecttool.h
==============================================================================
--- trunk/app/tools/gimpforegroundselecttool.h (original)
+++ trunk/app/tools/gimpforegroundselecttool.h Sun May 18 11:39:17 2008
@@ -40,6 +40,7 @@
{
GimpFreeSelectTool parent_instance;
+ GimpCoords last_coords;
guint idle_id;
GArray *stroke;
GList *strokes;
Modified: trunk/app/tools/gimpfreeselecttool.c
==============================================================================
--- trunk/app/tools/gimpfreeselecttool.c (original)
+++ trunk/app/tools/gimpfreeselecttool.c Sun May 18 11:39:17 2008
@@ -1,6 +1,9 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
+ * Major improvement to support polygonal segments
+ * Copyright (C) 2008 Martin Nordholts
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -18,7 +21,10 @@
#include "config.h"
+#include <string.h>
+
#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
#include "libgimpmath/gimpmath.h"
#include "libgimpwidgets/gimpwidgets.h"
@@ -41,42 +47,47 @@
#include "gimp-intl.h"
-#define DEFAULT_MAX_INC 1024
-
-
-static void gimp_free_select_tool_finalize (GObject *object);
-
-static void gimp_free_select_tool_control (GimpTool *tool,
- GimpToolAction action,
- GimpDisplay *display);
-static void gimp_free_select_tool_button_press (GimpTool *tool,
- GimpCoords *coords,
- guint32 time,
- GdkModifierType state,
- GimpDisplay *display);
-static void gimp_free_select_tool_button_release (GimpTool *tool,
- GimpCoords *coords,
- guint32 time,
- GdkModifierType state,
- GimpButtonReleaseType release_type,
- GimpDisplay *display);
-static void gimp_free_select_tool_motion (GimpTool *tool,
- GimpCoords *coords,
- guint32 time,
- GdkModifierType state,
- GimpDisplay *display);
-
-static void gimp_free_select_tool_draw (GimpDrawTool *draw_tool);
-
-static void gimp_free_select_tool_real_select (GimpFreeSelectTool *free_sel,
- GimpDisplay *display);
-
-static void gimp_free_select_tool_add_point (GimpFreeSelectTool *free_sel,
- gdouble x,
- gdouble y);
-static void gimp_free_select_tool_move_points (GimpFreeSelectTool *free_sel,
- gdouble dx,
- gdouble dy);
+#define HANDLE_SIZE 10
+#define POINT_GRAB_THRESHOLD_SQ SQR(HANDLE_SIZE / 2)
+#define N_ITEMS_PER_ALLOC 1024
+#define INVALID_INDEX (-1)
+
+
+static void gimp_free_select_tool_finalize (GObject *object);
+static void gimp_free_select_tool_control (GimpTool *tool,
+ GimpToolAction action,
+ GimpDisplay *display);
+static void gimp_free_select_tool_oper_update (GimpTool *tool,
+ GimpCoords *coords,
+ GdkModifierType state,
+ gboolean proximity,
+ GimpDisplay *display);
+static void gimp_free_select_tool_cursor_update (GimpTool *tool,
+ GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display);
+static void gimp_free_select_tool_button_press (GimpTool *tool,
+ GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpDisplay *display);
+static void gimp_free_select_tool_button_release (GimpTool *tool,
+ GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonReleaseType release_type,
+ GimpDisplay *display);
+static void gimp_free_select_tool_motion (GimpTool *tool,
+ GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpDisplay *display);
+static gboolean gimp_free_select_tool_key_press (GimpTool *tool,
+ GdkEventKey *kevent,
+ GimpDisplay *display);
+static void gimp_free_select_tool_draw (GimpDrawTool *draw_tool);
+static void gimp_free_select_tool_real_select (GimpFreeSelectTool *fst,
+ GimpDisplay *display);
G_DEFINE_TYPE (GimpFreeSelectTool, gimp_free_select_tool,
@@ -85,6 +96,9 @@
#define parent_class gimp_free_select_tool_parent_class
+static const GimpVector2 vector2_zero = { 0.0, 0.0 };
+
+
void
gimp_free_select_tool_register (GimpToolRegisterCallback callback,
gpointer data)
@@ -95,7 +109,7 @@
0,
"gimp-free-select-tool",
_("Free Select"),
- _("Free Select Tool: Select a hand-drawn region"),
+ _("Free Select Tool: Select a hand-drawn region with free and polygonal segments"),
N_("_Free Select"), "F",
NULL, GIMP_HELP_TOOL_FREE_SELECT,
GIMP_STOCK_TOOL_FREE_SELECT,
@@ -112,9 +126,12 @@
object_class->finalize = gimp_free_select_tool_finalize;
tool_class->control = gimp_free_select_tool_control;
+ tool_class->oper_update = gimp_free_select_tool_oper_update;
+ tool_class->cursor_update = gimp_free_select_tool_cursor_update;
tool_class->button_press = gimp_free_select_tool_button_press;
tool_class->button_release = gimp_free_select_tool_button_release;
tool_class->motion = gimp_free_select_tool_motion;
+ tool_class->key_press = gimp_free_select_tool_key_press;
draw_tool_class->draw = gimp_free_select_tool_draw;
@@ -122,32 +139,597 @@
}
static void
-gimp_free_select_tool_init (GimpFreeSelectTool *free_select)
+gimp_free_select_tool_init (GimpFreeSelectTool *fst)
{
- GimpTool *tool = GIMP_TOOL (free_select);
+ GimpTool *tool = GIMP_TOOL (fst);
gimp_tool_control_set_scroll_lock (tool->control, FALSE);
gimp_tool_control_set_wants_click (tool->control, TRUE);
gimp_tool_control_set_tool_cursor (tool->control,
GIMP_TOOL_CURSOR_FREE_SELECT);
- free_select->points = NULL;
- free_select->num_points = 0;
- free_select->max_segs = 0;
+ fst->grabbed_segment_index = INVALID_INDEX;
+
+ fst->button1_down = FALSE;
+
+ fst->saved_points_lower_segment = NULL;
+ fst->saved_points_higher_segment = NULL;
+ fst->n_saved_points_lower_segment = 0;
+ fst->n_saved_points_higher_segment = 0;
+
+ fst->polygon_modified = FALSE;
+
+ fst->show_pending_point = FALSE;
+
+ fst->points = NULL;
+ fst->n_points = 0;
+ fst->max_n_points = 0;
+
+ fst->segment_indices = NULL;
+ fst->n_segment_indices = 0;
+ fst->max_n_segment_indices = 0;
}
static void
gimp_free_select_tool_finalize (GObject *object)
{
- GimpFreeSelectTool *free_sel = GIMP_FREE_SELECT_TOOL (object);
+ GimpFreeSelectTool *fst = GIMP_FREE_SELECT_TOOL (object);
+
+ g_free (fst->points);
+
+ fst->points = NULL;
+ fst->n_points = 0;
+ fst->max_n_points = 0;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gimp_free_select_get_segment (GimpFreeSelectTool *fst,
+ GimpVector2 **points,
+ gint *n_points,
+ gint segment_start,
+ gint segment_end)
+{
+ *points = &fst->points[fst->segment_indices[segment_start]];
+ *n_points = fst->segment_indices[segment_end] -
+ fst->segment_indices[segment_start] +
+ 1;
+}
+
+static GimpVector2
+gimp_free_select_tool_get_grabbed_point (GimpFreeSelectTool *fst)
+{
+ return fst->points[fst->segment_indices[fst->grabbed_segment_index]];
+}
+
+static void
+gimp_free_select_tool_cleanup_after_move (GimpFreeSelectTool *fst)
+{
+ g_free (fst->saved_points_lower_segment);
+ fst->saved_points_lower_segment = NULL;
+
+ g_free (fst->saved_points_higher_segment);
+ fst->saved_points_higher_segment = NULL;
+}
+
+
+static gboolean
+gimp_free_select_tool_should_close (GimpFreeSelectTool *fst,
+ GimpDisplay *display,
+ GimpCoords *coords)
+{
+ gdouble dist;
+
+ if (fst->polygon_modified ||
+ fst->n_segment_indices <= 1)
+ return FALSE;
+
+ dist = gimp_draw_tool_calc_distance_square (GIMP_DRAW_TOOL (fst),
+ display,
+ coords->x,
+ coords->y,
+ fst->points[0].x,
+ fst->points[0].y);
+
+ return dist < POINT_GRAB_THRESHOLD_SQ;
+}
+
+static void
+gimp_free_select_tool_select_closest_segment_point (GimpFreeSelectTool *fst,
+ GimpDisplay *display,
+ GimpCoords *coords)
+{
+ GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (fst);
+ gdouble shortest_dist = POINT_GRAB_THRESHOLD_SQ;
+ gint grabbed_segment_index = INVALID_INDEX;
+ gint i;
- if (free_sel->points)
+ for (i = 0; i < fst->n_segment_indices; i++)
{
- g_free (free_sel->points);
- free_sel->points = NULL;
+ gdouble dist;
+ GimpVector2 *point;
+
+ point = &fst->points[fst->segment_indices[i]];
+
+ dist = gimp_draw_tool_calc_distance_square (draw_tool,
+ display,
+ coords->x,
+ coords->y,
+ point->x,
+ point->y);
+
+ if (dist < shortest_dist)
+ {
+ grabbed_segment_index = i;
+ }
}
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ if (grabbed_segment_index != fst->grabbed_segment_index)
+ {
+ gimp_draw_tool_pause(draw_tool);
+
+ fst->grabbed_segment_index = grabbed_segment_index;
+
+ gimp_draw_tool_resume(draw_tool);
+ }
+}
+
+static void
+gimp_free_select_tool_halt (GimpFreeSelectTool *fst)
+{
+ GimpTool *tool = GIMP_TOOL (fst);
+ GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (fst);
+
+ if (gimp_draw_tool_is_active (draw_tool))
+ gimp_draw_tool_stop (draw_tool);
+
+ if (gimp_tool_control_is_active (tool->control))
+ gimp_tool_control_halt (tool->control);
+
+ fst->grabbed_segment_index = INVALID_INDEX;
+ fst->show_pending_point = FALSE;
+ fst->n_points = 0;
+ fst->n_segment_indices = 0;
+
+ tool->display = NULL;
+}
+
+static void
+gimp_free_select_tool_revert_to_last_segment (GimpFreeSelectTool *fst)
+{
+ fst->n_points = fst->segment_indices[fst->n_segment_indices - 1] + 1;
+}
+
+static void
+gimp_free_select_tool_update_button_state (GimpFreeSelectTool *fst,
+ GdkModifierType state)
+{
+ fst->button1_down = state & GDK_BUTTON1_MASK ? TRUE : FALSE;
+}
+
+static void
+gimp_free_select_tool_remove_last_segment (GimpFreeSelectTool *fst)
+{
+ GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (fst);
+
+ gimp_draw_tool_pause (draw_tool);
+
+ fst->n_segment_indices--;
+
+ if (fst->n_segment_indices == 0)
+ {
+ gimp_free_select_tool_halt (fst);
+ }
+ else
+ {
+ gimp_free_select_tool_revert_to_last_segment (fst);
+ }
+
+ gimp_draw_tool_resume (draw_tool);
+}
+
+static void
+gimp_free_select_tool_add_point (GimpFreeSelectTool *fst,
+ gdouble x,
+ gdouble y)
+{
+ if (fst->n_points >= fst->max_n_points)
+ {
+ fst->max_n_points += N_ITEMS_PER_ALLOC;
+
+ fst->points = g_realloc (fst->points,
+ sizeof (GimpVector2) * fst->max_n_points);
+ }
+
+ fst->points[fst->n_points].x = x;
+ fst->points[fst->n_points].y = y;
+
+ fst->n_points++;
+}
+
+static void
+gimp_free_select_tool_add_segment_index (GimpFreeSelectTool *fst,
+ gint index)
+{
+ if (fst->n_segment_indices >= fst->max_n_segment_indices)
+ {
+ fst->max_n_segment_indices += N_ITEMS_PER_ALLOC;
+
+ fst->segment_indices = g_realloc (fst->segment_indices,
+ sizeof (GimpVector2) * fst->max_n_segment_indices);
+ }
+
+ fst->segment_indices[fst->n_segment_indices] = index;
+
+ fst->n_segment_indices++;
+}
+
+static gboolean
+gimp_free_select_tool_is_point_grabbed (GimpFreeSelectTool *fst)
+{
+ return fst->grabbed_segment_index != INVALID_INDEX;
+}
+
+static void
+gimp_free_select_tool_start (GimpFreeSelectTool *fst,
+ GimpCoords *coords,
+ GimpDisplay *display)
+{
+ GimpTool *tool = GIMP_TOOL (fst);
+ GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool);
+
+ gimp_free_select_tool_halt (fst);
+
+ gimp_tool_control_activate (tool->control);
+
+ tool->display = display;
+
+ gimp_draw_tool_start (draw_tool, display);
+
+ gimp_selection_tool_start_edit (GIMP_SELECTION_TOOL (fst),
+ coords);
+}
+
+static void
+gimp_free_select_tool_fit_segment (GimpFreeSelectTool *fst,
+ GimpVector2 *dest_points,
+ GimpVector2 dest_start_target,
+ GimpVector2 dest_end_target,
+ const GimpVector2 *source_points,
+ gint n_points)
+{
+ GimpVector2 origo_translation_offset;
+ GimpVector2 untranslation_offset;
+ gdouble rotation;
+ gdouble scale;
+
+ /* Handle some quick special cases */
+ if (n_points <= 0)
+ {
+ return;
+ }
+ else if (n_points == 1)
+ {
+ dest_points[0] = dest_end_target;
+ return;
+ }
+ else if (n_points == 2)
+ {
+ dest_points[0] = dest_start_target;
+ dest_points[1] = dest_end_target;
+ return;
+ }
+
+ /* Copy from source to dest; we work on the dest data */
+ memcpy (dest_points, source_points, sizeof (GimpVector2) * n_points);
+
+ /* Transform the destination end point */
+ {
+ GimpVector2 *dest_end;
+ GimpVector2 origo_translated_end_target;
+ gdouble target_rotation;
+ gdouble current_rotation;
+ gdouble target_length;
+ gdouble current_length;
+
+ dest_end = &dest_points[n_points - 1];
+
+ /* Transate to origin */
+ gimp_vector2_sub (&origo_translation_offset,
+ &vector2_zero,
+ &dest_points[0]);
+ gimp_vector2_add (dest_end,
+ dest_end,
+ &origo_translation_offset);
+
+ /* Calculate origo_translated_end_target */
+ gimp_vector2_sub (&origo_translated_end_target,
+ &dest_end_target,
+ &dest_start_target);
+
+ /* Rotate */
+ target_rotation = atan2 (vector2_zero.y - origo_translated_end_target.y,
+ vector2_zero.x - origo_translated_end_target.x);
+ current_rotation = atan2 (vector2_zero.y - dest_end->y,
+ vector2_zero.x - dest_end->x);
+ rotation = current_rotation - target_rotation;
+
+ gimp_vector2_rotate (dest_end, rotation);
+
+
+ /* Scale */
+ target_length = gimp_vector2_length (&origo_translated_end_target);
+ current_length = gimp_vector2_length (dest_end);
+ scale = target_length / current_length;
+
+ gimp_vector2_mul (dest_end, scale);
+
+
+ /* Untranslate */
+ gimp_vector2_sub (&untranslation_offset,
+ &dest_end_target,
+ dest_end);
+ gimp_vector2_add (dest_end,
+ dest_end,
+ &untranslation_offset);
+ }
+
+ /* Do the same transformation for the rest of the points */
+ {
+ gint i;
+
+ for (i = 0; i < n_points - 1; i++)
+ {
+ /* Translate */
+ gimp_vector2_add (&dest_points[i],
+ &origo_translation_offset,
+ &dest_points[i]);
+
+ /* Rotate */
+ gimp_vector2_rotate (&dest_points[i],
+ rotation);
+
+ /* Scale */
+ gimp_vector2_mul (&dest_points[i],
+ scale);
+
+ /* Untranslate */
+ gimp_vector2_add (&dest_points[i],
+ &dest_points[i],
+ &untranslation_offset);
+ }
+ }
+}
+
+static void
+gimp_free_select_tool_move_segment_vertex_to (GimpFreeSelectTool *fst,
+ gint segment_index,
+ gdouble new_x,
+ gdouble new_y)
+{
+ GimpVector2 cursor_point = { new_x, new_y };
+ GimpVector2 *dest;
+ GimpVector2 *dest_start_target;
+ GimpVector2 *dest_end_target;
+ gint n_points;
+
+ /* Handle the segment before the grabbed point */
+ if (segment_index > 0)
+ {
+ gimp_free_select_get_segment (fst,
+ &dest,
+ &n_points,
+ fst->grabbed_segment_index - 1,
+ fst->grabbed_segment_index);
+
+ dest_start_target = &dest[0];
+ dest_end_target = &cursor_point;
+
+ gimp_free_select_tool_fit_segment (fst,
+ dest,
+ *dest_start_target,
+ *dest_end_target,
+ fst->saved_points_lower_segment,
+ n_points);
+ }
+
+ /* Handle the segment after the grabbed point */
+ if (segment_index < fst->n_segment_indices - 1)
+ {
+ gimp_free_select_get_segment (fst,
+ &dest,
+ &n_points,
+ fst->grabbed_segment_index,
+ fst->grabbed_segment_index + 1);
+
+ dest_start_target = &cursor_point;
+ dest_end_target = &dest[n_points - 1];
+
+ gimp_free_select_tool_fit_segment (fst,
+ dest,
+ *dest_start_target,
+ *dest_end_target,
+ fst->saved_points_higher_segment,
+ n_points);
+ }
+}
+
+/**
+ * gimp_free_select_tool_finish_line_segment:
+ * @free_ploy_sel_tool:
+ * @end_x:
+ * @end_y:
+ *
+ * Adds a line segment. Also cancels a pending free segment if any.
+ **/
+static void
+gimp_free_select_tool_finish_line_segment (GimpFreeSelectTool *fst)
+{
+ /* Revert any free segment points that might have been added */
+ gimp_free_select_tool_revert_to_last_segment (fst);
+}
+
+/**
+ * gimp_free_select_tool_finish_free_segment:
+ * @fst:
+ *
+ * Finnishes off the creation of a free segment.
+ **/
+static void
+gimp_free_select_tool_finish_free_segment (GimpFreeSelectTool *fst)
+{
+ /* The points are all setup, just make a segment */
+ gimp_free_select_tool_add_segment_index (fst,
+ fst->n_points - 1);
+}
+
+static void
+gimp_free_select_tool_commit (GimpFreeSelectTool *fst,
+ GimpDisplay *display)
+{
+ if (fst->n_points >= 3)
+ {
+ gimp_free_select_tool_select (fst, display);
+ }
+
+ gimp_free_select_tool_halt (fst);
+
+ gimp_image_flush (display->image);
+}
+
+static void
+gimp_free_select_tool_handle_click (GimpFreeSelectTool *fst,
+ GimpCoords *coords,
+ GimpDisplay *display)
+{
+ /* First finish of the line segment if no point was grabbed */
+ if (! gimp_free_select_tool_is_point_grabbed (fst))
+ {
+ gimp_free_select_tool_finish_line_segment (fst);
+ }
+
+ /* After the segments are up to date, see if it's commiting time */
+ if (gimp_free_select_tool_should_close (fst,
+ display,
+ coords))
+ {
+ gimp_free_select_tool_commit (fst, display);
+ }
+}
+
+static void
+gimp_free_select_tool_handle_normal_release (GimpFreeSelectTool *fst,
+ GimpCoords *coords,
+ GimpDisplay *display)
+{
+ /* First finish of the free segment if no point was grabbed */
+ if (! gimp_free_select_tool_is_point_grabbed (fst))
+ {
+ gimp_free_select_tool_finish_free_segment (fst);
+ }
+
+ /* After the segments are up to date, see if it's commiting time */
+ if (gimp_free_select_tool_should_close (fst,
+ display,
+ coords))
+ {
+ gimp_free_select_tool_commit (fst, display);
+ }
+}
+
+static void
+gimp_free_select_tool_revert_to_saved_state (GimpFreeSelectTool *fst)
+{
+ GimpVector2 *source;
+ gint n_points;
+
+ if (fst->grabbed_segment_index > 0)
+ {
+ gimp_free_select_get_segment (fst,
+ &source,
+ &n_points,
+ fst->grabbed_segment_index - 1,
+ fst->grabbed_segment_index);
+
+ memcpy (source,
+ fst->saved_points_lower_segment,
+ sizeof (GimpVector2) * n_points);
+ }
+
+ if (fst->grabbed_segment_index < fst->n_segment_indices - 1)
+ {
+ gimp_free_select_get_segment (fst,
+ &source,
+ &n_points,
+ fst->grabbed_segment_index,
+ fst->grabbed_segment_index + 1);
+
+ memcpy (source,
+ fst->saved_points_higher_segment,
+ sizeof (GimpVector2) * n_points);
+ }
+}
+
+static void
+gimp_free_select_tool_handle_cancel (GimpFreeSelectTool *fst)
+{
+ if (gimp_free_select_tool_is_point_grabbed (fst))
+ {
+ gimp_free_select_tool_revert_to_saved_state (fst);
+ }
+ else
+ {
+ gimp_free_select_tool_remove_last_segment (fst);
+ }
+}
+
+void
+gimp_free_select_tool_select (GimpFreeSelectTool *fst,
+ GimpDisplay *display)
+{
+ g_return_if_fail (GIMP_IS_FREE_SELECT_TOOL (fst));
+ g_return_if_fail (GIMP_IS_DISPLAY (display));
+
+ GIMP_FREE_SELECT_TOOL_GET_CLASS (fst)->select (fst,
+ display);
+}
+
+static void
+gimp_free_select_tool_prepare_for_move (GimpFreeSelectTool *fst)
+{
+ GimpVector2 *source;
+ gint n_points;
+
+ if (fst->grabbed_segment_index > 0)
+ {
+ gimp_free_select_get_segment (fst,
+ &source,
+ &n_points,
+ fst->grabbed_segment_index - 1,
+ fst->grabbed_segment_index);
+
+ fst->saved_points_lower_segment = g_new0 (GimpVector2, n_points);
+
+ memcpy (fst->saved_points_lower_segment,
+ source,
+ sizeof (GimpVector2) * n_points);
+ }
+
+ if (fst->grabbed_segment_index < fst->n_segment_indices - 1)
+ {
+ gimp_free_select_get_segment (fst,
+ &source,
+ &n_points,
+ fst->grabbed_segment_index,
+ fst->grabbed_segment_index + 1);
+
+ fst->saved_points_higher_segment = g_new0 (GimpVector2, n_points);
+
+ memcpy (fst->saved_points_higher_segment,
+ source,
+ sizeof (GimpVector2) * n_points);
+ }
}
static void
@@ -162,7 +744,7 @@
break;
case GIMP_TOOL_ACTION_HALT:
- GIMP_FREE_SELECT_TOOL (tool)->num_points = 0;
+ gimp_free_select_tool_halt (GIMP_FREE_SELECT_TOOL (tool));
break;
}
@@ -170,26 +752,116 @@
}
static void
+gimp_free_select_tool_oper_update (GimpTool *tool,
+ GimpCoords *coords,
+ GdkModifierType state,
+ gboolean proximity,
+ GimpDisplay *display)
+{
+ GimpFreeSelectTool *fst;
+ gboolean hovering_first_point;
+
+ if (tool->display != display)
+ return;
+
+ fst = GIMP_FREE_SELECT_TOOL (tool);
+
+ gimp_free_select_tool_select_closest_segment_point (fst,
+ display,
+ coords);
+ hovering_first_point =
+ gimp_free_select_tool_should_close (fst,
+ display,
+ coords);
+
+ gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
+
+ if (fst->n_points == 0 ||
+ (gimp_free_select_tool_is_point_grabbed (fst) &&
+ ! hovering_first_point))
+ {
+ fst->show_pending_point = FALSE;
+ }
+ else
+ {
+ fst->show_pending_point = TRUE;
+
+ if (hovering_first_point)
+ {
+ fst->pending_point = fst->points[0];
+ }
+ else
+ {
+ fst->pending_point.x = coords->x;
+ fst->pending_point.y = coords->y;
+ }
+ }
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+}
+
+static void
+gimp_free_select_tool_cursor_update (GimpTool *tool,
+ GimpCoords *coords,
+ GdkModifierType state,
+ GimpDisplay *display)
+{
+ GimpFreeSelectTool *fst = GIMP_FREE_SELECT_TOOL (tool);
+
+ if (gimp_free_select_tool_is_point_grabbed (fst) &&
+ ! gimp_free_select_tool_should_close (fst, display, coords))
+ {
+ gimp_tool_set_cursor (tool, display,
+ gimp_tool_control_get_cursor (tool->control),
+ gimp_tool_control_get_tool_cursor (tool->control),
+ GIMP_CURSOR_MODIFIER_MOVE);
+
+ return;
+ }
+
+ GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
+}
+
+static void
gimp_free_select_tool_button_press (GimpTool *tool,
GimpCoords *coords,
guint32 time,
GdkModifierType state,
GimpDisplay *display)
{
- GimpFreeSelectTool *free_sel = GIMP_FREE_SELECT_TOOL (tool);
+ GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool);
+ GimpFreeSelectTool *fst = GIMP_FREE_SELECT_TOOL (tool);
- gimp_tool_control_activate (tool->control);
- tool->display = display;
+ gimp_draw_tool_pause (draw_tool);
- if (gimp_selection_tool_start_edit (GIMP_SELECTION_TOOL (free_sel), coords))
- return;
+ gimp_free_select_tool_update_button_state (fst, state);
- free_sel->last_coords = *coords;
- free_sel->num_points = 0;
+ if (display != tool->display)
+ {
+ gimp_free_select_tool_start (fst,
+ coords,
+ display);
+ }
- gimp_free_select_tool_add_point (free_sel, coords->x, coords->y);
+ if (gimp_free_select_tool_is_point_grabbed (fst))
+ {
+ gimp_free_select_tool_prepare_for_move (fst);
+ }
+ else
+ {
+ /* No point was grabbed, add a new point and mark this as a
+ * segment divider. For a line segment, this will be the only
+ * new point. For a free segment, this will be the first point
+ * of the free segment.
+ */
+ gimp_free_select_tool_add_point (fst,
+ coords->x,
+ coords->y);
+ gimp_free_select_tool_add_segment_index (fst,
+ fst->n_points - 1);
+ }
- gimp_draw_tool_start (GIMP_DRAW_TOOL (tool), display);
+ gimp_draw_tool_resume (draw_tool);
}
static void
@@ -200,37 +872,51 @@
GimpButtonReleaseType release_type,
GimpDisplay *display)
{
- GimpFreeSelectTool *free_sel = GIMP_FREE_SELECT_TOOL (tool);
+ GimpFreeSelectTool *fst = GIMP_FREE_SELECT_TOOL (tool);
+
+ if (tool->display != display)
+ return;
- gimp_draw_tool_stop (GIMP_DRAW_TOOL (tool));
+ gimp_draw_tool_pause (GIMP_DRAW_TOOL (fst));
- gimp_tool_control_halt (tool->control);
+ gimp_free_select_tool_update_button_state (fst, state);
switch (release_type)
{
- case GIMP_BUTTON_RELEASE_NORMAL:
- GIMP_FREE_SELECT_TOOL_GET_CLASS (free_sel)->select (free_sel, display);
+ case GIMP_BUTTON_RELEASE_CLICK:
+ case GIMP_BUTTON_RELEASE_NO_MOTION:
+ /* If a click was made, we don't consider the polygon modified */
+ fst->polygon_modified = FALSE;
+
+ gimp_free_select_tool_handle_click (fst,
+ coords,
+ display);
break;
- case GIMP_BUTTON_RELEASE_CANCEL:
- return;
+ case GIMP_BUTTON_RELEASE_NORMAL:
+ gimp_free_select_tool_handle_normal_release (fst,
+ coords,
+ display);
- case GIMP_BUTTON_RELEASE_CLICK:
- case GIMP_BUTTON_RELEASE_NO_MOTION:
- if (gimp_image_floating_sel (display->image))
- {
- /* If there is a floating selection, anchor it */
- floating_sel_anchor (gimp_image_floating_sel (display->image));
- }
- else
+ /* No need to be clever, we can cleanup even if no move was done */
+ if (fst->polygon_modified)
{
- /* Otherwise, clear the selection mask */
- gimp_channel_clear (gimp_image_get_mask (display->image), NULL, TRUE);
+ gimp_free_select_tool_cleanup_after_move (fst);
}
break;
+
+ case GIMP_BUTTON_RELEASE_CANCEL:
+ gimp_free_select_tool_handle_cancel (fst);
+ break;
+
+ default:
+ break;
}
- gimp_image_flush (display->image);
+ /* Reset */
+ fst->polygon_modified = FALSE;
+
+ gimp_draw_tool_resume (GIMP_DRAW_TOOL (fst));
}
static void
@@ -240,77 +926,124 @@
GdkModifierType state,
GimpDisplay *display)
{
- GimpFreeSelectTool *free_sel = GIMP_FREE_SELECT_TOOL (tool);
- GimpSelectionTool *sel_tool = GIMP_SELECTION_TOOL (tool);
+ GimpFreeSelectTool *fst;
+ GimpDrawTool *draw_tool;
+
+ if (tool->display != display)
+ return;
- if (sel_tool->function == SELECTION_ANCHOR)
+ fst = GIMP_FREE_SELECT_TOOL (tool);
+ draw_tool = GIMP_DRAW_TOOL (tool);
+
+ gimp_draw_tool_pause (draw_tool);
+
+ if (gimp_free_select_tool_is_point_grabbed (fst))
{
- sel_tool->function = SELECTION_SELECT;
+ fst->polygon_modified = TRUE;
+
+ gimp_free_select_tool_move_segment_vertex_to (fst,
+ fst->grabbed_segment_index,
+ coords->x,
+ coords->y);
+
+ /* We also must update the pending point if we are moving the
+ * first point
+ */
+ if (fst->grabbed_segment_index == 0)
+ {
+ fst->pending_point.x = coords->x;
+ fst->pending_point.y = coords->y;
+ }
+ }
+ else
+ {
+ /* Don't show the pending point while we are adding points */
+ fst->show_pending_point = FALSE;
- gimp_tool_cursor_update (tool, coords, state, display);
+ gimp_free_select_tool_add_point (fst,
+ coords->x,
+ coords->y);
}
- if (state & GDK_MOD1_MASK)
- {
- gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
+ gimp_draw_tool_resume (draw_tool);
+}
- gimp_free_select_tool_move_points (free_sel,
- coords->x - free_sel->last_coords.x,
- coords->y - free_sel->last_coords.y);
+static gboolean
+gimp_free_select_tool_key_press (GimpTool *tool,
+ GdkEventKey *kevent,
+ GimpDisplay *display)
+{
+ GimpFreeSelectTool *fst = GIMP_FREE_SELECT_TOOL (tool);
- gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
- }
- else
+ switch (kevent->keyval)
{
- gimp_free_select_tool_add_point (free_sel, coords->x, coords->y);
+ case GDK_BackSpace:
+ gimp_free_select_tool_remove_last_segment (fst);
+ return TRUE;
+
+ case GDK_Return:
+ case GDK_KP_Enter:
+ case GDK_ISO_Enter:
+ gimp_free_select_tool_commit (fst, display);
+ return TRUE;
+
+ case GDK_Escape:
+ gimp_free_select_tool_halt (fst);
+ return TRUE;
- gimp_draw_tool_draw_line (GIMP_DRAW_TOOL (tool),
- free_sel->points[free_sel->num_points - 2].x,
- free_sel->points[free_sel->num_points - 2].y,
- free_sel->points[free_sel->num_points - 1].x,
- free_sel->points[free_sel->num_points - 1].y,
- FALSE);
+ default:
+ break;
}
- free_sel->last_coords = *coords;
+ return FALSE;
}
static void
gimp_free_select_tool_draw (GimpDrawTool *draw_tool)
{
- GimpFreeSelectTool *free_sel = GIMP_FREE_SELECT_TOOL (draw_tool);
+ GimpFreeSelectTool *fst = GIMP_FREE_SELECT_TOOL (draw_tool);
gimp_draw_tool_draw_lines (draw_tool,
- free_sel->points, free_sel->num_points,
+ fst->points, fst->n_points,
FALSE, FALSE);
-}
+ if (gimp_free_select_tool_is_point_grabbed (fst) &&
+ ! fst->button1_down)
+ {
+ GimpVector2 grabbed_point;
-/* public functions */
-
-void
-gimp_free_select_tool_select (GimpFreeSelectTool *free_sel,
- GimpDisplay *display)
-{
- g_return_if_fail (GIMP_IS_FREE_SELECT_TOOL (free_sel));
- g_return_if_fail (GIMP_IS_DISPLAY (display));
+ grabbed_point = gimp_free_select_tool_get_grabbed_point (fst);
- GIMP_FREE_SELECT_TOOL_GET_CLASS (free_sel)->select (free_sel, display);
-}
+ gimp_draw_tool_draw_handle (draw_tool, GIMP_HANDLE_CIRCLE,
+ grabbed_point.x,
+ grabbed_point.y,
+ HANDLE_SIZE, HANDLE_SIZE,
+ GTK_ANCHOR_CENTER, FALSE);
+ }
+ if (fst->show_pending_point)
+ {
+ GimpVector2 last = fst->points[fst->n_points - 1];
-/* private functions */
+ gimp_draw_tool_draw_line (draw_tool,
+ last.x,
+ last.y,
+ fst->pending_point.x,
+ fst->pending_point.y,
+ FALSE);
+ }
+}
static void
-gimp_free_select_tool_real_select (GimpFreeSelectTool *free_sel,
+gimp_free_select_tool_real_select (GimpFreeSelectTool *fst,
GimpDisplay *display)
{
- GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (free_sel);
+ GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (fst);
gimp_channel_select_polygon (gimp_image_get_mask (display->image),
Q_("command|Free Select"),
- free_sel->num_points,
- free_sel->points,
+ fst->n_points,
+ fst->points,
options->operation,
options->antialias,
options->feather,
@@ -319,35 +1052,3 @@
TRUE);
}
-static void
-gimp_free_select_tool_add_point (GimpFreeSelectTool *free_sel,
- gdouble x,
- gdouble y)
-{
- if (free_sel->num_points >= free_sel->max_segs)
- {
- free_sel->max_segs += DEFAULT_MAX_INC;
-
- free_sel->points = g_realloc (free_sel->points,
- sizeof (GimpVector2) * free_sel->max_segs);
- }
-
- free_sel->points[free_sel->num_points].x = x;
- free_sel->points[free_sel->num_points].y = y;
-
- free_sel->num_points++;
-}
-
-static void
-gimp_free_select_tool_move_points (GimpFreeSelectTool *free_sel,
- gdouble dx,
- gdouble dy)
-{
- gint i;
-
- for (i = 0; i < free_sel->num_points; i++)
- {
- free_sel->points[i].x += dx;
- free_sel->points[i].y += dy;
- }
-}
Modified: trunk/app/tools/gimpfreeselecttool.h
==============================================================================
--- trunk/app/tools/gimpfreeselecttool.h (original)
+++ trunk/app/tools/gimpfreeselecttool.h Sun May 18 11:39:17 2008
@@ -20,6 +20,8 @@
#define __GIMP_FREE_SELECT_TOOL_H__
+#include "libgimpmath/gimpmath.h"
+
#include "gimpselectiontool.h"
@@ -38,11 +40,46 @@
{
GimpSelectionTool parent_instance;
- GimpCoords last_coords;
+ /* Index of grabbed segment index. */
+ gint grabbed_segment_index;
+
+ gboolean button1_down;
+ /* We need to keep track of a number of points when we move a
+ * segment vertex
+ */
+ GimpVector2 *saved_points_lower_segment;
+ GimpVector2 *saved_points_higher_segment;
+ gint n_saved_points_lower_segment;
+ gint n_saved_points_higher_segment;
+
+ /* Keeps track wether or not a modification of the polygon has been
+ * made between _button_press and _button_release
+ */
+ gboolean polygon_modified;
+
+ /* Point which is used to draw the polygon but which is not part of
+ * it yet
+ */
+ GimpVector2 pending_point;
+ gboolean show_pending_point;
+
+ /* The points of the polygon */
GimpVector2 *points;
- gint num_points;
- gint max_segs;
+ gint max_n_points;
+
+ /* The number of points actually in use */
+ gint n_points;
+
+
+ /* Any int array containing the indices for the points in the
+ * polygon that connects different segments together
+ */
+ gint *segment_indices;
+ gint max_n_segment_indices;
+
+ /* The number of segment indices actually in use */
+ gint n_segment_indices;
};
struct _GimpFreeSelectToolClass
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]