[gimp] app: add GimpToolTransformGrid, GimpToolRotateGrid and GimpToolShearGrid
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: add GimpToolTransformGrid, GimpToolRotateGrid and GimpToolShearGrid
- Date: Sat, 17 Jun 2017 01:04:31 +0000 (UTC)
commit 457f6bf95298e2904ac89336d719f649072de942
Author: Michael Natterer <mitch gimp org>
Date: Sat Jun 17 02:34:35 2017 +0200
app: add GimpToolTransformGrid, GimpToolRotateGrid and GimpToolShearGrid
which do all transform tools' (except handle transform) canvas GUI and
their interaction.
app/display/Makefile.am | 6 +
app/display/display-enums.c | 35 +
app/display/display-enums.h | 14 +
app/display/gimptoolrotategrid.c | 334 ++++++
app/display/gimptoolrotategrid.h | 65 +
app/display/gimptoolsheargrid.c | 364 ++++++
app/display/gimptoolsheargrid.h | 65 +
app/display/gimptooltransformgrid.c | 2245 +++++++++++++++++++++++++++++++++++
app/display/gimptooltransformgrid.h | 92 ++
9 files changed, 3220 insertions(+), 0 deletions(-)
---
diff --git a/app/display/Makefile.am b/app/display/Makefile.am
index 2069226..ccde805 100644
--- a/app/display/Makefile.am
+++ b/app/display/Makefile.am
@@ -168,6 +168,12 @@ libappdisplay_a_sources = \
gimptoolcompass.h \
gimptoolline.c \
gimptoolline.h \
+ gimptoolrotategrid.c \
+ gimptoolrotategrid.h \
+ gimptoolsheargrid.c \
+ gimptoolsheargrid.h \
+ gimptooltransformgrid.c \
+ gimptooltransformgrid.h \
gimptoolwidget.c \
gimptoolwidget.h
diff --git a/app/display/display-enums.c b/app/display/display-enums.c
index 577e473..e80a7ad 100644
--- a/app/display/display-enums.c
+++ b/app/display/display-enums.c
@@ -260,6 +260,41 @@ gimp_path_style_get_type (void)
}
GType
+gimp_transform_function_get_type (void)
+{
+ static const GEnumValue values[] =
+ {
+ { GIMP_TRANSFORM_FUNCTION_MOVE, "GIMP_TRANSFORM_FUNCTION_MOVE", "move" },
+ { GIMP_TRANSFORM_FUNCTION_SCALE, "GIMP_TRANSFORM_FUNCTION_SCALE", "scale" },
+ { GIMP_TRANSFORM_FUNCTION_ROTATE, "GIMP_TRANSFORM_FUNCTION_ROTATE", "rotate" },
+ { GIMP_TRANSFORM_FUNCTION_SHEAR, "GIMP_TRANSFORM_FUNCTION_SHEAR", "shear" },
+ { GIMP_TRANSFORM_FUNCTION_PERSPECTIVE, "GIMP_TRANSFORM_FUNCTION_PERSPECTIVE", "perspective" },
+ { 0, NULL, NULL }
+ };
+
+ static const GimpEnumDesc descs[] =
+ {
+ { GIMP_TRANSFORM_FUNCTION_MOVE, "GIMP_TRANSFORM_FUNCTION_MOVE", NULL },
+ { GIMP_TRANSFORM_FUNCTION_SCALE, "GIMP_TRANSFORM_FUNCTION_SCALE", NULL },
+ { GIMP_TRANSFORM_FUNCTION_ROTATE, "GIMP_TRANSFORM_FUNCTION_ROTATE", NULL },
+ { GIMP_TRANSFORM_FUNCTION_SHEAR, "GIMP_TRANSFORM_FUNCTION_SHEAR", NULL },
+ { GIMP_TRANSFORM_FUNCTION_PERSPECTIVE, "GIMP_TRANSFORM_FUNCTION_PERSPECTIVE", NULL },
+ { 0, NULL, NULL }
+ };
+
+ static GType type = 0;
+
+ if (G_UNLIKELY (! type))
+ {
+ type = g_enum_register_static ("GimpTransformFunction", values);
+ gimp_type_set_translation_context (type, "transform-function");
+ gimp_enum_set_value_descriptions (type, descs);
+ }
+
+ return type;
+}
+
+GType
gimp_zoom_focus_get_type (void)
{
static const GEnumValue values[] =
diff --git a/app/display/display-enums.h b/app/display/display-enums.h
index e0d3122..704856e 100644
--- a/app/display/display-enums.h
+++ b/app/display/display-enums.h
@@ -120,6 +120,20 @@ typedef enum
} GimpPathStyle;
+#define GIMP_TYPE_TRANSFORM_FUNCTION (gimp_transform_function_get_type ())
+
+GType gimp_transform_function_get_type (void) G_GNUC_CONST;
+
+typedef enum
+{
+ GIMP_TRANSFORM_FUNCTION_MOVE,
+ GIMP_TRANSFORM_FUNCTION_SCALE,
+ GIMP_TRANSFORM_FUNCTION_ROTATE,
+ GIMP_TRANSFORM_FUNCTION_SHEAR,
+ GIMP_TRANSFORM_FUNCTION_PERSPECTIVE
+} GimpTransformFunction;
+
+
#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/gimptoolrotategrid.c b/app/display/gimptoolrotategrid.c
new file mode 100644
index 0000000..77ffa02
--- /dev/null
+++ b/app/display/gimptoolrotategrid.c
@@ -0,0 +1,334 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptoolrotategrid.c
+ * Copyright (C) 2017 Michael Natterer <mitch gimp org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "display-types.h"
+
+#include "core/gimp-transform-utils.h"
+#include "core/gimp-utils.h"
+
+#include "gimpdisplayshell.h"
+#include "gimptoolrotategrid.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_ANGLE
+};
+
+
+struct _GimpToolRotateGridPrivate
+{
+ gdouble angle;
+
+ gboolean rotate_grab;
+ gdouble real_angle;
+ gdouble last_x;
+ gdouble last_y;
+};
+
+
+/* local function prototypes */
+
+static void gimp_tool_rotate_grid_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_tool_rotate_grid_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gint gimp_tool_rotate_grid_button_press (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonPressType press_type);
+static void gimp_tool_rotate_grid_motion (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state);
+
+
+G_DEFINE_TYPE (GimpToolRotateGrid, gimp_tool_rotate_grid,
+ GIMP_TYPE_TOOL_TRANSFORM_GRID)
+
+#define parent_class gimp_tool_rotate_grid_parent_class
+
+
+static void
+gimp_tool_rotate_grid_class_init (GimpToolRotateGridClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);
+
+ object_class->set_property = gimp_tool_rotate_grid_set_property;
+ object_class->get_property = gimp_tool_rotate_grid_get_property;
+
+ widget_class->button_press = gimp_tool_rotate_grid_button_press;
+ widget_class->motion = gimp_tool_rotate_grid_motion;
+
+ g_object_class_install_property (object_class, PROP_ANGLE,
+ g_param_spec_double ("angle",
+ 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 (GimpToolRotateGridPrivate));
+}
+
+static void
+gimp_tool_rotate_grid_init (GimpToolRotateGrid *grid)
+{
+ grid->private = G_TYPE_INSTANCE_GET_PRIVATE (grid,
+ GIMP_TYPE_TOOL_ROTATE_GRID,
+ GimpToolRotateGridPrivate);
+}
+
+static void
+gimp_tool_rotate_grid_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpToolRotateGrid *grid = GIMP_TOOL_ROTATE_GRID (object);
+ GimpToolRotateGridPrivate *private = grid->private;
+
+ switch (property_id)
+ {
+ case PROP_ANGLE:
+ private->angle = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_tool_rotate_grid_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpToolRotateGrid *grid = GIMP_TOOL_ROTATE_GRID (object);
+ GimpToolRotateGridPrivate *private = grid->private;
+
+ switch (property_id)
+ {
+ case PROP_ANGLE:
+ g_value_set_double (value, private->angle);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint
+gimp_tool_rotate_grid_button_press (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonPressType press_type)
+{
+ GimpToolRotateGrid *grid = GIMP_TOOL_ROTATE_GRID (widget);
+ GimpToolRotateGridPrivate *private = grid->private;
+ GimpTransformHandle handle;
+
+ handle = GIMP_TOOL_WIDGET_CLASS (parent_class)->button_press (widget,
+ coords, time,
+ state,
+ press_type);
+
+ if (handle == GIMP_TRANSFORM_HANDLE_ROTATION)
+ {
+ private->rotate_grab = TRUE;
+ private->real_angle = private->angle;
+ private->last_x = coords->x;
+ private->last_y = coords->y;
+ }
+ else
+ {
+ private->rotate_grab = FALSE;
+ }
+
+ return handle;
+}
+
+void
+gimp_tool_rotate_grid_motion (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state)
+{
+ GimpToolRotateGrid *grid = GIMP_TOOL_ROTATE_GRID (widget);
+ GimpToolRotateGridPrivate *private = grid->private;
+ gdouble angle1, angle2, angle;
+ gdouble pivot_x, pivot_y;
+ gdouble x1, y1, x2, y2;
+ gboolean constrain;
+ GimpMatrix3 transform;
+
+ if (! private->rotate_grab)
+ {
+ gdouble old_pivot_x;
+ gdouble old_pivot_y;
+
+ g_object_get (widget,
+ "pivot-x", &old_pivot_x,
+ "pivot-y", &old_pivot_y,
+ NULL);
+
+ g_object_freeze_notify (G_OBJECT (widget));
+
+ GIMP_TOOL_WIDGET_CLASS (parent_class)->motion (widget,
+ coords, time, state);
+
+ g_object_get (widget,
+ "pivot-x", &pivot_x,
+ "pivot-y", &pivot_y,
+ NULL);
+
+ if (old_pivot_x != pivot_x ||
+ old_pivot_y != pivot_y)
+ {
+ gimp_matrix3_identity (&transform);
+ gimp_transform_matrix_rotate_center (&transform,
+ pivot_x, pivot_y,
+ private->angle);
+
+ g_object_set (widget,
+ "transform", &transform,
+ NULL);
+ }
+
+ g_object_thaw_notify (G_OBJECT (widget));
+
+ return;
+ }
+
+ g_object_get (widget,
+ "pivot-x", &pivot_x,
+ "pivot-y", &pivot_y,
+ "constrain-rotate", &constrain,
+ NULL);
+
+ x1 = coords->x - pivot_x;
+ x2 = private->last_x - pivot_x;
+ y1 = pivot_y - coords->y;
+ y2 = pivot_y - private->last_y;
+
+ /* find the first angle */
+ angle1 = atan2 (y1, x1);
+
+ /* find the angle */
+ angle2 = atan2 (y2, x2);
+
+ angle = angle2 - angle1;
+
+ if (angle > G_PI || angle < -G_PI)
+ angle = angle2 - ((angle1 < 0) ? 2.0 * G_PI + angle1 : angle1 - 2.0 * G_PI);
+
+ /* increment the transform tool's angle */
+ private->real_angle += angle;
+
+ /* limit the angle to between -180 and 180 degrees */
+ if (private->real_angle < - G_PI)
+ {
+ private->real_angle += 2.0 * G_PI;
+ }
+ else if (private->real_angle > G_PI)
+ {
+ private->real_angle -= 2.0 * G_PI;
+ }
+
+ /* constrain the angle to 15-degree multiples if ctrl is held down */
+#define FIFTEEN_DEG (G_PI / 12.0)
+
+ if (constrain)
+ {
+ angle = FIFTEEN_DEG * (gint) ((private->real_angle +
+ FIFTEEN_DEG / 2.0) / FIFTEEN_DEG);
+ }
+ else
+ {
+ angle = private->real_angle;
+ }
+
+ gimp_matrix3_identity (&transform);
+ gimp_transform_matrix_rotate_center (&transform, pivot_x, pivot_y, angle);
+
+ g_object_set (widget,
+ "transform", &transform,
+ "angle", angle,
+ NULL);
+
+ private->last_x = coords->x;
+ private->last_y = coords->y;
+}
+
+
+/* public functions */
+
+GimpToolWidget *
+gimp_tool_rotate_grid_new (GimpDisplayShell *shell,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble pivot_x,
+ gdouble pivot_y,
+ gdouble angle)
+{
+ GimpMatrix3 transform;
+
+ g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
+
+ gimp_matrix3_identity (&transform);
+ gimp_transform_matrix_rotate_center (&transform, pivot_x, pivot_y, angle);
+
+ return g_object_new (GIMP_TYPE_TOOL_ROTATE_GRID,
+ "shell", shell,
+ "transform", &transform,
+ "x1", x1,
+ "y1", y1,
+ "x2", x2,
+ "y2", y2,
+ "pivot-x", pivot_x,
+ "pivot-y", pivot_y,
+ "angle", angle,
+ NULL);
+}
diff --git a/app/display/gimptoolrotategrid.h b/app/display/gimptoolrotategrid.h
new file mode 100644
index 0000000..43c97cc
--- /dev/null
+++ b/app/display/gimptoolrotategrid.h
@@ -0,0 +1,65 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptoolrotategrid.h
+ * Copyright (C) 2017 Michael Natterer <mitch gimp org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TOOL_ROTATE_GRID_H__
+#define __GIMP_TOOL_ROTATE_GRID_H__
+
+
+#include "gimptooltransformgrid.h"
+
+
+#define GIMP_TYPE_TOOL_ROTATE_GRID (gimp_tool_rotate_grid_get_type ())
+#define GIMP_TOOL_ROTATE_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GIMP_TYPE_TOOL_ROTATE_GRID, GimpToolRotateGrid))
+#define GIMP_TOOL_ROTATE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_ROTATE_GRID,
GimpToolRotateGridClass))
+#define GIMP_IS_TOOL_ROTATE_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GIMP_TYPE_TOOL_ROTATE_GRID))
+#define GIMP_IS_TOOL_ROTATE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_ROTATE_GRID))
+#define GIMP_TOOL_ROTATE_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_ROTATE_GRID,
GimpToolRotateGridClass))
+
+
+typedef struct _GimpToolRotateGrid GimpToolRotateGrid;
+typedef struct _GimpToolRotateGridPrivate GimpToolRotateGridPrivate;
+typedef struct _GimpToolRotateGridClass GimpToolRotateGridClass;
+
+struct _GimpToolRotateGrid
+{
+ GimpToolTransformGrid parent_instance;
+
+ GimpToolRotateGridPrivate *private;
+};
+
+struct _GimpToolRotateGridClass
+{
+ GimpToolTransformGridClass parent_class;
+};
+
+
+GType gimp_tool_rotate_grid_get_type (void) G_GNUC_CONST;
+
+GimpToolWidget * gimp_tool_rotate_grid_new (GimpDisplayShell *shell,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble pivot_x,
+ gdouble pivot_y,
+ gdouble angle);
+
+
+#endif /* __GIMP_TOOL_ROTATE_GRID_H__ */
diff --git a/app/display/gimptoolsheargrid.c b/app/display/gimptoolsheargrid.c
new file mode 100644
index 0000000..0827e90
--- /dev/null
+++ b/app/display/gimptoolsheargrid.c
@@ -0,0 +1,364 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptoolsheargrid.c
+ * Copyright (C) 2017 Michael Natterer <mitch gimp org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "display-types.h"
+
+#include "core/gimp-transform-utils.h"
+#include "core/gimp-utils.h"
+
+#include "gimpdisplayshell.h"
+#include "gimptoolsheargrid.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+ PROP_0,
+ PROP_ORIENTATION,
+ PROP_SHEAR_X,
+ PROP_SHEAR_Y
+};
+
+
+struct _GimpToolShearGridPrivate
+{
+ GimpOrientationType orientation;
+ gdouble shear_x;
+ gdouble shear_y;
+
+ gdouble last_x;
+ gdouble last_y;
+};
+
+
+/* local function prototypes */
+
+static void gimp_tool_shear_grid_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_tool_shear_grid_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gint gimp_tool_shear_grid_button_press (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonPressType press_type);
+static void gimp_tool_shear_grid_motion (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state);
+
+
+G_DEFINE_TYPE (GimpToolShearGrid, gimp_tool_shear_grid,
+ GIMP_TYPE_TOOL_TRANSFORM_GRID)
+
+#define parent_class gimp_tool_shear_grid_parent_class
+
+
+static void
+gimp_tool_shear_grid_class_init (GimpToolShearGridClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);
+
+ object_class->set_property = gimp_tool_shear_grid_set_property;
+ object_class->get_property = gimp_tool_shear_grid_get_property;
+
+ widget_class->button_press = gimp_tool_shear_grid_button_press;
+ widget_class->motion = gimp_tool_shear_grid_motion;
+
+ g_object_class_install_property (object_class, PROP_ORIENTATION,
+ g_param_spec_enum ("orientation",
+ NULL, NULL,
+ GIMP_TYPE_ORIENTATION_TYPE,
+ GIMP_ORIENTATION_UNKNOWN,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_SHEAR_X,
+ g_param_spec_double ("shear-x",
+ 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_SHEAR_Y,
+ g_param_spec_double ("shear-y",
+ 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 (GimpToolShearGridPrivate));
+}
+
+static void
+gimp_tool_shear_grid_init (GimpToolShearGrid *grid)
+{
+ grid->private = G_TYPE_INSTANCE_GET_PRIVATE (grid,
+ GIMP_TYPE_TOOL_SHEAR_GRID,
+ GimpToolShearGridPrivate);
+
+ g_object_set (grid,
+ "inside-function", GIMP_TRANSFORM_FUNCTION_SHEAR,
+ "outside-function", GIMP_TRANSFORM_FUNCTION_SHEAR,
+ "use-corner-handles", FALSE,
+ "use-perspective-handles", FALSE,
+ "use-side-handles", FALSE,
+ "use-shear-handles", FALSE,
+ "use-center-handle", FALSE,
+ "use-pivot-handle", FALSE,
+ NULL);
+}
+
+static void
+gimp_tool_shear_grid_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpToolShearGrid *grid = GIMP_TOOL_SHEAR_GRID (object);
+ GimpToolShearGridPrivate *private = grid->private;
+
+ switch (property_id)
+ {
+ case PROP_ORIENTATION:
+ private->orientation = g_value_get_enum (value);
+ break;
+ case PROP_SHEAR_X:
+ private->shear_x = g_value_get_double (value);
+ break;
+ case PROP_SHEAR_Y:
+ private->shear_y = g_value_get_double (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_tool_shear_grid_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpToolShearGrid *grid = GIMP_TOOL_SHEAR_GRID (object);
+ GimpToolShearGridPrivate *private = grid->private;
+
+ switch (property_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, private->orientation);
+ break;
+ case PROP_SHEAR_X:
+ g_value_set_double (value, private->shear_x);
+ break;
+ case PROP_SHEAR_Y:
+ g_value_set_double (value, private->shear_y);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gint
+gimp_tool_shear_grid_button_press (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonPressType press_type)
+{
+ GimpToolShearGrid *grid = GIMP_TOOL_SHEAR_GRID (widget);
+ GimpToolShearGridPrivate *private = grid->private;
+
+ private->last_x = coords->x;
+ private->last_y = coords->y;
+
+ return 1;
+}
+
+void
+gimp_tool_shear_grid_motion (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state)
+{
+ GimpToolShearGrid *grid = GIMP_TOOL_SHEAR_GRID (widget);
+ GimpToolShearGridPrivate *private = grid->private;
+ gdouble diffx = coords->x - private->last_x;
+ gdouble diffy = coords->y - private->last_y;
+ gdouble amount = 0.0;
+ GimpMatrix3 transform;
+ GimpMatrix3 *t;
+ gdouble x1, y1;
+ gdouble x2, y2;
+ gdouble tx1, ty1;
+ gdouble tx2, ty2;
+ gdouble tx3, ty3;
+ gdouble tx4, ty4;
+ gdouble current_x;
+ gdouble current_y;
+
+ g_object_get (widget,
+ "transform", &t,
+ "x1", &x1,
+ "y1", &y1,
+ "x2", &x2,
+ "y2", &y2,
+ NULL);
+
+ gimp_matrix3_transform_point (t, x1, y1, &tx1, &ty1);
+ gimp_matrix3_transform_point (t, x2, y1, &tx2, &ty2);
+ gimp_matrix3_transform_point (t, x1, y2, &tx3, &ty3);
+ gimp_matrix3_transform_point (t, x2, y2, &tx4, &ty4);
+
+ g_free (t);
+
+ current_x = coords->x;
+ current_y = coords->y;
+
+ diffx = current_x - private->last_x;
+ diffy = current_y - private->last_y;
+
+ /* If we haven't yet decided on which way to control shearing
+ * decide using the maximum differential
+ */
+ if (private->orientation == GIMP_ORIENTATION_UNKNOWN)
+ {
+#define MIN_MOVE 5
+
+ if (ABS (diffx) > MIN_MOVE || ABS (diffy) > MIN_MOVE)
+ {
+ if (ABS (diffx) > ABS (diffy))
+ {
+ private->orientation = GIMP_ORIENTATION_HORIZONTAL;
+ private->shear_x = 0.0;
+ }
+ else
+ {
+ private->orientation = GIMP_ORIENTATION_VERTICAL;
+ private->shear_y = 0.0;
+ }
+ }
+ /* set the current coords to the last ones */
+ else
+ {
+ current_x = private->last_x;
+ current_y = private->last_y;
+ }
+ }
+
+ /* if the direction is known, keep track of the magnitude */
+ if (private->orientation == GIMP_ORIENTATION_HORIZONTAL)
+ {
+ if (current_y > (ty1 + ty3) / 2)
+ private->shear_x += diffx;
+ else
+ private->shear_x -= diffx;
+
+ amount = private->shear_x;
+ }
+ else if (private->orientation == GIMP_ORIENTATION_VERTICAL)
+ {
+ if (current_x > (tx1 + tx2) / 2)
+ private->shear_y += diffy;
+ else
+ private->shear_y -= diffy;
+
+ amount = private->shear_y;
+ }
+
+ gimp_matrix3_identity (&transform);
+ gimp_transform_matrix_shear (&transform,
+ x1, y1, x2 - x1, y2 - y1,
+ private->orientation, amount);
+
+ g_object_set (widget,
+ "transform", &transform,
+ "orientation", private->orientation,
+ "shear-x", private->shear_x,
+ "shear_y", private->shear_y,
+ NULL);
+
+ private->last_x = current_x;
+ private->last_y = current_y;
+}
+
+
+/* public functions */
+
+GimpToolWidget *
+gimp_tool_shear_grid_new (GimpDisplayShell *shell,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ GimpOrientationType orientation,
+ gdouble shear_x,
+ gdouble shear_y)
+{
+ GimpMatrix3 transform;
+ gdouble amount;
+
+ g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
+
+ if (orientation == GIMP_ORIENTATION_HORIZONTAL)
+ amount = shear_x;
+ else
+ amount = shear_y;
+
+ gimp_matrix3_identity (&transform);
+ gimp_transform_matrix_shear (&transform,
+ x1, y1, x2 - x1, y2 - y1,
+ orientation, amount);
+
+ return g_object_new (GIMP_TYPE_TOOL_SHEAR_GRID,
+ "shell", shell,
+ "transform", &transform,
+ "x1", x1,
+ "y1", y1,
+ "x2", x2,
+ "y2", y2,
+ "orientation", orientation,
+ "shear-x", shear_x,
+ "shear-y", shear_y,
+ NULL);
+}
diff --git a/app/display/gimptoolsheargrid.h b/app/display/gimptoolsheargrid.h
new file mode 100644
index 0000000..09ed502
--- /dev/null
+++ b/app/display/gimptoolsheargrid.h
@@ -0,0 +1,65 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptoolsheargrid.h
+ * Copyright (C) 2017 Michael Natterer <mitch gimp org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TOOL_SHEAR_GRID_H__
+#define __GIMP_TOOL_SHEAR_GRID_H__
+
+
+#include "gimptooltransformgrid.h"
+
+
+#define GIMP_TYPE_TOOL_SHEAR_GRID (gimp_tool_shear_grid_get_type ())
+#define GIMP_TOOL_SHEAR_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_SHEAR_GRID,
GimpToolShearGrid))
+#define GIMP_TOOL_SHEAR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_SHEAR_GRID,
GimpToolShearGridClass))
+#define GIMP_IS_TOOL_SHEAR_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_SHEAR_GRID))
+#define GIMP_IS_TOOL_SHEAR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_SHEAR_GRID))
+#define GIMP_TOOL_SHEAR_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_SHEAR_GRID,
GimpToolShearGridClass))
+
+
+typedef struct _GimpToolShearGrid GimpToolShearGrid;
+typedef struct _GimpToolShearGridPrivate GimpToolShearGridPrivate;
+typedef struct _GimpToolShearGridClass GimpToolShearGridClass;
+
+struct _GimpToolShearGrid
+{
+ GimpToolTransformGrid parent_instance;
+
+ GimpToolShearGridPrivate *private;
+};
+
+struct _GimpToolShearGridClass
+{
+ GimpToolTransformGridClass parent_class;
+};
+
+
+GType gimp_tool_shear_grid_get_type (void) G_GNUC_CONST;
+
+GimpToolWidget * gimp_tool_shear_grid_new (GimpDisplayShell *shell,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ GimpOrientationType orientation,
+ gdouble shear_x,
+ gdouble shear_y);
+
+
+#endif /* __GIMP_TOOL_SHEAR_GRID_H__ */
diff --git a/app/display/gimptooltransformgrid.c b/app/display/gimptooltransformgrid.c
new file mode 100644
index 0000000..cf7f99c
--- /dev/null
+++ b/app/display/gimptooltransformgrid.c
@@ -0,0 +1,2245 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptooltransformgrid.c
+ * Copyright (C) 2017 Michael Natterer <mitch gimp org>
+ *
+ * Based on GimpUnifiedTransformTool
+ * Copyright (C) 2011 Mikael Magnusson <mikachu src gnome org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#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 "gimpcanvastransformguides.h"
+#include "gimpdisplayshell.h"
+#include "gimptooltransformgrid.h"
+
+#include "gimp-intl.h"
+
+
+#define MIN_HANDLE_SIZE 6
+#define GIMP_TOOL_HANDLE_SIZE_LARGE 25 /* FIXME */
+
+
+enum
+{
+ PROP_0,
+ PROP_TRANSFORM,
+ PROP_X1,
+ PROP_Y1,
+ PROP_X2,
+ PROP_Y2,
+ PROP_PIVOT_X,
+ PROP_PIVOT_Y,
+ PROP_GUIDE_TYPE,
+ PROP_N_GUIDES,
+ PROP_INSIDE_FUNCTION,
+ PROP_OUTSIDE_FUNCTION,
+ PROP_USE_CORNER_HANDLES,
+ PROP_USE_PERSPECTIVE_HANDLES,
+ PROP_USE_SIDE_HANDLES,
+ PROP_USE_SHEAR_HANDLES,
+ PROP_USE_CENTER_HANDLE,
+ PROP_USE_PIVOT_HANDLE,
+ PROP_CONSTRAIN_MOVE,
+ PROP_CONSTRAIN_SCALE,
+ PROP_CONSTRAIN_ROTATE,
+ PROP_CONSTRAIN_SHEAR,
+ PROP_CONSTRAIN_PERSPECTIVE,
+ PROP_FROMPIVOT_SCALE,
+ PROP_FROMPIVOT_SHEAR,
+ PROP_FROMPIVOT_PERSPECTIVE,
+ PROP_CORNERSNAP,
+ PROP_FIXEDPIVOT
+};
+
+
+struct _GimpToolTransformGridPrivate
+{
+ GimpMatrix3 transform;
+ gdouble x1, y1;
+ gdouble x2, y2;
+ gdouble pivot_x;
+ gdouble pivot_y;
+ GimpGuidesType guide_type;
+ gint n_guides;
+ GimpTransformFunction inside_function;
+ GimpTransformFunction outside_function;
+ gboolean use_corner_handles;
+ gboolean use_perspective_handles;
+ gboolean use_side_handles;
+ gboolean use_shear_handles;
+ gboolean use_center_handle;
+ gboolean use_pivot_handle;
+ gboolean constrain_move;
+ gboolean constrain_scale;
+ gboolean constrain_rotate;
+ gboolean constrain_shear;
+ gboolean constrain_perspective;
+ gboolean frompivot_scale;
+ gboolean frompivot_shear;
+ gboolean frompivot_perspective;
+ gboolean cornersnap;
+ gboolean fixedpivot;
+
+ gdouble curx; /* current x coord */
+ gdouble cury; /* current y coord */
+
+ gdouble mousex; /* x coord where mouse was clicked */
+ gdouble mousey; /* y coord where mouse was clicked */
+
+ gdouble cx, cy; /* center point (for moving) */
+
+ /* transformed handle coords */
+ gdouble tx1, ty1;
+ gdouble tx2, ty2;
+ gdouble tx3, ty3;
+ gdouble tx4, ty4;
+ gdouble tcx, tcy;
+ gdouble tpx, tpy;
+
+ /* previous transformed handle coords */
+ gdouble prev_tx1, prev_ty1;
+ gdouble prev_tx2, prev_ty2;
+ gdouble prev_tx3, prev_ty3;
+ gdouble prev_tx4, prev_ty4;
+ gdouble prev_tcx, prev_tcy;
+ gdouble prev_tpx, prev_tpy;
+
+ GimpTransformHandle handle; /* current tool activity */
+
+ GimpCanvasItem *guides;
+ GimpCanvasItem *handles[GIMP_N_TRANSFORM_HANDLES];
+ GimpCanvasItem *center_items[2];
+ GimpCanvasItem *pivot_items[2];
+};
+
+
+/* local function prototypes */
+
+static void gimp_tool_transform_grid_constructed (GObject *object);
+static void gimp_tool_transform_grid_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_tool_transform_grid_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gimp_tool_transform_grid_changed (GimpToolWidget *widget);
+static gint gimp_tool_transform_grid_button_press (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonPressType press_type);
+static void gimp_tool_transform_grid_button_release (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonReleaseType release_type);
+static void gimp_tool_transform_grid_motion (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state);
+static void gimp_tool_transform_grid_hover (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ gboolean proximity);
+static gboolean gimp_tool_transform_grid_get_cursor (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpCursorType *cursor,
+ GimpToolCursorType *tool_cursor,
+ GimpCursorModifier *cursor_modifier);
+
+static void gimp_tool_transform_grid_update_hilight (GimpToolTransformGrid *grid);
+static void gimp_tool_transform_grid_update_box (GimpToolTransformGrid *grid);
+static void gimp_tool_transform_grid_update_matrix (GimpToolTransformGrid *grid);
+static void gimp_tool_transform_grid_calc_handles (GimpToolTransformGrid *grid,
+ gint *handle_w,
+ gint *handle_h);
+
+
+G_DEFINE_TYPE (GimpToolTransformGrid, gimp_tool_transform_grid,
+ GIMP_TYPE_TOOL_WIDGET)
+
+#define parent_class gimp_tool_transform_grid_parent_class
+
+
+static void
+gimp_tool_transform_grid_class_init (GimpToolTransformGridClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);
+
+ object_class->constructed = gimp_tool_transform_grid_constructed;
+ object_class->set_property = gimp_tool_transform_grid_set_property;
+ object_class->get_property = gimp_tool_transform_grid_get_property;
+
+ widget_class->changed = gimp_tool_transform_grid_changed;
+ widget_class->button_press = gimp_tool_transform_grid_button_press;
+ widget_class->button_release = gimp_tool_transform_grid_button_release;
+ widget_class->motion = gimp_tool_transform_grid_motion;
+ widget_class->hover = gimp_tool_transform_grid_hover;
+ widget_class->get_cursor = gimp_tool_transform_grid_get_cursor;
+
+ g_object_class_install_property (object_class, PROP_TRANSFORM,
+ gimp_param_spec_matrix3 ("transform",
+ NULL, NULL,
+ NULL,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_X1,
+ g_param_spec_double ("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_Y1,
+ g_param_spec_double ("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_X2,
+ g_param_spec_double ("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_Y2,
+ g_param_spec_double ("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_PIVOT_X,
+ g_param_spec_double ("pivot-x",
+ 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_PIVOT_Y,
+ g_param_spec_double ("pivot-y",
+ 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_GUIDE_TYPE,
+ g_param_spec_enum ("guide-type", NULL, NULL,
+ GIMP_TYPE_GUIDES_TYPE,
+ GIMP_GUIDES_NONE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_N_GUIDES,
+ g_param_spec_int ("n-guides", NULL, NULL,
+ 1, 128, 4,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_INSIDE_FUNCTION,
+ g_param_spec_enum ("inside-function",
+ NULL, NULL,
+ GIMP_TYPE_TRANSFORM_FUNCTION,
+ GIMP_TRANSFORM_FUNCTION_MOVE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_OUTSIDE_FUNCTION,
+ g_param_spec_enum ("outside-function",
+ NULL, NULL,
+ GIMP_TYPE_TRANSFORM_FUNCTION,
+ GIMP_TRANSFORM_FUNCTION_ROTATE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_USE_CORNER_HANDLES,
+ g_param_spec_boolean ("use-corner-handles",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_USE_PERSPECTIVE_HANDLES,
+ g_param_spec_boolean ("use-perspective-handles",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_USE_SIDE_HANDLES,
+ g_param_spec_boolean ("use-side-handles",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_USE_SHEAR_HANDLES,
+ g_param_spec_boolean ("use-shear-handles",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_USE_CENTER_HANDLE,
+ g_param_spec_boolean ("use-center-handle",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_USE_PIVOT_HANDLE,
+ g_param_spec_boolean ("use-pivot-handle",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_CONSTRAIN_MOVE,
+ g_param_spec_boolean ("constrain-move",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_CONSTRAIN_SCALE,
+ g_param_spec_boolean ("constrain-scale",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_CONSTRAIN_ROTATE,
+ g_param_spec_boolean ("constrain-rotate",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_CONSTRAIN_SHEAR,
+ g_param_spec_boolean ("constrain-shear",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_CONSTRAIN_PERSPECTIVE,
+ g_param_spec_boolean ("constrain-perspective",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_FROMPIVOT_SCALE,
+ g_param_spec_boolean ("frompivot-scale",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_FROMPIVOT_SHEAR,
+ g_param_spec_boolean ("frompivot-shear",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_FROMPIVOT_PERSPECTIVE,
+ g_param_spec_boolean ("frompivot-perspective",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_CORNERSNAP,
+ g_param_spec_boolean ("cornersnap",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class, PROP_FIXEDPIVOT,
+ g_param_spec_boolean ("fixedpivot",
+ NULL, NULL,
+ FALSE,
+ GIMP_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_type_class_add_private (klass, sizeof (GimpToolTransformGridPrivate));
+}
+
+static void
+gimp_tool_transform_grid_init (GimpToolTransformGrid *grid)
+{
+ grid->private = G_TYPE_INSTANCE_GET_PRIVATE (grid,
+ GIMP_TYPE_TOOL_TRANSFORM_GRID,
+ GimpToolTransformGridPrivate);
+}
+
+static void
+gimp_tool_transform_grid_constructed (GObject *object)
+{
+ GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (object);
+ GimpToolWidget *widget = GIMP_TOOL_WIDGET (object);
+ GimpToolTransformGridPrivate *private = grid->private;
+ GimpCanvasGroup *stroke_group;
+ gint i;
+
+ G_OBJECT_CLASS (parent_class)->constructed (object);
+
+ private->guides = gimp_tool_widget_add_transform_guides (widget,
+ &private->transform,
+ private->x1,
+ private->y1,
+ private->x2,
+ private->y2,
+ private->guide_type,
+ private->n_guides);
+
+ for (i = 0; i < 4; i++)
+ {
+ /* draw the scale handles */
+ private->handles[GIMP_TRANSFORM_HANDLE_NW + i] =
+ gimp_tool_widget_add_handle (widget,
+ GIMP_HANDLE_SQUARE,
+ 0, 0, 10, 10,
+ GIMP_HANDLE_ANCHOR_CENTER);
+
+ /* draw the perspective handles */
+ private->handles[GIMP_TRANSFORM_HANDLE_NW_P + i] =
+ gimp_tool_widget_add_handle (widget,
+ GIMP_HANDLE_DIAMOND,
+ 0, 0, 10, 10,
+ GIMP_HANDLE_ANCHOR_CENTER);
+
+ /* draw the side handles */
+ private->handles[GIMP_TRANSFORM_HANDLE_N + i] =
+ gimp_tool_widget_add_handle (widget,
+ GIMP_HANDLE_SQUARE,
+ 0, 0, 10, 10,
+ GIMP_HANDLE_ANCHOR_CENTER);
+
+ /* draw the shear handles */
+ private->handles[GIMP_TRANSFORM_HANDLE_N_S + i] =
+ gimp_tool_widget_add_handle (widget,
+ GIMP_HANDLE_FILLED_DIAMOND,
+ 0, 0, 10, 10,
+ GIMP_HANDLE_ANCHOR_CENTER);
+ }
+
+ /* draw the rotation center axis handle */
+ stroke_group = gimp_tool_widget_add_stroke_group (widget);
+
+ private->handles[GIMP_TRANSFORM_HANDLE_PIVOT] =
+ GIMP_CANVAS_ITEM (stroke_group);
+
+ gimp_tool_widget_push_group (widget, stroke_group);
+
+ private->pivot_items[0] =
+ gimp_tool_widget_add_handle (widget,
+ GIMP_HANDLE_CIRCLE,
+ 0, 0, 10, 10,
+ GIMP_HANDLE_ANCHOR_CENTER);
+ private->pivot_items[1] =
+ gimp_tool_widget_add_handle (widget,
+ GIMP_HANDLE_CROSS,
+ 0, 0, 10, 10,
+ GIMP_HANDLE_ANCHOR_CENTER);
+
+ gimp_tool_widget_pop_group (widget);
+
+ /* draw the center handle */
+ stroke_group = gimp_tool_widget_add_stroke_group (widget);
+
+ private->handles[GIMP_TRANSFORM_HANDLE_CENTER] =
+ GIMP_CANVAS_ITEM (stroke_group);
+
+ gimp_tool_widget_push_group (widget, stroke_group);
+
+ private->center_items[0] =
+ gimp_tool_widget_add_handle (widget,
+ GIMP_HANDLE_SQUARE,
+ 0, 0, 10, 10,
+ GIMP_HANDLE_ANCHOR_CENTER);
+ private->center_items[1] =
+ gimp_tool_widget_add_handle (widget,
+ GIMP_HANDLE_CROSS,
+ 0, 0, 10, 10,
+ GIMP_HANDLE_ANCHOR_CENTER);
+
+ gimp_tool_widget_pop_group (widget);
+
+ gimp_tool_transform_grid_changed (widget);
+}
+
+static void
+gimp_tool_transform_grid_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (object);
+ GimpToolTransformGridPrivate *private = grid->private;
+ gboolean box = FALSE;
+
+ switch (property_id)
+ {
+ case PROP_TRANSFORM:
+ {
+ GimpMatrix3 *transform = g_value_get_boxed (value);
+
+ if (transform)
+ private->transform = *transform;
+ else
+ gimp_matrix3_identity (&private->transform);
+ }
+ break;
+
+ case PROP_X1:
+ private->x1 = g_value_get_double (value);
+ box = TRUE;
+ break;
+ case PROP_Y1:
+ private->y1 = g_value_get_double (value);
+ box = TRUE;
+ break;
+ case PROP_X2:
+ private->x2 = g_value_get_double (value);
+ box = TRUE;
+ break;
+ case PROP_Y2:
+ private->y2 = g_value_get_double (value);
+ box = TRUE;
+ break;
+
+ case PROP_PIVOT_X:
+ private->pivot_x = g_value_get_double (value);
+ break;
+ case PROP_PIVOT_Y:
+ private->pivot_y = g_value_get_double (value);
+ break;
+
+ case PROP_GUIDE_TYPE:
+ private->guide_type = g_value_get_enum (value);
+ break;
+ case PROP_N_GUIDES:
+ private->n_guides = g_value_get_int (value);
+ break;
+
+ case PROP_INSIDE_FUNCTION:
+ private->inside_function = g_value_get_enum (value);
+ break;
+ case PROP_OUTSIDE_FUNCTION:
+ private->outside_function = g_value_get_enum (value);
+ break;
+
+ case PROP_USE_CORNER_HANDLES:
+ private->use_corner_handles = g_value_get_boolean (value);
+ break;
+ case PROP_USE_PERSPECTIVE_HANDLES:
+ private->use_perspective_handles = g_value_get_boolean (value);
+ break;
+ case PROP_USE_SIDE_HANDLES:
+ private->use_side_handles = g_value_get_boolean (value);
+ break;
+ case PROP_USE_SHEAR_HANDLES:
+ private->use_shear_handles = g_value_get_boolean (value);
+ break;
+ case PROP_USE_CENTER_HANDLE:
+ private->use_center_handle = g_value_get_boolean (value);
+ break;
+ case PROP_USE_PIVOT_HANDLE:
+ private->use_pivot_handle = g_value_get_boolean (value);
+ break;
+
+ case PROP_CONSTRAIN_MOVE:
+ private->constrain_move = g_value_get_boolean (value);
+ break;
+ case PROP_CONSTRAIN_SCALE:
+ private->constrain_scale = g_value_get_boolean (value);
+ break;
+ case PROP_CONSTRAIN_ROTATE:
+ private->constrain_rotate = g_value_get_boolean (value);
+ break;
+ case PROP_CONSTRAIN_SHEAR:
+ private->constrain_shear = g_value_get_boolean (value);
+ break;
+ case PROP_CONSTRAIN_PERSPECTIVE:
+ private->constrain_perspective = g_value_get_boolean (value);
+ break;
+
+ case PROP_FROMPIVOT_SCALE:
+ private->frompivot_scale = g_value_get_boolean (value);
+ break;
+ case PROP_FROMPIVOT_SHEAR:
+ private->frompivot_shear = g_value_get_boolean (value);
+ break;
+ case PROP_FROMPIVOT_PERSPECTIVE:
+ private->frompivot_perspective = g_value_get_boolean (value);
+ break;
+
+ case PROP_CORNERSNAP:
+ private->cornersnap = g_value_get_boolean (value);
+ break;
+ case PROP_FIXEDPIVOT:
+ private->fixedpivot = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+
+ if (box)
+ {
+ private->cx = (private->x1 + private->x2) / 2.0;
+ private->cy = (private->y1 + private->y2) / 2.0;
+ }
+}
+
+static void
+gimp_tool_transform_grid_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (object);
+ GimpToolTransformGridPrivate *private = grid->private;
+
+ switch (property_id)
+ {
+ case PROP_TRANSFORM:
+ g_value_set_boxed (value, &private->transform);
+ break;
+
+ case PROP_X1:
+ g_value_set_double (value, private->x1);
+ break;
+ case PROP_Y1:
+ g_value_set_double (value, private->y1);
+ break;
+ case PROP_X2:
+ g_value_set_double (value, private->x2);
+ break;
+ case PROP_Y2:
+ g_value_set_double (value, private->y2);
+ break;
+
+ case PROP_PIVOT_X:
+ g_value_set_double (value, private->pivot_x);
+ break;
+ case PROP_PIVOT_Y:
+ g_value_set_double (value, private->pivot_y);
+ break;
+
+ case PROP_GUIDE_TYPE:
+ g_value_set_enum (value, private->guide_type);
+ break;
+ case PROP_N_GUIDES:
+ g_value_set_int (value, private->n_guides);
+ break;
+
+ case PROP_INSIDE_FUNCTION:
+ g_value_set_enum (value, private->inside_function);
+ break;
+ case PROP_OUTSIDE_FUNCTION:
+ g_value_set_enum (value, private->outside_function);
+ break;
+
+ case PROP_USE_CORNER_HANDLES:
+ g_value_set_boolean (value, private->use_corner_handles);
+ break;
+ case PROP_USE_PERSPECTIVE_HANDLES:
+ g_value_set_boolean (value, private->use_perspective_handles);
+ break;
+ case PROP_USE_SIDE_HANDLES:
+ g_value_set_boolean (value, private->use_side_handles);
+ break;
+ case PROP_USE_SHEAR_HANDLES:
+ g_value_set_boolean (value, private->use_shear_handles);
+ break;
+ case PROP_USE_CENTER_HANDLE:
+ g_value_set_boolean (value, private->use_center_handle);
+ break;
+ case PROP_USE_PIVOT_HANDLE:
+ g_value_set_boolean (value, private->use_pivot_handle);
+ break;
+
+ case PROP_CONSTRAIN_MOVE:
+ g_value_set_boolean (value, private->constrain_move);
+ break;
+ case PROP_CONSTRAIN_SCALE:
+ g_value_set_boolean (value, private->constrain_scale);
+ break;
+ case PROP_CONSTRAIN_ROTATE:
+ g_value_set_boolean (value, private->constrain_rotate);
+ break;
+ case PROP_CONSTRAIN_SHEAR:
+ g_value_set_boolean (value, private->constrain_shear);
+ break;
+ case PROP_CONSTRAIN_PERSPECTIVE:
+ g_value_set_boolean (value, private->constrain_perspective);
+ break;
+
+ case PROP_FROMPIVOT_SCALE:
+ g_value_set_boolean (value, private->frompivot_scale);
+ break;
+ case PROP_FROMPIVOT_SHEAR:
+ g_value_set_boolean (value, private->frompivot_shear);
+ break;
+ case PROP_FROMPIVOT_PERSPECTIVE:
+ g_value_set_boolean (value, private->frompivot_perspective);
+ break;
+
+ case PROP_CORNERSNAP:
+ g_value_set_boolean (value, private->cornersnap);
+ break;
+ case PROP_FIXEDPIVOT:
+ g_value_set_boolean (value, private->fixedpivot);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+transform_is_convex (GimpVector2 *pos)
+{
+ return gimp_transform_polygon_is_convex (pos[0].x, pos[0].y,
+ pos[1].x, pos[1].y,
+ pos[2].x, pos[2].y,
+ pos[3].x, pos[3].y);
+}
+
+static inline gboolean
+vectorisnull (GimpVector2 v)
+{
+ return ((v.x == 0.0) && (v.y == 0.0));
+}
+
+static inline gdouble
+dotprod (GimpVector2 a,
+ GimpVector2 b)
+{
+ return a.x * b.x + a.y * b.y;
+}
+
+static inline gdouble
+norm (GimpVector2 a)
+{
+ return sqrt (dotprod (a, a));
+}
+
+static inline GimpVector2
+vectorsubtract (GimpVector2 a,
+ GimpVector2 b)
+{
+ GimpVector2 c;
+
+ c.x = a.x - b.x;
+ c.y = a.y - b.y;
+
+ return c;
+}
+
+static inline GimpVector2
+vectoradd (GimpVector2 a,
+ GimpVector2 b)
+{
+ GimpVector2 c;
+
+ c.x = a.x + b.x;
+ c.y = a.y + b.y;
+
+ return c;
+}
+
+static inline GimpVector2
+scalemult (GimpVector2 a,
+ gdouble b)
+{
+ GimpVector2 c;
+
+ c.x = a.x * b;
+ c.y = a.y * b;
+
+ return c;
+}
+
+static inline GimpVector2
+vectorproject (GimpVector2 a,
+ GimpVector2 b)
+{
+ return scalemult (b, dotprod (a, b) / dotprod (b, b));
+}
+
+/* finds the clockwise angle between the vectors given, 0-2π */
+static inline gdouble
+calcangle (GimpVector2 a,
+ GimpVector2 b)
+{
+ gdouble angle, angle2;
+ gdouble length;
+
+ if (vectorisnull (a) || vectorisnull (b))
+ return 0.0;
+
+ length = norm (a) * norm (b);
+
+ angle = acos (dotprod (a, b)/length);
+ angle2 = b.y;
+ b.y = -b.x;
+ b.x = angle2;
+ angle2 = acos (dotprod (a, b)/length);
+
+ return ((angle2 > G_PI / 2.0) ? angle : 2.0 * G_PI - angle);
+}
+
+static inline GimpVector2
+rotate2d (GimpVector2 p,
+ gdouble angle)
+{
+ GimpVector2 ret;
+
+ ret.x = cos (angle) * p.x-sin (angle) * p.y;
+ ret.y = sin (angle) * p.x+cos (angle) * p.y;
+
+ return ret;
+}
+
+static inline GimpVector2
+lineintersect (GimpVector2 p1, GimpVector2 p2,
+ GimpVector2 q1, GimpVector2 q2)
+{
+ gdouble denom, u;
+ GimpVector2 p;
+
+ denom = (q2.y - q1.y) * (p2.x - p1.x) - (q2.x - q1.x) * (p2.y - p1.y);
+ if (denom == 0.0)
+ {
+ p.x = (p1.x + p2.x + q1.x + q2.x) / 4;
+ p.y = (p1.y + p2.y + q1.y + q2.y) / 4;
+ }
+ else
+ {
+ u = (q2.x - q1.x) * (p1.y - q1.y) - (q2.y - q1.y) * (p1.x - q1.x);
+ u /= denom;
+
+ p.x = p1.x + u * (p2.x - p1.x);
+ p.y = p1.y + u * (p2.y - p1.y);
+ }
+
+ return p;
+}
+
+static inline GimpVector2
+get_pivot_delta (GimpToolTransformGrid *grid,
+ GimpVector2 *oldpos,
+ GimpVector2 *newpos,
+ GimpVector2 pivot)
+{
+ GimpToolTransformGridPrivate *private = grid->private;
+ GimpMatrix3 transform_before;
+ GimpMatrix3 transform_after;
+ GimpVector2 delta;
+
+ gimp_matrix3_identity (&transform_before);
+ gimp_matrix3_identity (&transform_after);
+
+ gimp_transform_matrix_perspective (&transform_before,
+ private->x1,
+ private->y1,
+ private->x2 - private->x1,
+ private->y2 - private->y1,
+ oldpos[0].x, oldpos[0].y,
+ oldpos[1].x, oldpos[1].y,
+ oldpos[2].x, oldpos[2].y,
+ oldpos[3].x, oldpos[3].y);
+ gimp_transform_matrix_perspective (&transform_after,
+ private->x1,
+ private->y1,
+ private->x2 - private->x1,
+ private->y2 - private->y1,
+ newpos[0].x, newpos[0].y,
+ newpos[1].x, newpos[1].y,
+ newpos[2].x, newpos[2].y,
+ newpos[3].x, newpos[3].y);
+ gimp_matrix3_invert (&transform_before);
+ gimp_matrix3_mult (&transform_after, &transform_before);
+ gimp_matrix3_transform_point (&transform_before,
+ pivot.x, pivot.y, &delta.x, &delta.y);
+
+ delta = vectorsubtract (delta, pivot);
+
+ return delta;
+}
+
+static gboolean
+point_is_inside_polygon (gint n,
+ gdouble *x,
+ gdouble *y,
+ gdouble px,
+ gdouble py)
+{
+ gint i, j;
+ gboolean odd = FALSE;
+
+ for (i = 0, j = n - 1; i < n; j = i++)
+ {
+ if ((y[i] < py && y[j] >= py) ||
+ (y[j] < py && y[i] >= py))
+ {
+ if (x[i] + (py - y[i]) / (y[j] - y[i]) * (x[j] - x[i]) < px)
+ odd = !odd;
+ }
+ }
+
+ return odd;
+}
+
+static gboolean
+point_is_inside_polygon_pos (GimpVector2 *pos,
+ GimpVector2 point)
+{
+ return point_is_inside_polygon (4,
+ (gdouble[4]){ pos[0].x, pos[1].x,
+ pos[3].x, pos[2].x },
+ (gdouble[4]){ pos[0].y, pos[1].y,
+ pos[3].y, pos[2].y },
+ point.x, point.y);
+}
+
+static void
+get_handle_geometry (GimpToolTransformGrid *grid,
+ GimpVector2 *position,
+ gdouble *angle)
+{
+ GimpToolTransformGridPrivate *private = grid->private;
+
+ GimpVector2 o[] = { { .x = private->tx1, .y = private->ty1 },
+ { .x = private->tx2, .y = private->ty2 },
+ { .x = private->tx3, .y = private->ty3 },
+ { .x = private->tx4, .y = private->ty4 } };
+ GimpVector2 right = { .x = 1.0, .y = 0.0 };
+ GimpVector2 up = { .x = 0.0, .y = 1.0 };
+
+ if (position)
+ {
+ position[0] = o[0];
+ position[1] = o[1];
+ position[2] = o[2];
+ position[3] = o[3];
+ }
+
+ angle[0] = calcangle (vectorsubtract (o[1], o[0]), right);
+ angle[1] = calcangle (vectorsubtract (o[3], o[2]), right);
+ angle[2] = calcangle (vectorsubtract (o[3], o[1]), up);
+ angle[3] = calcangle (vectorsubtract (o[2], o[0]), up);
+
+ angle[4] = (angle[0] + angle[3]) / 2.0;
+ angle[5] = (angle[0] + angle[2]) / 2.0;
+ angle[6] = (angle[1] + angle[3]) / 2.0;
+ angle[7] = (angle[1] + angle[2]) / 2.0;
+
+ angle[8] = (angle[0] + angle[1] + angle[2] + angle[3]) / 4.0;
+}
+
+static void
+gimp_tool_transform_grid_changed (GimpToolWidget *widget)
+{
+ GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget);
+ GimpToolTransformGridPrivate *private = grid->private;
+ gdouble angle[9];
+ GimpVector2 o[4], t[4];
+ gint handle_w;
+ gint handle_h;
+ gint d, i;
+
+ gimp_tool_transform_grid_update_box (grid);
+
+ gimp_canvas_transform_guides_set (private->guides,
+ &private->transform,
+ private->x1,
+ private->y1,
+ private->x2,
+ private->y2,
+ private->guide_type,
+ private->n_guides);
+
+ get_handle_geometry (grid, o, angle);
+ gimp_tool_transform_grid_calc_handles (grid, &handle_w, &handle_h);
+
+ for (i = 0; i < 4; i++)
+ {
+ GimpCanvasItem *h;
+ gdouble factor;
+
+ /* the scale handles */
+ factor = 1.0;
+ if (private->use_perspective_handles)
+ factor = 1.5;
+
+ h = private->handles[GIMP_TRANSFORM_HANDLE_NW + i];
+ gimp_canvas_handle_set_position (h, o[i].x, o[i].y);
+ gimp_canvas_handle_set_size (h, handle_w * factor, handle_h * factor);
+ gimp_canvas_handle_set_angles (h, angle[i + 4], 0.0);
+ gimp_canvas_item_set_visible (h, private->use_corner_handles);
+
+ /* the perspective handles */
+ factor = 1.0;
+ if (private->use_corner_handles)
+ factor = 0.8;
+
+ h = private->handles[GIMP_TRANSFORM_HANDLE_NW_P + i];
+ gimp_canvas_handle_set_position (h, o[i].x, o[i].y);
+ gimp_canvas_handle_set_size (h, handle_w * factor, handle_h * factor);
+ gimp_canvas_handle_set_angles (h, angle[i + 4], 0.0);
+ gimp_canvas_item_set_visible (h, private->use_perspective_handles);
+ }
+
+ /* draw the side handles */
+ t[0] = scalemult (vectoradd (o[0], o[1]), 0.5);
+ t[1] = scalemult (vectoradd (o[2], o[3]), 0.5);
+ t[2] = scalemult (vectoradd (o[1], o[3]), 0.5);
+ t[3] = scalemult (vectoradd (o[2], o[0]), 0.5);
+
+ for (i = 0; i < 4; i++)
+ {
+ GimpCanvasItem *h;
+
+ h = private->handles[GIMP_TRANSFORM_HANDLE_N + i];
+ gimp_canvas_handle_set_position (h, t[i].x, t[i].y);
+ gimp_canvas_handle_set_size (h, handle_w, handle_h);
+ gimp_canvas_handle_set_angles (h, angle[i], 0.0);
+ gimp_canvas_item_set_visible (h, private->use_side_handles);
+ }
+
+ /* draw the shear handles */
+ t[0] = scalemult (vectoradd ( o[0] , scalemult (o[1], 3.0)),
+ 0.25);
+ t[1] = scalemult (vectoradd (scalemult (o[2], 3.0), o[3] ),
+ 0.25);
+ t[2] = scalemult (vectoradd ( o[1] , scalemult (o[3], 3.0)),
+ 0.25);
+ t[3] = scalemult (vectoradd (scalemult (o[2], 3.0), o[0] ),
+ 0.25);
+
+ for (i = 0; i < 4; i++)
+ {
+ GimpCanvasItem *h;
+
+ h = private->handles[GIMP_TRANSFORM_HANDLE_N_S + i];
+ gimp_canvas_handle_set_position (h, t[i].x, t[i].y);
+ gimp_canvas_handle_set_size (h, handle_w, handle_h);
+ gimp_canvas_handle_set_angles (h, angle[i], 0.0);
+ gimp_canvas_item_set_visible (h, private->use_shear_handles);
+ }
+
+ d = MIN (handle_w, handle_h);
+ if (private->use_center_handle)
+ d *= 2; /* so you can grab it from under the center handle */
+
+ gimp_canvas_item_set_visible (private->handles[GIMP_TRANSFORM_HANDLE_PIVOT],
+ private->use_pivot_handle);
+
+ gimp_canvas_handle_set_position (private->pivot_items[0],
+ private->tpx, private->tpy);
+ gimp_canvas_handle_set_size (private->pivot_items[0], d, d);
+
+ gimp_canvas_handle_set_position (private->pivot_items[1],
+ private->tpx, private->tpy);
+ gimp_canvas_handle_set_size (private->pivot_items[1], d, d);
+
+ d = MIN (handle_w, handle_h);
+
+ gimp_canvas_item_set_visible (private->handles[GIMP_TRANSFORM_HANDLE_CENTER],
+ private->use_center_handle);
+
+ gimp_canvas_handle_set_position (private->center_items[0],
+ private->tcx, private->tcy);
+ gimp_canvas_handle_set_size (private->center_items[0], d, d);
+ gimp_canvas_handle_set_angles (private->center_items[0], angle[8], 0.0);
+
+ gimp_canvas_handle_set_position (private->center_items[1],
+ private->tcx, private->tcy);
+ gimp_canvas_handle_set_size (private->center_items[1], d, d);
+ gimp_canvas_handle_set_angles (private->center_items[1], angle[8], 0.0);
+
+ gimp_tool_transform_grid_update_hilight (grid);
+}
+
+gint
+gimp_tool_transform_grid_button_press (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonPressType press_type)
+{
+ GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget);
+ GimpToolTransformGridPrivate *private = grid->private;
+
+ private->mousex = coords->x;
+ private->mousey = coords->y;
+
+ if (private->handle != GIMP_TRANSFORM_HANDLE_NONE)
+ {
+ if (private->handles[private->handle])
+ {
+ GimpCanvasItem *handle;
+ gdouble x, y;
+
+ switch (private->handle)
+ {
+ case GIMP_TRANSFORM_HANDLE_CENTER:
+ handle = private->center_items[0];
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_PIVOT:
+ handle = private->pivot_items[0];
+ break;
+
+ default:
+ handle = private->handles[private->handle];
+ break;
+ }
+
+ gimp_canvas_handle_get_position (handle, &x, &y);
+
+ gimp_tool_widget_snap_offsets (widget,
+ SIGNED_ROUND (x - coords->x),
+ SIGNED_ROUND (y - coords->y),
+ 0, 0);
+ }
+ else
+ {
+ gimp_tool_widget_snap_offsets (widget, 0, 0, 0, 0);
+ }
+
+ private->prev_tx1 = private->tx1;
+ private->prev_ty1 = private->ty1;
+ private->prev_tx2 = private->tx2;
+ private->prev_ty2 = private->ty2;
+ private->prev_tx3 = private->tx3;
+ private->prev_ty3 = private->ty3;
+ private->prev_tx4 = private->tx4;
+ private->prev_ty4 = private->ty4;
+ private->prev_tpx = private->tpx;
+ private->prev_tpy = private->tpy;
+ private->prev_tcx = private->tcx;
+ private->prev_tcy = private->tcy;
+
+ return private->handle;
+ }
+
+ gimp_tool_widget_snap_offsets (widget, 0, 0, 0, 0);
+
+ return 0;
+}
+
+void
+gimp_tool_transform_grid_button_release (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state,
+ GimpButtonReleaseType release_type)
+{
+}
+
+void
+gimp_tool_transform_grid_motion (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ guint32 time,
+ GdkModifierType state)
+{
+ GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget);
+ GimpToolTransformGridPrivate *private = grid->private;
+ gdouble *x[4], *y[4];
+ gdouble *newpivot_x, *newpivot_y;
+
+ GimpVector2 oldpos[5], newpos[4];
+ GimpVector2 cur = { .x = coords->x,
+ .y = coords->y };
+ GimpVector2 mouse = { .x = private->mousex,
+ .y = private->mousey };
+ GimpVector2 d;
+ GimpVector2 pivot;
+
+ gboolean fixedpivot = private->fixedpivot;
+ GimpTransformHandle handle = private->handle;
+ gint i;
+
+ private->curx = coords->x;
+ private->cury = coords->y;
+
+ x[0] = &private->tx1;
+ y[0] = &private->ty1;
+ x[1] = &private->tx2;
+ y[1] = &private->ty2;
+ x[2] = &private->tx3;
+ y[2] = &private->ty3;
+ x[3] = &private->tx4;
+ y[3] = &private->ty4;
+
+ newpos[0].x = oldpos[0].x = private->prev_tx1;
+ newpos[0].y = oldpos[0].y = private->prev_ty1;
+ newpos[1].x = oldpos[1].x = private->prev_tx2;
+ newpos[1].y = oldpos[1].y = private->prev_ty2;
+ newpos[2].x = oldpos[2].x = private->prev_tx3;
+ newpos[2].y = oldpos[2].y = private->prev_ty3;
+ newpos[3].x = oldpos[3].x = private->prev_tx4;
+ newpos[3].y = oldpos[3].y = private->prev_ty4;
+
+ /* put center point in this array too */
+ oldpos[4].x = (oldpos[0].x + oldpos[1].x + oldpos[2].x + oldpos[3].x) / 4.;
+ oldpos[4].y = (oldpos[0].y + oldpos[1].y + oldpos[2].y + oldpos[3].y) / 4.;
+
+ d = vectorsubtract (cur, mouse);
+
+ newpivot_x = &private->tpx;
+ newpivot_y = &private->tpy;
+
+ pivot.x = private->prev_tpx;
+ pivot.y = private->prev_tpy;
+
+ /* move */
+ if (handle == GIMP_TRANSFORM_HANDLE_CENTER)
+ {
+ if (private->constrain_move)
+ {
+ /* snap to 45 degree vectors from starting point */
+ gdouble angle = 16.0 * calcangle ((GimpVector2) { 1.0, 0.0 },
+ d) / (2.0 * G_PI);
+ gdouble dist = norm (d) / sqrt (2);
+
+ if (angle < 1.0 || angle >= 15.0)
+ d.y = 0;
+ else if (angle < 3.0)
+ d.y = -(d.x = dist);
+ else if (angle < 5.0)
+ d.x = 0;
+ else if (angle < 7.0)
+ d.x = d.y = -dist;
+ else if (angle < 9.0)
+ d.y = 0;
+ else if (angle < 11.0)
+ d.x = -(d.y = dist);
+ else if (angle < 13.0)
+ d.x = 0;
+ else if (angle < 15.0)
+ d.x = d.y = dist;
+ }
+
+ for (i = 0; i < 4; i++)
+ newpos[i] = vectoradd (oldpos[i], d);
+ }
+
+ /* rotate */
+ if (handle == GIMP_TRANSFORM_HANDLE_ROTATION)
+ {
+ gdouble angle = calcangle (vectorsubtract (cur, pivot),
+ vectorsubtract (mouse, pivot));
+
+ if (private->constrain_rotate)
+ {
+ /* round to 15 degree multiple */
+ angle /= 2 * G_PI / 24.0;
+ angle = round (angle);
+ angle *= 2 * G_PI / 24.0;
+ }
+
+ for (i = 0; i < 4; i++)
+ newpos[i] = vectoradd (pivot,
+ rotate2d (vectorsubtract (oldpos[i], pivot),
+ angle));
+
+ fixedpivot = TRUE;
+ }
+
+ /* move rotation axis */
+ if (handle == GIMP_TRANSFORM_HANDLE_PIVOT)
+ {
+ pivot = vectoradd (pivot, d);
+
+ if (private->cornersnap)
+ {
+ /* snap to corner points and center */
+ gint closest = 0;
+ gdouble closest_dist = G_MAXDOUBLE, dist;
+
+ for (i = 0; i < 5; i++)
+ {
+ dist = norm (vectorsubtract (pivot, oldpos[i]));
+ if (dist < closest_dist)
+ {
+ closest_dist = dist;
+ closest = i;
+ }
+ }
+
+ if (closest_dist *
+ gimp_tool_widget_get_shell (widget)->scale_x < 50)
+ {
+ pivot = oldpos[closest];
+ }
+ }
+
+ fixedpivot = TRUE;
+ }
+
+ /* scaling via corner */
+ if (handle == GIMP_TRANSFORM_HANDLE_NW ||
+ handle == GIMP_TRANSFORM_HANDLE_NE ||
+ handle == GIMP_TRANSFORM_HANDLE_SE ||
+ handle == GIMP_TRANSFORM_HANDLE_SW)
+ {
+ /* Scaling through scale handles means translating one corner point,
+ * with all sides at constant angles.
+ */
+
+ gint this, left, right, opposite;
+
+ /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */
+ if (handle == GIMP_TRANSFORM_HANDLE_NW)
+ {
+ this = 0; left = 1; right = 2; opposite = 3;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_NE)
+ {
+ this = 1; left = 3; right = 0; opposite = 2;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_SW)
+ {
+ this = 2; left = 0; right = 3; opposite = 1;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_SE)
+ {
+ this = 3; left = 2; right = 1; opposite = 0;
+ }
+ else
+ g_assert_not_reached ();
+
+ /* when the keep aspect transformation constraint is enabled,
+ * the translation shall only be along the diagonal that runs
+ * trough this corner point.
+ */
+ if (private->constrain_scale)
+ {
+ /* restrict to movement along the diagonal */
+ GimpVector2 diag = vectorsubtract (oldpos[this], oldpos[opposite]);
+
+ d = vectorproject (d, diag);
+ }
+
+ /* Move the corner being interacted with */
+ /* rp---------tp
+ * / /\ <- d, the interaction vector
+ * / / tp
+ * op----------/
+ *
+ */
+ newpos[this] = vectoradd (oldpos[this], d);
+
+ /* Where the corner to the right and left would go, need these to form
+ * lines to intersect with the sides */
+ /* rp----------/
+ * /\ /\
+ * / nr / nt
+ * op----------lp
+ * \
+ * nl
+ */
+
+ newpos[right] = vectoradd (oldpos[right], d);
+ newpos[left] = vectoradd (oldpos[left], d);
+
+ /* Now we just need to find the intersection of op-rp and nr-nt.
+ * rp----------/
+ * / /
+ * / nr==========nt
+ * op----------/
+ *
+ */
+ newpos[right] = lineintersect (newpos[right], newpos[this],
+ oldpos[opposite], oldpos[right]);
+ newpos[left] = lineintersect (newpos[left], newpos[this],
+ oldpos[opposite], oldpos[left]);
+ /* /-----------/
+ * / /
+ * rp============nt
+ * op----------/
+ *
+ */
+
+ /*
+ *
+ * /--------------/
+ * /--------------/
+ *
+ */
+
+ if (private->frompivot_scale &&
+ transform_is_convex (newpos) &&
+ transform_is_convex (oldpos))
+ {
+ /* transform the pivot point before the interaction and
+ * after, and move everything by this difference
+ */
+ //TODO the handle doesn't actually end up where the mouse cursor is
+ GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
+ for (i = 0; i < 4; i++)
+ newpos[i] = vectorsubtract (newpos[i], delta);
+
+ fixedpivot = TRUE;
+ }
+ }
+
+ /* scaling via sides */
+ if (handle == GIMP_TRANSFORM_HANDLE_N ||
+ handle == GIMP_TRANSFORM_HANDLE_E ||
+ handle == GIMP_TRANSFORM_HANDLE_S ||
+ handle == GIMP_TRANSFORM_HANDLE_W)
+ {
+ gint this_l, this_r, opp_l, opp_r;
+ GimpVector2 side_l, side_r, midline;
+
+ /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */
+ if (handle == GIMP_TRANSFORM_HANDLE_N)
+ {
+ this_l = 1; this_r = 0;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_E)
+ {
+ this_l = 3; this_r = 1;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_S)
+ {
+ this_l = 2; this_r = 3;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_W)
+ {
+ this_l = 0; this_r = 2;
+ }
+ else
+ g_assert_not_reached ();
+
+ opp_l = 3 - this_r; opp_r = 3 - this_l;
+
+ side_l = vectorsubtract (oldpos[opp_l], oldpos[this_l]);
+ side_r = vectorsubtract (oldpos[opp_r], oldpos[this_r]);
+ midline = vectoradd (side_l, side_r);
+
+ /* restrict to movement along the midline */
+ d = vectorproject (d, midline);
+
+ if (private->constrain_scale)
+ {
+ GimpVector2 before, after, effective_pivot = pivot;
+ gdouble distance;
+
+ if (! private->frompivot_scale)
+ {
+ /* center of the opposite side is pivot */
+ effective_pivot = scalemult (vectoradd (oldpos[opp_l],
+ oldpos[opp_r]), 0.5);
+ }
+
+ /* get the difference between the distance from the pivot to
+ * where interaction started and the distance from the pivot
+ * to where cursor is now, and scale all corners distance
+ * from the pivot with this factor
+ */
+ before = vectorsubtract (effective_pivot, mouse);
+ after = vectorsubtract (effective_pivot, cur);
+ after = vectorproject (after, before);
+
+ distance = 0.5 * (after.x / before.x + after.y / before.y);
+
+ for (i = 0; i < 4; i++)
+ newpos[i] = vectoradd (effective_pivot,
+ scalemult (vectorsubtract (oldpos[i],
+ effective_pivot),
+ distance));
+ }
+ else
+ {
+ /* just move the side */
+ newpos[this_l] = vectoradd (oldpos[this_l], d);
+ newpos[this_r] = vectoradd (oldpos[this_r], d);
+ }
+
+ if (! private->constrain_scale &&
+ private->frompivot_scale &&
+ transform_is_convex (newpos) &&
+ transform_is_convex (oldpos))
+ {
+ GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
+ for (i = 0; i < 4; i++)
+ newpos[i] = vectorsubtract (newpos[i], delta);
+
+ fixedpivot = TRUE;
+ }
+ }
+
+ /* shear */
+ if (handle == GIMP_TRANSFORM_HANDLE_N_S ||
+ handle == GIMP_TRANSFORM_HANDLE_E_S ||
+ handle == GIMP_TRANSFORM_HANDLE_S_S ||
+ handle == GIMP_TRANSFORM_HANDLE_W_S)
+ {
+ gint this_l, this_r;
+
+ /* set up indices for this edge and the opposite edge */
+ if (handle == GIMP_TRANSFORM_HANDLE_N_S)
+ {
+ this_l = 1; this_r = 0;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_W_S)
+ {
+ this_l = 0; this_r = 2;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_S_S)
+ {
+ this_l = 2; this_r = 3;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_E_S)
+ {
+ this_l = 3; this_r = 1;
+ }
+ else
+ g_assert_not_reached ();
+
+ if (private->constrain_shear)
+ {
+ /* restrict to movement along the side */
+ GimpVector2 side = vectorsubtract (oldpos[this_r], oldpos[this_l]);
+
+ d = vectorproject (d, side);
+ }
+
+ newpos[this_l] = vectoradd (oldpos[this_l], d);
+ newpos[this_r] = vectoradd (oldpos[this_r], d);
+
+ if (private->frompivot_shear &&
+ transform_is_convex (newpos) &&
+ transform_is_convex (oldpos))
+ {
+ GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
+ for (i = 0; i < 4; i++)
+ newpos[i] = vectorsubtract (newpos[i], delta);
+
+ fixedpivot = TRUE;
+ }
+ }
+
+ /* perspective transform */
+ if (handle == GIMP_TRANSFORM_HANDLE_NW_P ||
+ handle == GIMP_TRANSFORM_HANDLE_NE_P ||
+ handle == GIMP_TRANSFORM_HANDLE_SE_P ||
+ handle == GIMP_TRANSFORM_HANDLE_SW_P)
+ {
+ gint this, left, right, opposite;
+
+ /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */
+ if (handle == GIMP_TRANSFORM_HANDLE_NW_P)
+ {
+ this = 0; left = 1; right = 2; opposite = 3;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_NE_P)
+ {
+ this = 1; left = 3; right = 0; opposite = 2;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_SW_P)
+ {
+ this = 2; left = 0; right = 3; opposite = 1;
+ }
+ else if (handle == GIMP_TRANSFORM_HANDLE_SE_P)
+ {
+ this = 3; left = 2; right = 1; opposite = 0;
+ }
+ else
+ g_assert_not_reached ();
+
+ if (private->constrain_perspective)
+ {
+ /* when the constrain transformation constraint is enabled,
+ * the translation shall only be either along the side
+ * angles of the two sides that run to this corner point, or
+ * along the diagonal that runs trough this corner point.
+ */
+ GimpVector2 proj[4];
+ gdouble rej[4];
+
+ for (i = 0; i < 4; i++)
+ {
+ if (i == this)
+ continue;
+
+ /* get the vectors along the sides and the diagonal */
+ proj[i] = vectorsubtract (oldpos[this], oldpos[i]);
+
+ /* project d on each candidate vector and see which has
+ * the shortest rejection
+ */
+ proj[i] = vectorproject (d, proj[i]);
+ rej[i] = norm (vectorsubtract (d, proj[i]));
+ }
+
+ if (rej[left] < rej[right] && rej[left] < rej[opposite])
+ d = proj[left];
+ else if (rej[right] < rej[opposite])
+ d = proj[right];
+ else
+ d = proj[opposite];
+ }
+
+ newpos[this] = vectoradd (oldpos[this], d);
+
+ if (private->frompivot_perspective &&
+ transform_is_convex (newpos) &&
+ transform_is_convex (oldpos))
+ {
+ GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
+
+ for (i = 0; i < 4; i++)
+ newpos[i] = vectorsubtract (newpos[i], delta);
+
+ fixedpivot = TRUE;
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ *x[i] = newpos[i].x;
+ *y[i] = newpos[i].y;
+ }
+
+ /* this will have been set to TRUE if an operation used the pivot in
+ * addition to being a user option
+ */
+ if (! fixedpivot &&
+ transform_is_convex (newpos) &&
+ transform_is_convex (oldpos) &&
+ point_is_inside_polygon_pos (oldpos, pivot))
+ {
+ GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
+ pivot = vectoradd (pivot, delta);
+ }
+
+ /* set unconditionally: if options get toggled during operation, we
+ * have to move pivot back
+ */
+ *newpivot_x = pivot.x;
+ *newpivot_y = pivot.y;
+
+ gimp_tool_transform_grid_update_matrix (grid);
+}
+
+static const gchar *
+get_friendly_operation_name (GimpTransformHandle handle)
+{
+ switch (handle)
+ {
+ case GIMP_TRANSFORM_HANDLE_NONE:
+ return "";
+ case GIMP_TRANSFORM_HANDLE_NW_P:
+ case GIMP_TRANSFORM_HANDLE_NE_P:
+ case GIMP_TRANSFORM_HANDLE_SW_P:
+ case GIMP_TRANSFORM_HANDLE_SE_P:
+ return _("Click-Drag to change perspective");
+ case GIMP_TRANSFORM_HANDLE_NW:
+ case GIMP_TRANSFORM_HANDLE_NE:
+ case GIMP_TRANSFORM_HANDLE_SW:
+ case GIMP_TRANSFORM_HANDLE_SE:
+ return _("Click-Drag to scale");
+ case GIMP_TRANSFORM_HANDLE_N:
+ case GIMP_TRANSFORM_HANDLE_S:
+ case GIMP_TRANSFORM_HANDLE_E:
+ case GIMP_TRANSFORM_HANDLE_W:
+ return _("Click-Drag to scale");
+ case GIMP_TRANSFORM_HANDLE_CENTER:
+ return _("Click-Drag to move");
+ case GIMP_TRANSFORM_HANDLE_PIVOT:
+ return _("Click-Drag to move the pivot point");
+ case GIMP_TRANSFORM_HANDLE_N_S:
+ case GIMP_TRANSFORM_HANDLE_S_S:
+ case GIMP_TRANSFORM_HANDLE_E_S:
+ case GIMP_TRANSFORM_HANDLE_W_S:
+ return _("Click-Drag to shear");
+ case GIMP_TRANSFORM_HANDLE_ROTATION:
+ return _("Click-Drag to rotate");
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static GimpTransformHandle
+gimp_tool_transform_get_area_handle (GimpToolTransformGrid *grid,
+ const GimpCoords *coords,
+ GimpTransformFunction function)
+{
+ GimpToolTransformGridPrivate *private = grid->private;
+ GimpTransformHandle handle = GIMP_TRANSFORM_HANDLE_NONE;
+
+ switch (function)
+ {
+ case GIMP_TRANSFORM_FUNCTION_MOVE:
+ handle = GIMP_TRANSFORM_HANDLE_CENTER;
+ break;
+
+ case GIMP_TRANSFORM_FUNCTION_ROTATE:
+ handle = GIMP_TRANSFORM_HANDLE_ROTATION;
+ break;
+
+ case GIMP_TRANSFORM_FUNCTION_SCALE:
+ case GIMP_TRANSFORM_FUNCTION_PERSPECTIVE:
+ {
+ gdouble closest_dist;
+ gdouble dist;
+
+ dist = gimp_canvas_item_transform_distance_square (private->guides,
+ coords->x, coords->y,
+ private->tx1,
+ private->ty1);
+ closest_dist = dist;
+ if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE)
+ handle = GIMP_TRANSFORM_HANDLE_NW_P;
+ else
+ handle = GIMP_TRANSFORM_HANDLE_NW;
+
+ dist = gimp_canvas_item_transform_distance_square (private->guides,
+ coords->x, coords->y,
+ private->tx2,
+ private->ty2);
+ if (dist < closest_dist)
+ {
+ closest_dist = dist;
+ if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE)
+ handle = GIMP_TRANSFORM_HANDLE_NE_P;
+ else
+ handle = GIMP_TRANSFORM_HANDLE_NE;
+ }
+
+ dist = gimp_canvas_item_transform_distance_square (private->guides,
+ coords->x, coords->y,
+ private->tx3,
+ private->ty3);
+ if (dist < closest_dist)
+ {
+ closest_dist = dist;
+ if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE)
+ handle = GIMP_TRANSFORM_HANDLE_SW_P;
+ else
+ handle = GIMP_TRANSFORM_HANDLE_SW;
+ }
+
+ dist = gimp_canvas_item_transform_distance_square (private->guides,
+ coords->x, coords->y,
+ private->tx4,
+ private->ty4);
+ if (dist < closest_dist)
+ {
+ closest_dist = dist;
+ if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE)
+ handle = GIMP_TRANSFORM_HANDLE_SE_P;
+ else
+ handle = GIMP_TRANSFORM_HANDLE_SE;
+ }
+ }
+ break;
+
+ case GIMP_TRANSFORM_FUNCTION_SHEAR:
+ {
+ gdouble handle_x;
+ gdouble handle_y;
+ gdouble closest_dist;
+ gdouble dist;
+
+ gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_N],
+ &handle_x, &handle_y);
+ dist = gimp_canvas_item_transform_distance_square (private->guides,
+ coords->x, coords->y,
+ handle_x, handle_y);
+ closest_dist = dist;
+ handle = GIMP_TRANSFORM_HANDLE_N_S;
+
+ gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_W],
+ &handle_x, &handle_y);
+ dist = gimp_canvas_item_transform_distance_square (private->guides,
+ coords->x, coords->y,
+ handle_x, handle_y);
+ if (dist < closest_dist)
+ {
+ closest_dist = dist;
+ handle = GIMP_TRANSFORM_HANDLE_W_S;
+ }
+
+ gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_E],
+ &handle_x, &handle_y);
+ dist = gimp_canvas_item_transform_distance_square (private->guides,
+ coords->x, coords->y,
+ handle_x, handle_y);
+ if (dist < closest_dist)
+ {
+ closest_dist = dist;
+ handle = GIMP_TRANSFORM_HANDLE_E_S;
+ }
+
+ gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_S],
+ &handle_x, &handle_y);
+ dist = gimp_canvas_item_transform_distance_square (private->guides,
+ coords->x, coords->y,
+ handle_x, handle_y);
+ if (dist < closest_dist)
+ {
+ closest_dist = dist;
+ handle = GIMP_TRANSFORM_HANDLE_S_S;
+ }
+ }
+ break;
+ }
+
+ return handle;
+}
+
+void
+gimp_tool_transform_grid_hover (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ gboolean proximity)
+{
+ GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget);
+ GimpToolTransformGridPrivate *private = grid->private;
+ GimpTransformHandle handle = GIMP_TRANSFORM_HANDLE_NONE;
+ GimpTransformHandle i;
+
+ for (i = GIMP_TRANSFORM_HANDLE_NONE + 1; i < GIMP_N_TRANSFORM_HANDLES; i++)
+ {
+ if (private->handles[i] &&
+ gimp_canvas_item_hit (private->handles[i], coords->x, coords->y))
+ {
+ handle = i;
+ break;
+ }
+ }
+
+ if (handle == GIMP_TRANSFORM_HANDLE_NONE)
+ {
+ /* points passed in clockwise order */
+ if (point_is_inside_polygon (4,
+ (gdouble[4]){ private->tx1, private->tx2,
+ private->tx4, private->tx3 },
+ (gdouble[4]){ private->ty1, private->ty2,
+ private->ty4, private->ty3 },
+ coords->x, coords->y))
+ {
+ handle = gimp_tool_transform_get_area_handle (grid, coords,
+ private->inside_function);
+ }
+ else
+ {
+ handle = gimp_tool_transform_get_area_handle (grid, coords,
+ private->outside_function);
+ }
+ }
+
+ if (handle != GIMP_TRANSFORM_HANDLE_NONE)
+ gimp_tool_widget_status (widget, get_friendly_operation_name (handle));
+
+ private->handle = handle;
+
+ gimp_tool_transform_grid_update_hilight (grid);
+}
+
+static gboolean
+gimp_tool_transform_grid_get_cursor (GimpToolWidget *widget,
+ const GimpCoords *coords,
+ GdkModifierType state,
+ GimpCursorType *cursor,
+ GimpToolCursorType *tool_cursor,
+ GimpCursorModifier *cursor_modifier)
+{
+ GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget);
+ GimpToolTransformGridPrivate *private = grid->private;
+ gdouble angle[8];
+ gint i;
+ GimpCursorType map[8];
+ GimpVector2 pos[4], this, that;
+ gboolean flip = FALSE;
+ gboolean side = FALSE;
+ gboolean set_cursor = TRUE;
+
+ map[0] = GIMP_CURSOR_CORNER_TOP_LEFT;
+ map[1] = GIMP_CURSOR_CORNER_TOP;
+ map[2] = GIMP_CURSOR_CORNER_TOP_RIGHT;
+ map[3] = GIMP_CURSOR_CORNER_RIGHT;
+ map[4] = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
+ map[5] = GIMP_CURSOR_CORNER_BOTTOM;
+ map[6] = GIMP_CURSOR_CORNER_BOTTOM_LEFT;
+ map[7] = GIMP_CURSOR_CORNER_LEFT;
+
+ get_handle_geometry (grid, pos, angle);
+
+ for (i = 0; i < 8; i++)
+ angle[i] = round (angle[i] * 180.0 / G_PI / 45.0);
+
+ switch (private->handle)
+ {
+ case GIMP_TRANSFORM_HANDLE_NW_P:
+ case GIMP_TRANSFORM_HANDLE_NW:
+ i = (gint) angle[4] + 0;
+ this = pos[0];
+ that = pos[3];
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_NE_P:
+ case GIMP_TRANSFORM_HANDLE_NE:
+ i = (gint) angle[5] + 2;
+ this = pos[1];
+ that = pos[2];
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_SW_P:
+ case GIMP_TRANSFORM_HANDLE_SW:
+ i = (gint) angle[6] + 6;
+ this = pos[2];
+ that = pos[1];
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_SE_P:
+ case GIMP_TRANSFORM_HANDLE_SE:
+ i = (gint) angle[7] + 4;
+ this = pos[3];
+ that = pos[0];
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_N:
+ case GIMP_TRANSFORM_HANDLE_N_S:
+ i = (gint) angle[0] + 1;
+ this = vectoradd (pos[0], pos[1]);
+ that = vectoradd (pos[2], pos[3]);
+ side = TRUE;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_S:
+ case GIMP_TRANSFORM_HANDLE_S_S:
+ i = (gint) angle[1] + 5;
+ this = vectoradd (pos[2], pos[3]);
+ that = vectoradd (pos[0], pos[1]);
+ side = TRUE;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_E:
+ case GIMP_TRANSFORM_HANDLE_E_S:
+ i = (gint) angle[2] + 3;
+ this = vectoradd (pos[1], pos[3]);
+ that = vectoradd (pos[0], pos[2]);
+ side = TRUE;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_W:
+ case GIMP_TRANSFORM_HANDLE_W_S:
+ i = (gint) angle[3] + 7;
+ this = vectoradd (pos[0], pos[2]);
+ that = vectoradd (pos[1], pos[3]);
+ side = TRUE;
+ break;
+
+ default:
+ set_cursor = FALSE;
+ break;
+ }
+
+ if (set_cursor)
+ {
+ i %= 8;
+
+ switch (map[i])
+ {
+ case GIMP_CURSOR_CORNER_TOP_LEFT:
+ if (this.x + this.y > that.x + that.y)
+ flip = TRUE;
+ break;
+ case GIMP_CURSOR_CORNER_TOP:
+ if (this.y > that.y)
+ flip = TRUE;
+ break;
+ case GIMP_CURSOR_CORNER_TOP_RIGHT:
+ if (this.x - this.y < that.x - that.y)
+ flip = TRUE;
+ break;
+ case GIMP_CURSOR_CORNER_RIGHT:
+ if (this.x < that.x)
+ flip = TRUE;
+ break;
+ case GIMP_CURSOR_CORNER_BOTTOM_RIGHT:
+ if (this.x + this.y < that.x + that.y)
+ flip = TRUE;
+ break;
+ case GIMP_CURSOR_CORNER_BOTTOM:
+ if (this.y < that.y)
+ flip = TRUE;
+ break;
+ case GIMP_CURSOR_CORNER_BOTTOM_LEFT:
+ if (this.x - this.y > that.x - that.y)
+ flip = TRUE;
+ break;
+ case GIMP_CURSOR_CORNER_LEFT:
+ if (this.x > that.x)
+ flip = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (flip)
+ *cursor = map[(i + 4) % 8];
+ else
+ *cursor = map[i];
+
+ if (side)
+ *cursor += 8;
+ }
+
+ /* parent class handles *cursor and *modifier for most handles */
+ switch (private->handle)
+ {
+ case GIMP_TRANSFORM_HANDLE_NONE:
+ *tool_cursor = GIMP_TOOL_CURSOR_NONE;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_NW_P:
+ case GIMP_TRANSFORM_HANDLE_NE_P:
+ case GIMP_TRANSFORM_HANDLE_SW_P:
+ case GIMP_TRANSFORM_HANDLE_SE_P:
+ *tool_cursor = GIMP_TOOL_CURSOR_PERSPECTIVE;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_NW:
+ case GIMP_TRANSFORM_HANDLE_NE:
+ case GIMP_TRANSFORM_HANDLE_SW:
+ case GIMP_TRANSFORM_HANDLE_SE:
+ case GIMP_TRANSFORM_HANDLE_N:
+ case GIMP_TRANSFORM_HANDLE_S:
+ case GIMP_TRANSFORM_HANDLE_E:
+ case GIMP_TRANSFORM_HANDLE_W:
+ *tool_cursor = GIMP_TOOL_CURSOR_RESIZE;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_CENTER:
+ *tool_cursor = GIMP_TOOL_CURSOR_MOVE;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_PIVOT:
+ *tool_cursor = GIMP_TOOL_CURSOR_ROTATE;
+ *cursor_modifier = GIMP_CURSOR_MODIFIER_MOVE;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_N_S:
+ case GIMP_TRANSFORM_HANDLE_S_S:
+ case GIMP_TRANSFORM_HANDLE_E_S:
+ case GIMP_TRANSFORM_HANDLE_W_S:
+ *tool_cursor = GIMP_TOOL_CURSOR_SHEAR;
+ break;
+
+ case GIMP_TRANSFORM_HANDLE_ROTATION:
+ *tool_cursor = GIMP_TOOL_CURSOR_ROTATE;
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ return TRUE;
+}
+
+static void
+gimp_tool_transform_grid_update_hilight (GimpToolTransformGrid *grid)
+{
+ GimpToolTransformGridPrivate *private = grid->private;
+ GimpTransformHandle handle;
+
+ for (handle = GIMP_TRANSFORM_HANDLE_NONE;
+ handle < GIMP_N_TRANSFORM_HANDLES;
+ handle++)
+ {
+ if (private->handles[handle])
+ {
+ gimp_canvas_item_set_highlight (private->handles[handle],
+ handle == private->handle);
+ }
+ }
+}
+
+static void
+gimp_tool_transform_grid_update_box (GimpToolTransformGrid *grid)
+{
+ GimpToolTransformGridPrivate *private = grid->private;
+
+ gimp_matrix3_transform_point (&private->transform,
+ private->x1, private->y1,
+ &private->tx1, &private->ty1);
+ gimp_matrix3_transform_point (&private->transform,
+ private->x2, private->y1,
+ &private->tx2, &private->ty2);
+ gimp_matrix3_transform_point (&private->transform,
+ private->x1, private->y2,
+ &private->tx3, &private->ty3);
+ gimp_matrix3_transform_point (&private->transform,
+ private->x2, private->y2,
+ &private->tx4, &private->ty4);
+
+ /* don't transform pivot */
+ private->tpx = private->pivot_x;
+ private->tpy = private->pivot_y;
+
+ private->tcx = (private->tx1 +
+ private->tx2 +
+ private->tx3 +
+ private->tx4) / 4.0;
+ private->tcy = (private->ty1 +
+ private->ty2 +
+ private->ty3 +
+ private->ty4) / 4.0;
+}
+
+static void
+gimp_tool_transform_grid_update_matrix (GimpToolTransformGrid *grid)
+{
+ GimpToolTransformGridPrivate *private = grid->private;
+
+ gimp_matrix3_identity (&private->transform);
+ gimp_transform_matrix_perspective (&private->transform,
+ private->x1,
+ private->y1,
+ private->x2 - private->x1,
+ private->y2 - private->y1,
+ private->tx1,
+ private->ty1,
+ private->tx2,
+ private->ty2,
+ private->tx3,
+ private->ty3,
+ private->tx4,
+ private->ty4);
+
+ private->pivot_x = private->tpx;
+ private->pivot_y = private->tpy;
+
+ g_object_freeze_notify (G_OBJECT (grid));
+ g_object_notify (G_OBJECT (grid), "transform");
+ g_object_notify (G_OBJECT (grid), "pivot-x");
+ g_object_notify (G_OBJECT (grid), "pivot-x");
+ g_object_thaw_notify (G_OBJECT (grid));
+}
+
+static void
+gimp_tool_transform_grid_calc_handles (GimpToolTransformGrid *grid,
+ gint *handle_w,
+ gint *handle_h)
+{
+ GimpToolTransformGridPrivate *private = grid->private;
+ gint dx1, dy1;
+ gint dx2, dy2;
+ gint dx3, dy3;
+ gint dx4, dy4;
+ gint x1, y1;
+ gint x2, y2;
+
+ gimp_canvas_item_transform_xy (private->guides,
+ private->tx1, private->ty1,
+ &dx1, &dy1);
+ gimp_canvas_item_transform_xy (private->guides,
+ private->tx2, private->ty2,
+ &dx2, &dy2);
+ gimp_canvas_item_transform_xy (private->guides,
+ private->tx3, private->ty3,
+ &dx3, &dy3);
+ gimp_canvas_item_transform_xy (private->guides,
+ private->tx4, private->ty4,
+ &dx4, &dy4);
+
+ x1 = MIN4 (dx1, dx2, dx3, dx4);
+ y1 = MIN4 (dy1, dy2, dy3, dy4);
+ x2 = MAX4 (dx1, dx2, dx3, dx4);
+ y2 = MAX4 (dy1, dy2, dy3, dy4);
+
+ *handle_w = CLAMP ((x2 - x1) / 3,
+ MIN_HANDLE_SIZE, GIMP_TOOL_HANDLE_SIZE_LARGE);
+ *handle_h = CLAMP ((y2 - y1) / 3,
+ MIN_HANDLE_SIZE, GIMP_TOOL_HANDLE_SIZE_LARGE);
+}
+
+
+/* public functions */
+
+GimpToolWidget *
+gimp_tool_transform_grid_new (GimpDisplayShell *shell,
+ const GimpMatrix3 *transform,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ GimpGuidesType guide_type,
+ gint n_guides)
+{
+ g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
+
+ return g_object_new (GIMP_TYPE_TOOL_TRANSFORM_GRID,
+ "shell", shell,
+ "transform", transform,
+ "x1", x1,
+ "y1", y1,
+ "x2", x2,
+ "y2", y2,
+ "guide-type", guide_type,
+ "n-guides", n_guides,
+ NULL);
+}
diff --git a/app/display/gimptooltransformgrid.h b/app/display/gimptooltransformgrid.h
new file mode 100644
index 0000000..81d7f39
--- /dev/null
+++ b/app/display/gimptooltransformgrid.h
@@ -0,0 +1,92 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptooltransformgrid.h
+ * Copyright (C) 2017 Michael Natterer <mitch gimp org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TOOL_TRANSFORM_GRID_H__
+#define __GIMP_TOOL_TRANSFORM_GRID_H__
+
+
+#include "gimptoolwidget.h"
+
+
+typedef enum
+{
+ GIMP_TRANSFORM_HANDLE_NONE,
+ GIMP_TRANSFORM_HANDLE_NW_P, /* north west perspective */
+ GIMP_TRANSFORM_HANDLE_NE_P, /* north east perspective */
+ GIMP_TRANSFORM_HANDLE_SW_P, /* south west perspective */
+ GIMP_TRANSFORM_HANDLE_SE_P, /* south east perspective */
+ GIMP_TRANSFORM_HANDLE_NW, /* north west */
+ GIMP_TRANSFORM_HANDLE_NE, /* north east */
+ GIMP_TRANSFORM_HANDLE_SW, /* south west */
+ GIMP_TRANSFORM_HANDLE_SE, /* south east */
+ GIMP_TRANSFORM_HANDLE_N, /* north */
+ GIMP_TRANSFORM_HANDLE_S, /* south */
+ GIMP_TRANSFORM_HANDLE_E, /* east */
+ GIMP_TRANSFORM_HANDLE_W, /* west */
+ GIMP_TRANSFORM_HANDLE_CENTER, /* center for moving */
+ GIMP_TRANSFORM_HANDLE_PIVOT, /* pivot for rotation and scaling */
+ GIMP_TRANSFORM_HANDLE_N_S, /* north shearing */
+ GIMP_TRANSFORM_HANDLE_S_S, /* south shearing */
+ GIMP_TRANSFORM_HANDLE_E_S, /* east shearing */
+ GIMP_TRANSFORM_HANDLE_W_S, /* west shearing */
+ GIMP_TRANSFORM_HANDLE_ROTATION, /* rotation */
+
+ GIMP_N_TRANSFORM_HANDLES /* keep this last so *handles[] is the right size */
+} GimpTransformHandle;
+
+
+#define GIMP_TYPE_TOOL_TRANSFORM_GRID (gimp_tool_transform_grid_get_type ())
+#define GIMP_TOOL_TRANSFORM_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GIMP_TYPE_TOOL_TRANSFORM_GRID, GimpToolTransformGrid))
+#define GIMP_TOOL_TRANSFORM_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GIMP_TYPE_TOOL_TRANSFORM_GRID, GimpToolTransformGridClass))
+#define GIMP_IS_TOOL_TRANSFORM_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GIMP_TYPE_TOOL_TRANSFORM_GRID))
+#define GIMP_IS_TOOL_TRANSFORM_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GIMP_TYPE_TOOL_TRANSFORM_GRID))
+#define GIMP_TOOL_TRANSFORM_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GIMP_TYPE_TOOL_TRANSFORM_GRID, GimpToolTransformGridClass))
+
+
+typedef struct _GimpToolTransformGrid GimpToolTransformGrid;
+typedef struct _GimpToolTransformGridPrivate GimpToolTransformGridPrivate;
+typedef struct _GimpToolTransformGridClass GimpToolTransformGridClass;
+
+struct _GimpToolTransformGrid
+{
+ GimpToolWidget parent_instance;
+
+ GimpToolTransformGridPrivate *private;
+};
+
+struct _GimpToolTransformGridClass
+{
+ GimpToolWidgetClass parent_class;
+};
+
+
+GType gimp_tool_transform_grid_get_type (void) G_GNUC_CONST;
+
+GimpToolWidget * gimp_tool_transform_grid_new (GimpDisplayShell *shell,
+ const GimpMatrix3 *transform,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ GimpGuidesType guide_type,
+ gint n_guides);
+
+
+#endif /* __GIMP_TOOL_TRANSFORM_GRID_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]