diff --git a/app/display/Makefile.am b/app/display/Makefile.am index ccde805c12..f7c1f41817 100644 --- a/app/display/Makefile.am +++ b/app/display/Makefile.am @@ -166,6 +166,8 @@ libappdisplay_a_sources = \ gimptoolgui.h \ gimptoolcompass.c \ gimptoolcompass.h \ + gimptoolhandlegrid.c \ + gimptoolhandlegrid.h \ gimptoolline.c \ gimptoolline.h \ gimptoolrotategrid.c \ diff --git a/app/display/display-enums.c b/app/display/display-enums.c index e80a7ad3cd..b3fcae7a94 100644 --- a/app/display/display-enums.c +++ b/app/display/display-enums.c @@ -294,6 +294,37 @@ gimp_transform_function_get_type (void) return type; } +GType +gimp_transform_handle_mode_get_type (void) +{ + static const GEnumValue values[] = + { + { GIMP_HANDLE_MODE_ADD_TRANSFORM, "GIMP_HANDLE_MODE_ADD_TRANSFORM", "add-transform" }, + { GIMP_HANDLE_MODE_MOVE, "GIMP_HANDLE_MODE_MOVE", "move" }, + { GIMP_HANDLE_MODE_REMOVE, "GIMP_HANDLE_MODE_REMOVE", "remove" }, + { 0, NULL, NULL } + }; + + static const GimpEnumDesc descs[] = + { + { GIMP_HANDLE_MODE_ADD_TRANSFORM, NC_("transform-handle-mode", "Add / Transform"), NULL }, + { GIMP_HANDLE_MODE_MOVE, NC_("transform-handle-mode", "Move"), NULL }, + { GIMP_HANDLE_MODE_REMOVE, NC_("transform-handle-mode", "Remove"), NULL }, + { 0, NULL, NULL } + }; + + static GType type = 0; + + if (G_UNLIKELY (! type)) + { + type = g_enum_register_static ("GimpTransformHandleMode", values); + gimp_type_set_translation_context (type, "transform-handle-mode"); + gimp_enum_set_value_descriptions (type, descs); + } + + return type; +} + GType gimp_zoom_focus_get_type (void) { diff --git a/app/display/display-enums.h b/app/display/display-enums.h index 704856e151..3e1d497a2e 100644 --- a/app/display/display-enums.h +++ b/app/display/display-enums.h @@ -134,6 +134,18 @@ typedef enum } GimpTransformFunction; +#define GIMP_TYPE_TRANSFORM_HANDLE_MODE (gimp_transform_handle_mode_get_type ()) + +GType gimp_transform_handle_mode_get_type (void) G_GNUC_CONST; + +typedef enum +{ + GIMP_HANDLE_MODE_ADD_TRANSFORM, /*< desc="Add / Transform" >*/ + GIMP_HANDLE_MODE_MOVE, /*< desc="Move" >*/ + GIMP_HANDLE_MODE_REMOVE /*< desc="Remove" >*/ +} GimpTransformHandleMode; + + #define GIMP_TYPE_ZOOM_FOCUS (gimp_zoom_focus_get_type ()) GType gimp_zoom_focus_get_type (void) G_GNUC_CONST; diff --git a/app/display/gimptoolhandlegrid.c b/app/display/gimptoolhandlegrid.c new file mode 100644 index 0000000000..ad0294636e --- /dev/null +++ b/app/display/gimptoolhandlegrid.c @@ -0,0 +1,1070 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolhandlegrid.c + * Copyright (C) 2017 Michael Natterer + * + * Based on GimpHandleTransformTool + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "libgimpbase/gimpbase.h" +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" + +#include "gimpcanvashandle.h" +#include "gimpdisplayshell.h" +#include "gimptoolhandlegrid.h" + +#include "gimp-intl.h" + + +#define GIMP_TOOL_HANDLE_SIZE_CIRCLE 13 + + +enum +{ + PROP_0, + PROP_HANDLE_MODE, + PROP_N_HANDLES, + PROP_ORIG_X1, + PROP_ORIG_Y1, + PROP_ORIG_X2, + PROP_ORIG_Y2, + PROP_ORIG_X3, + PROP_ORIG_Y3, + PROP_ORIG_X4, + PROP_ORIG_Y4, + PROP_TRANS_X1, + PROP_TRANS_Y1, + PROP_TRANS_X2, + PROP_TRANS_Y2, + PROP_TRANS_X3, + PROP_TRANS_Y3, + PROP_TRANS_X4, + PROP_TRANS_Y4 +}; + + +struct _GimpToolHandleGridPrivate +{ + GimpTransformHandleMode handle_mode; /* enum to be renamed */ + + gint n_handles; + gdouble orig_x[4]; + gdouble orig_y[4]; + gdouble trans_x[4]; + gdouble trans_y[4]; + + gint handle; + gdouble last_x; + gdouble last_y; + + GimpCanvasItem *handles[5]; +}; + + +/* local function prototypes */ + +static void gimp_tool_handle_grid_constructed (GObject *object); +static void gimp_tool_handle_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_tool_handle_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +static void gimp_tool_handle_grid_changed (GimpToolWidget *widget); +static gint gimp_tool_handle_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type); +static void gimp_tool_handle_grid_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type); +static void gimp_tool_handle_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state); +static void gimp_tool_handle_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +static gboolean gimp_tool_handle_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *cursor_modifier); + +static void gimp_tool_handle_grid_update_hilight (GimpToolHandleGrid *grid); +static void gimp_tool_handle_grid_update_matrix (GimpToolHandleGrid *grid); + +static gboolean is_handle_position_valid (GimpToolHandleGrid *grid, + gint handle); +static void handle_micro_move (GimpToolHandleGrid *grid, + gint handle); + +static inline gdouble calc_angle (gdouble ax, + gdouble ay, + gdouble bx, + gdouble by); +static inline gdouble calc_len (gdouble a, + gdouble b); +static inline gdouble calc_lineintersect_ratio (gdouble p1x, + gdouble p1y, + gdouble p2x, + gdouble p2y, + gdouble q1x, + gdouble q1y, + gdouble q2x, + gdouble q2y); + + +G_DEFINE_TYPE (GimpToolHandleGrid, gimp_tool_handle_grid, + GIMP_TYPE_TOOL_TRANSFORM_GRID) + +#define parent_class gimp_tool_handle_grid_parent_class + + +static void +gimp_tool_handle_grid_class_init (GimpToolHandleGridClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass); + + object_class->constructed = gimp_tool_handle_grid_constructed; + object_class->set_property = gimp_tool_handle_grid_set_property; + object_class->get_property = gimp_tool_handle_grid_get_property; + + widget_class->changed = gimp_tool_handle_grid_changed; + widget_class->button_press = gimp_tool_handle_grid_button_press; + widget_class->button_release = gimp_tool_handle_grid_button_release; + widget_class->motion = gimp_tool_handle_grid_motion; + widget_class->hover = gimp_tool_handle_grid_hover; + widget_class->get_cursor = gimp_tool_handle_grid_get_cursor; + + g_object_class_install_property (object_class, PROP_HANDLE_MODE, + g_param_spec_enum ("handle-mode", + NULL, NULL, + GIMP_TYPE_TRANSFORM_HANDLE_MODE, + GIMP_HANDLE_MODE_ADD_TRANSFORM, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_N_HANDLES, + g_param_spec_int ("n-handles", + NULL, NULL, + 0, 4, 0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_X1, + g_param_spec_double ("orig-x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_Y1, + g_param_spec_double ("orig-y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_X2, + g_param_spec_double ("orig-x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_Y2, + g_param_spec_double ("orig-y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_X3, + g_param_spec_double ("orig-x3", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_Y3, + g_param_spec_double ("orig-y3", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_X4, + g_param_spec_double ("orig-x4", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_ORIG_Y4, + g_param_spec_double ("orig-y4", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_X1, + g_param_spec_double ("trans-x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_Y1, + g_param_spec_double ("trans-y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_X2, + g_param_spec_double ("trans-x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_Y2, + g_param_spec_double ("trans-y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_X3, + g_param_spec_double ("trans-x3", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_Y3, + g_param_spec_double ("trans-y3", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_X4, + g_param_spec_double ("trans-x4", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, PROP_TRANS_Y4, + g_param_spec_double ("trans-y4", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GimpToolHandleGridPrivate)); +} + +static void +gimp_tool_handle_grid_init (GimpToolHandleGrid *grid) +{ + grid->private = G_TYPE_INSTANCE_GET_PRIVATE (grid, + GIMP_TYPE_TOOL_HANDLE_GRID, + GimpToolHandleGridPrivate); +} + +static void +gimp_tool_handle_grid_constructed (GObject *object) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (object); + GimpToolWidget *widget = GIMP_TOOL_WIDGET (object); + GimpToolHandleGridPrivate *private = grid->private; + gint i; + + G_OBJECT_CLASS (parent_class)->constructed (object); + + for (i = 0; i < 4; i++) + { + private->handles[i + 1] = + gimp_tool_widget_add_handle (widget, + GIMP_HANDLE_CIRCLE, + 0, 0, + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_TOOL_HANDLE_SIZE_CIRCLE, + GIMP_HANDLE_ANCHOR_CENTER); + } + + gimp_tool_handle_grid_changed (widget); +} + +static void +gimp_tool_handle_grid_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (object); + GimpToolHandleGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_HANDLE_MODE: + private->handle_mode = g_value_get_enum (value); + break; + + case PROP_N_HANDLES: + private->n_handles = g_value_get_int (value); + break; + + case PROP_ORIG_X1: + private->orig_x[0] = g_value_get_double (value); + break; + case PROP_ORIG_Y1: + private->orig_y[0] = g_value_get_double (value); + break; + case PROP_ORIG_X2: + private->orig_x[1] = g_value_get_double (value); + break; + case PROP_ORIG_Y2: + private->orig_y[1] = g_value_get_double (value); + break; + case PROP_ORIG_X3: + private->orig_x[2] = g_value_get_double (value); + break; + case PROP_ORIG_Y3: + private->orig_y[2] = g_value_get_double (value); + break; + case PROP_ORIG_X4: + private->orig_x[3] = g_value_get_double (value); + break; + case PROP_ORIG_Y4: + private->orig_y[3] = g_value_get_double (value); + break; + + case PROP_TRANS_X1: + private->trans_x[0] = g_value_get_double (value); + break; + case PROP_TRANS_Y1: + private->trans_y[0] = g_value_get_double (value); + break; + case PROP_TRANS_X2: + private->trans_x[1] = g_value_get_double (value); + break; + case PROP_TRANS_Y2: + private->trans_y[1] = g_value_get_double (value); + break; + case PROP_TRANS_X3: + private->trans_x[2] = g_value_get_double (value); + break; + case PROP_TRANS_Y3: + private->trans_y[2] = g_value_get_double (value); + break; + case PROP_TRANS_X4: + private->trans_x[3] = g_value_get_double (value); + break; + case PROP_TRANS_Y4: + private->trans_y[3] = g_value_get_double (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_handle_grid_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (object); + GimpToolHandleGridPrivate *private = grid->private; + + switch (property_id) + { + case PROP_N_HANDLES: + g_value_set_int (value, private->n_handles); + break; + + case PROP_HANDLE_MODE: + g_value_set_enum (value, private->handle_mode); + break; + + case PROP_ORIG_X1: + g_value_set_double (value, private->orig_x[0]); + break; + case PROP_ORIG_Y1: + g_value_set_double (value, private->orig_y[0]); + break; + case PROP_ORIG_X2: + g_value_set_double (value, private->orig_x[1]); + break; + case PROP_ORIG_Y2: + g_value_set_double (value, private->orig_y[1]); + break; + case PROP_ORIG_X3: + g_value_set_double (value, private->orig_x[2]); + break; + case PROP_ORIG_Y3: + g_value_set_double (value, private->orig_y[2]); + break; + case PROP_ORIG_X4: + g_value_set_double (value, private->orig_x[3]); + break; + case PROP_ORIG_Y4: + g_value_set_double (value, private->orig_y[3]); + break; + + case PROP_TRANS_X1: + g_value_set_double (value, private->trans_x[0]); + break; + case PROP_TRANS_Y1: + g_value_set_double (value, private->trans_y[0]); + break; + case PROP_TRANS_X2: + g_value_set_double (value, private->trans_x[1]); + break; + case PROP_TRANS_Y2: + g_value_set_double (value, private->trans_y[1]); + break; + case PROP_TRANS_X3: + g_value_set_double (value, private->trans_x[2]); + break; + case PROP_TRANS_Y3: + g_value_set_double (value, private->trans_y[2]); + break; + case PROP_TRANS_X4: + g_value_set_double (value, private->trans_x[3]); + break; + case PROP_TRANS_Y4: + g_value_set_double (value, private->trans_y[3]); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_tool_handle_grid_changed (GimpToolWidget *widget) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint i; + + GIMP_TOOL_WIDGET_CLASS (parent_class)->changed (widget); + + for (i = 0; i < 4; i++) + { + gimp_canvas_handle_set_position (private->handles[i + 1], + private->trans_x[i], + private->trans_y[i]); + gimp_canvas_item_set_visible (private->handles[i + 1], + i < private->n_handles); + } + + gimp_tool_handle_grid_update_hilight (grid); +} + +static gint +gimp_tool_handle_grid_button_press (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonPressType press_type) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint n_handles = private->n_handles; + gint active_handle = private->handle - 1; + + switch (private->handle_mode) + { + case GIMP_HANDLE_MODE_ADD_TRANSFORM: + if (n_handles < 4 && active_handle == -1) + { + /* add handle */ + + GimpMatrix3 *matrix; + + active_handle = n_handles; + + private->trans_x[active_handle] = coords->x; + private->trans_y[active_handle] = coords->y; + private->n_handles++; + + if (! is_handle_position_valid (grid, active_handle)) + { + handle_micro_move (grid, active_handle); + } + + /* handle was added, calculate new original position */ + g_object_get (grid, + "transform", &matrix, + NULL); + + gimp_matrix3_invert (matrix); + gimp_matrix3_transform_point (matrix, + private->trans_x[active_handle], + private->trans_y[active_handle], + &private->orig_x[active_handle], + &private->orig_y[active_handle]); + + g_free (matrix); + + private->handle = active_handle + 1; + + g_object_notify (G_OBJECT (grid), "n-handles"); + } + break; + + case GIMP_HANDLE_MODE_MOVE: + /* check for valid position and calculating of OX0...OY3 is + * done on button release + */ + break; + + case GIMP_HANDLE_MODE_REMOVE: + if (n_handles > 0 && + active_handle >= 0 && + active_handle < 4) + { + /* remove handle */ + + gdouble tempx = private->trans_x[active_handle]; + gdouble tempy = private->trans_y[active_handle]; + gdouble tempox = private->orig_x[active_handle]; + gdouble tempoy = private->orig_y[active_handle]; + gint i; + + n_handles--; + private->n_handles--; + + for (i = active_handle; i < n_handles; i++) + { + private->trans_x[i] = private->trans_x[i + 1]; + private->trans_y[i] = private->trans_y[i + 1]; + private->orig_x[i] = private->orig_x[i + 1]; + private->orig_y[i] = private->orig_y[i + 1]; + } + + private->trans_x[n_handles] = tempx; + private->trans_y[n_handles] = tempy; + private->orig_x[n_handles] = tempox; + private->orig_y[n_handles] = tempoy; + + g_object_notify (G_OBJECT (grid), "n-handles"); + } + break; + } + + private->last_x = coords->x; + private->last_y = coords->y; + + return private->handle; +} + +static void +gimp_tool_handle_grid_button_release (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state, + GimpButtonReleaseType release_type) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint active_handle = private->handle - 1; + + if (private->handle_mode == GIMP_HANDLE_MODE_MOVE && + active_handle >= 0 && + active_handle < 4) + { + GimpMatrix3 *matrix; + + if (! is_handle_position_valid (grid, active_handle)) + { + handle_micro_move (grid, active_handle); + } + + /* handle was moved, calculate new original position */ + g_object_get (grid, + "transform", &matrix, + NULL); + + gimp_matrix3_invert (matrix); + gimp_matrix3_transform_point (matrix, + private->trans_x[active_handle], + private->trans_y[active_handle], + &private->orig_x[active_handle], + &private->orig_y[active_handle]); + + g_free (matrix); + + gimp_tool_handle_grid_update_matrix (grid); + } +} + +void +gimp_tool_handle_grid_motion (GimpToolWidget *widget, + const GimpCoords *coords, + guint32 time, + GdkModifierType state) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint n_handles = private->n_handles; + gint active_handle = private->handle - 1; + gdouble diff_x = coords->x - private->last_x; + gdouble diff_y = coords->y - private->last_y; + + if (active_handle >= 0 && active_handle < 4) + { + if (private->handle_mode == GIMP_HANDLE_MODE_MOVE) + { + private->trans_x[active_handle] += diff_x; + private->trans_y[active_handle] += diff_y; + + /* check for valid position and calculating of OX0...OY3 is + * done on button release hopefully this makes the code run + * faster Moving could be even faster if there was caching + * for the image preview + */ + gimp_canvas_handle_set_position (private->handles[active_handle + 1], + private->trans_x[active_handle], + private->trans_y[active_handle]); + } + else if (private->handle_mode == GIMP_HANDLE_MODE_ADD_TRANSFORM) + { + gdouble angle, angle_sin, angle_cos, scale; + gdouble fixed_handles_x[3]; + gdouble fixed_handles_y[3]; + gdouble oldpos_x[4], oldpos_y[4]; + gdouble newpos_x[4], newpos_y[4]; + gint i, j; + + for (i = 0, j = 0; i < 4; i++) + { + /* Find all visible handles that are not being moved */ + if (i < n_handles && i != active_handle) + { + fixed_handles_x[j] = private->trans_x[i]; + fixed_handles_y[j] = private->trans_y[i]; + j++; + } + + newpos_x[i] = oldpos_x[i] = private->trans_x[i]; + newpos_y[i] = oldpos_y[i] = private->trans_y[i]; + } + + newpos_x[active_handle] = oldpos_x[active_handle] + diff_x; + newpos_y[active_handle] = oldpos_y[active_handle] + diff_y; + + switch (n_handles) + { + case 1: + /* move */ + for (i = 1; i < 4; i++) + { + newpos_x[i] = oldpos_x[i] + diff_y; + newpos_y[i] = oldpos_y[i] + diff_y; + } + break; + + case 2: + /* rotate and keep-aspect-scale */ + scale = + calc_len (newpos_x[active_handle] - fixed_handles_x[0], + newpos_y[active_handle] - fixed_handles_y[0]) / + calc_len (oldpos_x[active_handle] - fixed_handles_x[0], + oldpos_y[active_handle] - fixed_handles_y[0]); + + angle = calc_angle (oldpos_x[active_handle] - fixed_handles_x[0], + oldpos_y[active_handle] - fixed_handles_y[0], + newpos_x[active_handle] - fixed_handles_x[0], + newpos_y[active_handle] - fixed_handles_y[0]); + + angle_sin = sin (angle); + angle_cos = cos (angle); + + for (i = 2; i < 4; i++) + { + newpos_x[i] = + fixed_handles_x[0] + + scale * (angle_cos * (oldpos_x[i] - fixed_handles_x[0]) + + angle_sin * (oldpos_y[i] - fixed_handles_y[0])); + + newpos_y[i] = + fixed_handles_y[0] + + scale * (-angle_sin * (oldpos_x[i] - fixed_handles_x[0]) + + angle_cos * (oldpos_y[i] - fixed_handles_y[0])); + } + break; + + case 3: + /* shear and non-aspect-scale */ + scale = calc_lineintersect_ratio (oldpos_x[3], + oldpos_y[3], + oldpos_x[active_handle], + oldpos_y[active_handle], + fixed_handles_x[0], + fixed_handles_y[0], + fixed_handles_x[1], + fixed_handles_y[1]); + + newpos_x[3] = oldpos_x[3] + scale * diff_x; + newpos_y[3] = oldpos_y[3] + scale * diff_y; + break; + } + + for (i = 0; i < 4; i++) + { + private->trans_x[i] = newpos_x[i]; + private->trans_y[i] = newpos_y[i]; + } + + gimp_tool_handle_grid_update_matrix (grid); + } + } + + private->last_x = coords->x; + private->last_y = coords->y; +} + +static void +gimp_tool_handle_grid_hover (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + gint i; + + private->handle = 0; + + for (i = 0; i < 4; i++) + { + if (private->handles[i + 1] && + gimp_canvas_item_hit (private->handles[i + 1], + coords->x, coords->y)) + { + private->handle = i + 1; + break; + } + } + + gimp_tool_handle_grid_update_hilight (grid); +} + +static gboolean +gimp_tool_handle_grid_get_cursor (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + GimpCursorType *cursor, + GimpToolCursorType *tool_cursor, + GimpCursorModifier *cursor_modifier) +{ + GimpToolHandleGrid *grid = GIMP_TOOL_HANDLE_GRID (widget); + GimpToolHandleGridPrivate *private = grid->private; + + *cursor = GIMP_CURSOR_CROSSHAIR_SMALL; + *tool_cursor = GIMP_TOOL_CURSOR_NONE; + *cursor_modifier = GIMP_CURSOR_MODIFIER_NONE; + + switch (private->handle_mode) + { + case GIMP_HANDLE_MODE_ADD_TRANSFORM: + if (private->handle > 0) + { + switch (private->n_handles) + { + case 1: + *tool_cursor = GIMP_TOOL_CURSOR_MOVE; + break; + case 2: + *tool_cursor = GIMP_TOOL_CURSOR_ROTATE; + break; + case 3: + *tool_cursor = GIMP_TOOL_CURSOR_SHEAR; + break; + case 4: + *tool_cursor = GIMP_TOOL_CURSOR_PERSPECTIVE; + break; + } + } + else + { + if (private->n_handles < 4) + *cursor_modifier = GIMP_CURSOR_MODIFIER_PLUS; + else + *cursor_modifier = GIMP_CURSOR_MODIFIER_BAD; + } + break; + + case GIMP_HANDLE_MODE_MOVE: + if (private->handle > 0) + *cursor_modifier = GIMP_CURSOR_MODIFIER_MOVE; + else + *cursor_modifier = GIMP_CURSOR_MODIFIER_BAD; + break; + + case GIMP_HANDLE_MODE_REMOVE: + if (private->handle > 0) + *cursor_modifier = GIMP_CURSOR_MODIFIER_MINUS; + else + *cursor_modifier = GIMP_CURSOR_MODIFIER_BAD; + break; + } + + return TRUE; +} + +static void +gimp_tool_handle_grid_update_hilight (GimpToolHandleGrid *grid) +{ + GimpToolHandleGridPrivate *private = grid->private; + gint i; + + for (i = 0; i < 4; i++) + { + if (private->handles[i + 1]) + { + gimp_canvas_item_set_highlight (private->handles[i + 1], + (i + 1) == private->handle); + } + } +} + +static void +gimp_tool_handle_grid_update_matrix (GimpToolHandleGrid *grid) +{ + GimpToolHandleGridPrivate *private = grid->private; + GimpMatrix3 transform; + + gimp_matrix3_identity (&transform); + gimp_transform_matrix_handles (&transform, + private->orig_x[0], + private->orig_y[0], + private->orig_x[1], + private->orig_y[1], + private->orig_x[2], + private->orig_y[2], + private->orig_x[3], + private->orig_y[3], + private->trans_x[0], + private->trans_y[0], + private->trans_x[1], + private->trans_y[1], + private->trans_x[2], + private->trans_y[2], + private->trans_x[3], + private->trans_y[3]); + + g_object_set (grid, + "transform", &transform, + NULL); +} + +/* check if a handle is not on the connection line of two other handles */ +static gboolean +is_handle_position_valid (GimpToolHandleGrid *grid, + gint handle) +{ + GimpToolHandleGridPrivate *private = grid->private; + gint i, j, k; + + for (i = 0; i < 2; i++) + { + for (j = i + 1; j < 3; j++) + { + for (k = j + 1; i < 4; i++) + { + if (handle == i || + handle == j || + handle == k) + { + if ((private->trans_x[i] - + private->trans_x[j]) * + (private->trans_y[j] - + private->trans_y[k]) == + + (private->trans_x[j] - + private->trans_x[k]) * + (private->trans_y[i] - + private->trans_y[j])) + { + return FALSE; + } + } + } + } + } + + return TRUE; +} + +/* three handles on a line causes problems. + * Let's move the new handle around a bit to find a better position */ +static void +handle_micro_move (GimpToolHandleGrid *grid, + gint handle) +{ + GimpToolHandleGridPrivate *private = grid->private; + gdouble posx = private->trans_x[handle]; + gdouble posy = private->trans_y[handle]; + gdouble dx, dy; + + for (dx = -0.1; dx < 0.11; dx += 0.1) + { + private->trans_x[handle] = posx + dx; + + for (dy = -0.1; dy < 0.11; dy += 0.1) + { + private->trans_y[handle] = posy + dy; + + if (is_handle_position_valid (grid, handle)) + { + return; + } + } + } +} + +/* finds the clockwise angle between the vectors given, 0-2π */ +static inline gdouble +calc_angle (gdouble ax, + gdouble ay, + gdouble bx, + gdouble by) +{ + gdouble angle; + gdouble direction; + gdouble length = sqrt ((ax * ax + ay * ay) * (bx * bx + by * by)); + + angle = acos ((ax * bx + ay * by) / length); + direction = ax * by - ay * bx; + + return ((direction < 0) ? angle : 2 * G_PI - angle); +} + +static inline gdouble +calc_len (gdouble a, + gdouble b) +{ + return sqrt (a * a + b * b); +} + +/* imagine two lines, one through the points p1 and p2, the other one + * through the points q1 and q2. Find the intersection point r. + * Calculate (distance p1 to r)/(distance p2 to r) + */ +static inline gdouble +calc_lineintersect_ratio (gdouble p1x, gdouble p1y, + gdouble p2x, gdouble p2y, + gdouble q1x, gdouble q1y, + gdouble q2x, gdouble q2y) +{ + gdouble denom, u; + + denom = (q2y - q1y) * (p2x - p1x) - (q2x - q1x) * (p2y - p1y); + if (denom == 0.0) + { + /* u is infinite, so u/(u-1) is 1 */ + return 1.0; + } + + u = (q2y - q1y) * (q1x - p1x) - (q1y - p1y) * (q2x - q1x); + u /= denom; + + return u / (u - 1); +} + + +/* public functions */ + +GimpToolWidget * +gimp_tool_handle_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_TOOL_HANDLE_GRID, + "shell", shell, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); +} diff --git a/app/display/gimptoolhandlegrid.h b/app/display/gimptoolhandlegrid.h new file mode 100644 index 0000000000..5a979c0a8e --- /dev/null +++ b/app/display/gimptoolhandlegrid.h @@ -0,0 +1,62 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimptoolhandlegrid.h + * Copyright (C) 2017 Michael Natterer + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __GIMP_TOOL_HANDLE_GRID_H__ +#define __GIMP_TOOL_HANDLE_GRID_H__ + + +#include "gimptooltransformgrid.h" + + +#define GIMP_TYPE_TOOL_HANDLE_GRID (gimp_tool_handle_grid_get_type ()) +#define GIMP_TOOL_HANDLE_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_HANDLE_GRID, GimpToolHandleGrid)) +#define GIMP_TOOL_HANDLE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_HANDLE_GRID, GimpToolHandleGridClass)) +#define GIMP_IS_TOOL_HANDLE_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_HANDLE_GRID)) +#define GIMP_IS_TOOL_HANDLE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_HANDLE_GRID)) +#define GIMP_TOOL_HANDLE_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_HANDLE_GRID, GimpToolHandleGridClass)) + + +typedef struct _GimpToolHandleGrid GimpToolHandleGrid; +typedef struct _GimpToolHandleGridPrivate GimpToolHandleGridPrivate; +typedef struct _GimpToolHandleGridClass GimpToolHandleGridClass; + +struct _GimpToolHandleGrid +{ + GimpToolTransformGrid parent_instance; + + GimpToolHandleGridPrivate *private; +}; + +struct _GimpToolHandleGridClass +{ + GimpToolTransformGridClass parent_class; +}; + + +GType gimp_tool_handle_grid_get_type (void) G_GNUC_CONST; + +GimpToolWidget * gimp_tool_handle_grid_new (GimpDisplayShell *shell, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2); + + +#endif /* __GIMP_TOOL_HANDLE_GRID_H__ */ diff --git a/app/tools/tools-enums.c b/app/tools/tools-enums.c index 972532fdca..261d78a8b6 100644 --- a/app/tools/tools-enums.c +++ b/app/tools/tools-enums.c @@ -9,37 +9,6 @@ #include "gimp-intl.h" /* enumerations from "tools-enums.h" */ -GType -gimp_transform_handle_mode_get_type (void) -{ - static const GEnumValue values[] = - { - { GIMP_HANDLE_MODE_ADD_TRANSFORM, "GIMP_HANDLE_MODE_ADD_TRANSFORM", "add-transform" }, - { GIMP_HANDLE_MODE_MOVE, "GIMP_HANDLE_MODE_MOVE", "move" }, - { GIMP_HANDLE_MODE_REMOVE, "GIMP_HANDLE_MODE_REMOVE", "remove" }, - { 0, NULL, NULL } - }; - - static const GimpEnumDesc descs[] = - { - { GIMP_HANDLE_MODE_ADD_TRANSFORM, NC_("transform-handle-mode", "Add / Transform"), NULL }, - { GIMP_HANDLE_MODE_MOVE, NC_("transform-handle-mode", "Move"), NULL }, - { GIMP_HANDLE_MODE_REMOVE, NC_("transform-handle-mode", "Remove"), NULL }, - { 0, NULL, NULL } - }; - - static GType type = 0; - - if (G_UNLIKELY (! type)) - { - type = g_enum_register_static ("GimpTransformHandleMode", values); - gimp_type_set_translation_context (type, "transform-handle-mode"); - gimp_enum_set_value_descriptions (type, descs); - } - - return type; -} - GType gimp_rectangle_constraint_get_type (void) { diff --git a/app/tools/tools-enums.h b/app/tools/tools-enums.h index 25a0527ae1..43d388205a 100644 --- a/app/tools/tools-enums.h +++ b/app/tools/tools-enums.h @@ -23,18 +23,6 @@ * these enums are registered with the type system */ -#define GIMP_TYPE_TRANSFORM_HANDLE_MODE (gimp_transform_handle_mode_get_type ()) - -GType gimp_transform_handle_mode_get_type (void) G_GNUC_CONST; - -typedef enum -{ - GIMP_HANDLE_MODE_ADD_TRANSFORM, /*< desc="Add / Transform" >*/ - GIMP_HANDLE_MODE_MOVE, /*< desc="Move" >*/ - GIMP_HANDLE_MODE_REMOVE /*< desc="Remove" >*/ -} GimpTransformHandleMode; - - #define GIMP_TYPE_RECTANGLE_CONSTRAINT (gimp_rectangle_constraint_get_type ()) GType gimp_rectangle_constraint_get_type (void) G_GNUC_CONST;