[gimp] Bug 721009 - new transform tool with freely placeable handles



commit 2989bad35a8ac3c6f752bd89b009f41adeecb3f6
Author: Johannes Matschke <butesa freenet de>
Date:   Thu Mar 5 12:29:19 2015 +0100

    Bug 721009 - new transform tool with freely placeable handles
    
    Add new tool GimpHandleTransformTool which allows to freely place up
    to 4 handles on the image, then move any one of them, which transforms
    the image so that the remaining handles keep their position.
    
    Did quite some cleanup on the code before pushing --Mitch

 app/dialogs/dialogs.c                   |    1 +
 app/tools/Makefile.am                   |    4 +
 app/tools/gimp-tools.c                  |    2 +
 app/tools/gimphandletransformoptions.c  |  201 +++++++
 app/tools/gimphandletransformoptions.h  |   54 ++
 app/tools/gimphandletransformtool.c     |  985 +++++++++++++++++++++++++++++++
 app/tools/gimphandletransformtool.h     |   60 ++
 app/tools/gimptransformtool.c           |    9 +-
 app/tools/gimptransformtool.h           |    2 +-
 app/tools/tools-enums.c                 |   31 +
 app/tools/tools-enums.h                 |   12 +
 app/widgets/gimphelp-ids.h              |    1 +
 icons/16/gimp-tool-handle-transform.png |  Bin 0 -> 1166 bytes
 icons/22/gimp-tool-handle-transform.png |  Bin 0 -> 2084 bytes
 icons/Makefile.am                       |    2 +
 libgimpwidgets/gimpstock.c              |    1 +
 libgimpwidgets/gimpstock.h              |    1 +
 menus/image-menu.xml.in                 |    1 +
 po/POTFILES.in                          |    2 +
 19 files changed, 1366 insertions(+), 3 deletions(-)
---
diff --git a/app/dialogs/dialogs.c b/app/dialogs/dialogs.c
index 1624a8d..17eae74 100644
--- a/app/dialogs/dialogs.c
+++ b/app/dialogs/dialogs.c
@@ -244,6 +244,7 @@ static const GimpDialogFactoryEntry entries[] =
   FOREIGN ("gimp-threshold-tool-dialog",           TRUE,  FALSE),
   FOREIGN ("gimp-perspective-tool-dialog",         TRUE,  FALSE),
   FOREIGN ("gimp-unified-transform-tool-dialog",   TRUE,  FALSE),
+  FOREIGN ("gimp-handle-transform-tool-dialog",    TRUE,  FALSE),
 
   FOREIGN ("gimp-toolbox-color-dialog",            TRUE,  FALSE),
   FOREIGN ("gimp-gradient-editor-color-dialog",    TRUE,  FALSE),
diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am
index dcdacde..08186ac 100644
--- a/app/tools/Makefile.am
+++ b/app/tools/Makefile.am
@@ -96,6 +96,10 @@ libapptools_a_sources = \
        gimpfuzzyselecttool.h           \
        gimpgegltool.c                  \
        gimpgegltool.h                  \
+       gimphandletransformoptions.c    \
+       gimphandletransformoptions.h    \
+       gimphandletransformtool.c       \
+       gimphandletransformtool.h       \
        gimphealtool.c                  \
        gimphealtool.h                  \
        gimphistogramoptions.c          \
diff --git a/app/tools/gimp-tools.c b/app/tools/gimp-tools.c
index 0ed25ae..54b44fc 100644
--- a/app/tools/gimp-tools.c
+++ b/app/tools/gimp-tools.c
@@ -61,6 +61,7 @@
 #include "gimpforegroundselecttool.h"
 #include "gimpfuzzyselecttool.h"
 #include "gimpgegltool.h"
+#include "gimphandletransformtool.h"
 #include "gimphealtool.h"
 #include "gimphuesaturationtool.h"
 #include "gimpinktool.h"
@@ -160,6 +161,7 @@ gimp_tools_init (Gimp *gimp)
     gimp_cage_tool_register,
     gimp_flip_tool_register,
     gimp_perspective_tool_register,
+    gimp_handle_transform_tool_register,
     gimp_shear_tool_register,
     gimp_scale_tool_register,
     gimp_rotate_tool_register,
diff --git a/app/tools/gimphandletransformoptions.c b/app/tools/gimphandletransformoptions.c
new file mode 100644
index 0000000..78ce7c6
--- /dev/null
+++ b/app/tools/gimphandletransformoptions.c
@@ -0,0 +1,201 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 "libgimpconfig/gimpconfig.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "core/gimp.h"
+#include "core/gimptoolinfo.h"
+
+#include "widgets/gimppropwidgets.h"
+#include "widgets/gimpspinscale.h"
+#include "widgets/gimpwidgets-utils.h"
+
+#include "gimphandletransformoptions.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+  PROP_0,
+  PROP_HANDLE_MODE
+};
+
+
+static void   gimp_handle_transform_options_set_property (GObject         *object,
+                                                          guint            property_id,
+                                                          const GValue    *value,
+                                                          GParamSpec      *pspec);
+static void   gimp_handle_transform_options_get_property (GObject         *object,
+                                                          guint            property_id,
+                                                          GValue          *value,
+                                                          GParamSpec      *pspec);
+
+
+G_DEFINE_TYPE (GimpHandleTransformOptions, gimp_handle_transform_options,
+               GIMP_TYPE_TRANSFORM_OPTIONS)
+
+#define parent_class gimp_handle_transform_options_parent_class
+
+
+static void
+gimp_handle_transform_options_class_init (GimpHandleTransformOptionsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = gimp_handle_transform_options_set_property;
+  object_class->get_property = gimp_handle_transform_options_get_property;
+
+  GIMP_CONFIG_INSTALL_PROP_ENUM (object_class, PROP_HANDLE_MODE,
+                                 "handle-mode",
+                                 N_("Handle mode"),
+                                 GIMP_TYPE_TRANSFORM_HANDLE_MODE,
+                                 GIMP_HANDLE_MODE_TRANSFORM,
+                                 GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_handle_transform_options_init (GimpHandleTransformOptions *options)
+{
+}
+
+static void
+gimp_handle_transform_options_set_property (GObject      *object,
+                                            guint         property_id,
+                                            const GValue *value,
+                                            GParamSpec   *pspec)
+{
+  GimpHandleTransformOptions *options = GIMP_HANDLE_TRANSFORM_OPTIONS (object);
+
+  switch (property_id)
+    {
+    case PROP_HANDLE_MODE:
+      options->handle_mode = g_value_get_enum (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_handle_transform_options_get_property (GObject    *object,
+                                            guint       property_id,
+                                            GValue     *value,
+                                            GParamSpec *pspec)
+{
+  GimpHandleTransformOptions *options = GIMP_HANDLE_TRANSFORM_OPTIONS (object);
+
+  switch (property_id)
+    {
+    case PROP_HANDLE_MODE:
+      g_value_set_enum (value, options->handle_mode);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+/**
+ * gimp_handle_transform_options_gui:
+ * @tool_options: a #GimpToolOptions
+ *
+ * Build the Transform Tool Options.
+ *
+ * Return value: a container holding the transform tool options
+ **/
+GtkWidget *
+gimp_handle_transform_options_gui (GimpToolOptions *tool_options)
+{
+  GObject   *config = G_OBJECT (tool_options);
+  GtkWidget *vbox   = gimp_transform_options_gui (tool_options);
+  GtkWidget *frame;
+  GtkWidget *button;
+  gint       i;
+
+  frame = gimp_prop_enum_radio_frame_new (config, "handle-mode",
+                                          _("Handle mode"), 0, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  /* add modifier to name, add tooltip */
+  button = g_object_get_data (G_OBJECT (frame), "radio-button");
+
+  if (GTK_IS_RADIO_BUTTON (button))
+    {
+      GSList *list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+
+      for (i = g_slist_length (list) - 1 ; list; list = list->next, i--)
+        {
+          GdkModifierType  shift    = gimp_get_extend_selection_mask ();
+          GdkModifierType  ctrl     = gimp_get_constrain_behavior_mask ();
+          GdkModifierType  modifier = 0;
+          gchar           *tooltip  = "";
+          gchar           *tip;
+          gchar           *label;
+
+          switch (i)
+            {
+            case GIMP_HANDLE_MODE_ADD_MOVE:
+              modifier = shift;
+              tooltip  = "Add or move transform handles";
+              break;
+
+            case GIMP_HANDLE_MODE_REMOVE:
+              modifier = ctrl;
+              tooltip  = "Remove transform handles";
+              break;
+
+            case GIMP_HANDLE_MODE_TRANSFORM:
+              modifier = 0;
+              tooltip  = "Transform image by moving handles";
+              break;
+            }
+
+          if (modifier)
+            {
+              label = g_strdup_printf ("%s (%s)",
+                                       gtk_button_get_label (GTK_BUTTON (list->data)),
+                                       gimp_get_mod_string (modifier));
+              gtk_button_set_label (GTK_BUTTON (list->data), label);
+              g_free (label);
+
+              tip = g_strdup_printf ("%s  (%s)",
+                                     tooltip, gimp_get_mod_string (modifier));
+              gimp_help_set_help_data (list->data, tip, NULL);
+              g_free (tip);
+            }
+          else
+            {
+              gimp_help_set_help_data (list->data, tooltip, NULL);
+            }
+        }
+    }
+
+  return vbox;
+}
diff --git a/app/tools/gimphandletransformoptions.h b/app/tools/gimphandletransformoptions.h
new file mode 100644
index 0000000..3407740
--- /dev/null
+++ b/app/tools/gimphandletransformoptions.h
@@ -0,0 +1,54 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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_HANDLE_TRANSFORM_OPTIONS_H__
+#define __GIMP_HANDLE_TRANSFORM_OPTIONS_H__
+
+
+#include "gimptransformoptions.h"
+
+
+#define GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS            (gimp_handle_transform_options_get_type ())
+#define GIMP_HANDLE_TRANSFORM_OPTIONS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS, GimpHandleTransformOptions))
+#define GIMP_HANDLE_TRANSFORM_OPTIONS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS, GimpHandleTransformOptionsClass))
+#define GIMP_IS_HANDLE_TRANSFORM_OPTIONS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS))
+#define GIMP_IS_HANDLE_TRANSFORM_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS))
+#define GIMP_HANDLE_TRANSFORM_OPTIONS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS, GimpHandleTransformOptionsClass))
+
+
+typedef struct _GimpHandleTransformOptions      GimpHandleTransformOptions;
+typedef struct _GimpHandleTransformOptionsClass GimpHandleTransformOptionsClass;
+
+struct _GimpHandleTransformOptions
+{
+  GimpTransformOptions      parent_instance;
+
+  GimpTransformHandleMode   handle_mode;
+};
+
+struct _GimpHandleTransformOptionsClass
+{
+  GimpTransformOptionsClass  parent_class;
+};
+
+
+GType       gimp_handle_transform_options_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_handle_transform_options_gui      (GimpToolOptions *tool_options);
+
+
+#endif /* __GIMP_HANDLE_TRANSFORM_OPTIONS_H__ */
diff --git a/app/tools/gimphandletransformtool.c b/app/tools/gimphandletransformtool.c
new file mode 100644
index 0000000..b214149
--- /dev/null
+++ b/app/tools/gimphandletransformtool.c
@@ -0,0 +1,985 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "widgets/gimphelp-ids.h"
+#include "widgets/gimpwidgets-utils.h"
+
+#include "display/gimpcanvasitem.h"
+#include "display/gimpdisplay.h"
+#include "display/gimptoolgui.h"
+
+#include "gimphandletransformoptions.h"
+#include "gimphandletransformtool.h"
+#include "gimptoolcontrol.h"
+
+#include "gimp-intl.h"
+
+
+/* the transformation is defined by 8 points:
+ *
+ * 4 points on the original image and 4 corresponding points on the
+ * transformed image. The first NUM points on the transformed image
+ * are visible as handles.
+ *
+ * For these handles, the constants TRANSFORM_HANDLE_N,
+ * TRANSFORM_HANDLE_S, TRANSFORM_HANDLE_E and TRANSFORM_HANDLE_W are
+ * used. Actually, it makes no sense to name the handles with north,
+ * south, east, and west.  But this way, we don't need to define even
+ * more enum constants.
+ */
+
+/*  index into trans_info array  */
+enum
+{
+  X0,
+  Y0,
+  X1,
+  Y1,
+  X2,
+  Y2,
+  X3,
+  Y3,
+  OX0,
+  OY0,
+  OX1,
+  OY1,
+  OX2,
+  OY2,
+  OX3,
+  OY3,
+  NUM
+};
+
+
+/*  local function prototypes  */
+
+static void   gimp_handle_transform_tool_button_press  (GimpTool              *tool,
+                                                        const GimpCoords      *coords,
+                                                        guint32                time,
+                                                        GdkModifierType        state,
+                                                        GimpButtonPressType    press_type,
+                                                        GimpDisplay           *display);
+static void   gimp_handle_transform_tool_button_release (GimpTool              *tool,
+                                                         const GimpCoords      *coords,
+                                                         guint32                time,
+                                                         GdkModifierType        state,
+                                                         GimpButtonReleaseType    release_type,
+                                                         GimpDisplay           *display);
+static void   gimp_handle_transform_tool_modifier_key   (GimpTool              *tool,
+                                                         GdkModifierType        key,
+                                                         gboolean               press,
+                                                         GdkModifierType        state,
+                                                         GimpDisplay           *display);
+
+static void   gimp_handle_transform_tool_dialog         (GimpTransformTool  *tr_tool);
+static void   gimp_handle_transform_tool_dialog_update  (GimpTransformTool  *tr_tool);
+static void   gimp_handle_transform_tool_prepare        (GimpTransformTool  *tr_tool);
+static void   gimp_handle_transform_tool_motion         (GimpTransformTool  *tr_tool);
+static void   gimp_handle_transform_tool_recalc_matrix  (GimpTransformTool  *tr_tool);
+static gchar *gimp_handle_transform_tool_get_undo_desc  (GimpTransformTool  *tr_tool);
+static TransformAction
+              gimp_handle_transform_tool_pick_function  (GimpTransformTool  *tr_tool,
+                                                         const GimpCoords   *coords,
+                                                         GdkModifierType     state,
+                                                         GimpDisplay        *display);
+static void   gimp_handle_transform_tool_cursor_update  (GimpTransformTool  *tr_tool,
+                                                         GimpCursorType     *cursor,
+                                                         GimpCursorModifier *modifier);
+static void   gimp_handle_transform_tool_draw_gui       (GimpTransformTool  *tr_tool,
+                                                         gint                handle_w,
+                                                         gint                handle_h);
+
+static gboolean       is_handle_position_valid          (GimpTransformTool  *tr_tool,
+                                                         gint                active_handle);
+static void           handle_micro_move                 (GimpTransformTool *tr_tool,
+                                                         gint               active_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);
+static gboolean       mod_gauss                         (gdouble  matrix[],
+                                                         gdouble  solution[],
+                                                         gint     s);
+
+
+G_DEFINE_TYPE (GimpHandleTransformTool, gimp_handle_transform_tool,
+               GIMP_TYPE_TRANSFORM_TOOL)
+
+#define parent_class gimp_handle_transform_tool_parent_class
+
+
+void
+gimp_handle_transform_tool_register (GimpToolRegisterCallback  callback,
+                                     gpointer                  data)
+{
+  (* callback) (GIMP_TYPE_HANDLE_TRANSFORM_TOOL,
+                GIMP_TYPE_HANDLE_TRANSFORM_OPTIONS,
+                gimp_handle_transform_options_gui,
+                GIMP_CONTEXT_BACKGROUND_MASK,
+                "gimp-handle-transform-tool",
+                _("Handle Transform"),
+                _("Handle Transform Tool: "
+                  "Deform the layer, selection or path with handles"),
+                N_("_Handle Transform"), "<ctrl><shift>H",
+                NULL, GIMP_HELP_TOOL_HANDLE_TRANSFORM,
+                GIMP_STOCK_TOOL_HANDLE_TRANSFORM,
+                data);
+}
+
+static void
+gimp_handle_transform_tool_class_init (GimpHandleTransformToolClass *klass)
+{
+  GimpToolClass          *tool_class  = GIMP_TOOL_CLASS (klass);
+  GimpTransformToolClass *trans_class = GIMP_TRANSFORM_TOOL_CLASS (klass);
+
+  tool_class->button_press   = gimp_handle_transform_tool_button_press;
+  tool_class->button_release = gimp_handle_transform_tool_button_release;
+  tool_class->modifier_key   = gimp_handle_transform_tool_modifier_key;
+
+  trans_class->dialog        = gimp_handle_transform_tool_dialog;
+  trans_class->dialog_update = gimp_handle_transform_tool_dialog_update;
+  trans_class->prepare       = gimp_handle_transform_tool_prepare;
+  trans_class->motion        = gimp_handle_transform_tool_motion;
+  trans_class->recalc_matrix = gimp_handle_transform_tool_recalc_matrix;
+  trans_class->get_undo_desc = gimp_handle_transform_tool_get_undo_desc;
+  trans_class->pick_function = gimp_handle_transform_tool_pick_function;
+  trans_class->cursor_update = gimp_handle_transform_tool_cursor_update;
+  trans_class->draw_gui      = gimp_handle_transform_tool_draw_gui;
+}
+
+static void
+gimp_handle_transform_tool_init (GimpHandleTransformTool *ht_tool)
+{
+  GimpTransformTool *tr_tool = GIMP_TRANSFORM_TOOL (ht_tool);
+
+  tr_tool->progress_text = _("Handle transformation");
+  tr_tool->use_grid      = TRUE;
+
+  ht_tool->saved_handle_mode = GIMP_HANDLE_MODE_TRANSFORM;
+}
+
+static void
+gimp_handle_transform_tool_button_press (GimpTool            *tool,
+                                         const GimpCoords    *coords,
+                                         guint32              time,
+                                         GdkModifierType      state,
+                                         GimpButtonPressType  press_type,
+                                         GimpDisplay         *display)
+{
+  GimpHandleTransformTool    *ht      = GIMP_HANDLE_TRANSFORM_TOOL (tool);
+  GimpTransformTool          *tr_tool = GIMP_TRANSFORM_TOOL (tool);
+  GimpHandleTransformOptions *options;
+  gint                        num;
+  gint                        active_handle;
+
+  options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
+
+  num           = (gint) tr_tool->trans_info[NUM];
+  active_handle = tr_tool->function - TRANSFORM_HANDLE_N;
+
+  /* There is nothing to be done on creation */
+  if (tr_tool->function == TRANSFORM_CREATING)
+    {
+      GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time,
+                                                    state, press_type, display);
+      return;
+    }
+
+  if (options->handle_mode == GIMP_HANDLE_MODE_ADD_MOVE)
+    {
+      /* add handle */
+
+      if (num < 4 && tr_tool->function == TRANSFORM_HANDLE_NONE)
+        {
+          tr_tool->trans_info[X0 + 2 * num] = coords->x;
+          tr_tool->trans_info[Y0 + 2 * num] = coords->y;
+          tr_tool->function = TRANSFORM_HANDLE_N + num;
+          tr_tool->trans_info[NUM]++;
+
+          /* check for valid position and calculating of OX0...OY3 is
+           * done on button release
+           */
+        }
+
+      /* move handles without changing the transformation matrix */
+      ht->matrix_recalculation = FALSE;
+    }
+  else if (options->handle_mode == GIMP_HANDLE_MODE_REMOVE &&
+           num > 0            &&
+           active_handle >= 0 &&
+           active_handle < 4)
+    {
+      /* remove handle */
+
+      gdouble tempx  = tr_tool->trans_info[X0  + 2 * active_handle];
+      gdouble tempy  = tr_tool->trans_info[Y0  + 2 * active_handle];
+      gdouble tempox = tr_tool->trans_info[OX0 + 2 * active_handle];
+      gdouble tempoy = tr_tool->trans_info[OY0 + 2 * active_handle];
+      gint    i;
+
+      num--;
+      tr_tool->trans_info[NUM]--;
+
+      for (i = active_handle; i < num; i++)
+        {
+          tr_tool->trans_info[X0  + 2 * i] = tr_tool->trans_info[X1  + 2 * i];
+          tr_tool->trans_info[Y0  + 2 * i] = tr_tool->trans_info[Y1  + 2 * i];
+          tr_tool->trans_info[OX0 + 2 * i] = tr_tool->trans_info[OX1 + 2 * i];
+          tr_tool->trans_info[OY0 + 2 * i] = tr_tool->trans_info[OY1 + 2 * i];
+        }
+
+      tr_tool->trans_info[X0  + 2 * num] = tempx;
+      tr_tool->trans_info[Y0  + 2 * num] = tempy;
+      tr_tool->trans_info[OX0 + 2 * num] = tempox;
+      tr_tool->trans_info[OY0 + 2 * num] = tempoy;
+    }
+
+  GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time,
+                                                state, press_type, display);
+}
+
+static void
+gimp_handle_transform_tool_button_release (GimpTool              *tool,
+                                           const GimpCoords      *coords,
+                                           guint32                time,
+                                           GdkModifierType        state,
+                                           GimpButtonReleaseType  release_type,
+                                           GimpDisplay           *display)
+{
+  GimpHandleTransformTool    *ht      = GIMP_HANDLE_TRANSFORM_TOOL (tool);
+  GimpTransformTool          *tr_tool = GIMP_TRANSFORM_TOOL (tool);
+  GimpHandleTransformOptions *options;
+  gint                        active_handle;
+
+  options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
+
+  active_handle = tr_tool->function - TRANSFORM_HANDLE_N;
+
+  if (options->handle_mode == GIMP_HANDLE_MODE_ADD_MOVE &&
+      active_handle >= 0 &&
+      active_handle < 4)
+    {
+      GimpMatrix3 matrix;
+
+      if (! is_handle_position_valid (tr_tool, active_handle))
+        {
+          handle_micro_move (tr_tool, active_handle);
+        }
+
+      /* handle was added or moved. calculate new original position */
+      matrix = tr_tool->transform;
+      gimp_matrix3_invert (&matrix);
+      gimp_matrix3_transform_point (&matrix,
+                                    tr_tool->trans_info[X0 + 2 * active_handle],
+                                    tr_tool->trans_info[Y0 + 2 * active_handle],
+                                    &tr_tool->trans_info[OX0 + 2 * active_handle],
+                                    &tr_tool->trans_info[OY0 + 2 * active_handle]);
+    }
+
+  if (release_type != GIMP_BUTTON_RELEASE_CANCEL)
+    {
+      /* force redraw */
+      gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
+      gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
+    }
+
+  ht->matrix_recalculation = TRUE;
+
+  GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time,
+                                                  state, release_type, display);
+}
+
+static void
+gimp_handle_transform_tool_modifier_key (GimpTool        *tool,
+                                         GdkModifierType  key,
+                                         gboolean         press,
+                                         GdkModifierType  state,
+                                         GimpDisplay     *display)
+{
+  GimpHandleTransformTool    *ht_tool = GIMP_HANDLE_TRANSFORM_TOOL (tool);
+  GimpHandleTransformOptions *options;
+  GdkModifierType             shift   = gimp_get_extend_selection_mask ();
+  GdkModifierType             ctrl    = gimp_get_constrain_behavior_mask ();
+  GimpTransformHandleMode     handle_mode;
+
+  options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tool);
+
+  handle_mode = options->handle_mode;
+
+  if (press)
+    {
+      if (key == (state & (shift | ctrl)))
+        {
+          /*  first modifier pressed  */
+          ht_tool->saved_handle_mode = options->handle_mode;
+        }
+    }
+  else
+    {
+      if (! (state & (shift | ctrl)))
+        {
+          /*  last modifier released  */
+          handle_mode = ht_tool->saved_handle_mode;
+        }
+    }
+
+  if (state & shift)
+    {
+      handle_mode = GIMP_HANDLE_MODE_ADD_MOVE;
+    }
+  else if (state & ctrl)
+    {
+      handle_mode = GIMP_HANDLE_MODE_REMOVE;
+    }
+
+  if (handle_mode != options->handle_mode)
+    {
+      g_object_set (options, "handle-mode", handle_mode, NULL);
+    }
+
+  GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press,
+                                                state, display);
+}
+
+static void
+gimp_handle_transform_tool_dialog (GimpTransformTool *tr_tool)
+{
+  GimpHandleTransformTool *handle_transform = GIMP_HANDLE_TRANSFORM_TOOL (tr_tool);
+  GtkWidget               *frame;
+  GtkWidget               *table;
+  gint                     x, y;
+
+  frame = gimp_frame_new (_("Transformation Matrix"));
+  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tr_tool->gui)), frame,
+                      FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  table = gtk_table_new (3, 3, FALSE);
+  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
+  gtk_table_set_col_spacings (GTK_TABLE (table), 2);
+  gtk_container_add (GTK_CONTAINER (frame), table);
+  gtk_widget_show (table);
+
+  for (y = 0; y < 3; y++)
+    for (x = 0; x < 3; x++)
+      {
+        GtkWidget *label = gtk_label_new (" ");
+
+        gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.0);
+        gtk_label_set_width_chars (GTK_LABEL (label), 12);
+        gtk_table_attach (GTK_TABLE (table), label,
+                          x, x + 1, y, y + 1, GTK_EXPAND, GTK_FILL, 0, 0);
+        gtk_widget_show (label);
+
+        handle_transform->label[y][x] = label;
+      }
+}
+
+static void
+gimp_handle_transform_tool_dialog_update (GimpTransformTool *tr_tool)
+{
+  GimpHandleTransformTool *ht_tool = GIMP_HANDLE_TRANSFORM_TOOL (tr_tool);
+  gint                     x, y;
+
+  for (y = 0; y < 3; y++)
+    {
+      for (x = 0; x < 3; x++)
+        {
+          gchar buf[32];
+
+          g_snprintf (buf, sizeof (buf),
+                      "%10.5f", tr_tool->transform.coeff[y][x]);
+
+          gtk_label_set_text (GTK_LABEL (ht_tool->label[y][x]), buf);
+        }
+    }
+}
+
+static void
+gimp_handle_transform_tool_prepare (GimpTransformTool  *tr_tool)
+{
+  GimpHandleTransformTool *ht_tool = GIMP_HANDLE_TRANSFORM_TOOL (tr_tool);
+
+  tr_tool->trans_info[X0]  = (gdouble) tr_tool->x1;
+  tr_tool->trans_info[Y0]  = (gdouble) tr_tool->y1;
+  tr_tool->trans_info[X1]  = (gdouble) tr_tool->x2;
+  tr_tool->trans_info[Y1]  = (gdouble) tr_tool->y1;
+  tr_tool->trans_info[X2]  = (gdouble) tr_tool->x1;
+  tr_tool->trans_info[Y2]  = (gdouble) tr_tool->y2;
+  tr_tool->trans_info[X3]  = (gdouble) tr_tool->x2;
+  tr_tool->trans_info[Y3]  = (gdouble) tr_tool->y2;
+  tr_tool->trans_info[OX0] = (gdouble) tr_tool->x1;
+  tr_tool->trans_info[OY0] = (gdouble) tr_tool->y1;
+  tr_tool->trans_info[OX1] = (gdouble) tr_tool->x2;
+  tr_tool->trans_info[OY1] = (gdouble) tr_tool->y1;
+  tr_tool->trans_info[OX2] = (gdouble) tr_tool->x1;
+  tr_tool->trans_info[OY2] = (gdouble) tr_tool->y2;
+  tr_tool->trans_info[OX3] = (gdouble) tr_tool->x2;
+  tr_tool->trans_info[OY3] = (gdouble) tr_tool->y2;
+  tr_tool->trans_info[NUM] = 0;
+
+  ht_tool->matrix_recalculation = TRUE;
+}
+
+static void
+gimp_handle_transform_tool_motion (GimpTransformTool *tr_tool)
+{
+  GimpHandleTransformOptions *options;
+  gint                        active_handle;
+  gint                        num;
+
+  options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
+
+  active_handle = tr_tool->function - TRANSFORM_HANDLE_N;
+  num           = (gint) tr_tool->trans_info[NUM];
+
+  if (active_handle >= 0 && active_handle < 4)
+    {
+      if (options->handle_mode == GIMP_HANDLE_MODE_ADD_MOVE)
+        {
+          tr_tool->trans_info[X0 + 2*active_handle] += tr_tool->curx - tr_tool->lastx;
+          tr_tool->trans_info[Y0 + 2*active_handle] += tr_tool->cury - tr_tool->lasty;
+          /* 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
+           */
+        }
+      else if (options->handle_mode == GIMP_HANDLE_MODE_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 < num && i != active_handle)
+                {
+                  fixed_handles_x[j] = tr_tool->prev_trans_info[0][X0+i*2];
+                  fixed_handles_y[j] = tr_tool->prev_trans_info[0][Y0+i*2];
+                  j++;
+                }
+
+              newpos_x[i] = oldpos_x[i] = tr_tool->prev_trans_info[0][X0+i*2];
+              newpos_y[i] = oldpos_y[i] = tr_tool->prev_trans_info[0][Y0+i*2];
+            }
+
+          newpos_x[active_handle] = oldpos_x[active_handle] + tr_tool->curx - tr_tool->mousex;
+          newpos_y[active_handle] = oldpos_y[active_handle] + tr_tool->cury - tr_tool->mousey;
+
+          switch (num)
+            {
+            case 1:
+              /* move */
+              for (i = 1; i < 4; i++)
+                {
+                  newpos_x[i] = oldpos_x[i] + tr_tool->curx - tr_tool->mousex;
+                  newpos_y[i] = oldpos_y[i] + tr_tool->cury - tr_tool->mousey;
+                }
+              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 * (tr_tool->curx - tr_tool->mousex);
+              newpos_y[3] = oldpos_y[3] + scale * (tr_tool->cury - tr_tool->mousey);
+              break;
+            }
+
+          for (i = 0; i < 4; i++)
+            {
+              tr_tool->trans_info[X0 + 2*i] = newpos_x[i];
+              tr_tool->trans_info[Y0 + 2*i] = newpos_y[i];
+            }
+        }
+    }
+}
+
+static void
+gimp_handle_transform_tool_recalc_matrix (GimpTransformTool *tr_tool)
+{
+  gdouble coeff[8*9];
+  gdouble sol[8];
+  int i;
+  gdouble opos_x[4], opos_y[4];
+  gdouble pos_x[4], pos_y[4];
+  GimpHandleTransformTool *handle_transform = GIMP_HANDLE_TRANSFORM_TOOL (tr_tool);
+
+  if (handle_transform->matrix_recalculation)
+    {
+      for (i = 0; i < 4; i++)
+        {
+          pos_x[i] = tr_tool->trans_info[X0+i*2];
+          pos_y[i] = tr_tool->trans_info[Y0+i*2];
+          opos_x[i] = tr_tool->trans_info[OX0+i*2];
+          opos_y[i] = tr_tool->trans_info[OY0+i*2];
+        }
+
+      for (i = 0; i < 4; i++)
+        {
+          coeff[i*9+0] = opos_x[i];
+          coeff[i*9+1] = opos_y[i];
+          coeff[i*9+2] = 1;
+          coeff[i*9+3] = 0;
+          coeff[i*9+4] = 0;
+          coeff[i*9+5] = 0;
+          coeff[i*9+6] = -opos_x[i]*pos_x[i];
+          coeff[i*9+7] = -opos_y[i]*pos_x[i];
+          coeff[i*9+8] = pos_x[i];
+
+          coeff[(i+4)*9+0] = 0;
+          coeff[(i+4)*9+1] = 0;
+          coeff[(i+4)*9+2] = 0;
+          coeff[(i+4)*9+3] = opos_x[i];
+          coeff[(i+4)*9+4] = opos_y[i];
+          coeff[(i+4)*9+5] = 1;
+          coeff[(i+4)*9+6] = -opos_x[i]*pos_y[i];
+          coeff[(i+4)*9+7] = -opos_y[i]*pos_y[i];
+          coeff[(i+4)*9+8] = pos_y[i];
+        }
+
+      if (mod_gauss(coeff, sol, 8))
+        {
+          tr_tool->transform.coeff[0][0] = sol[0];
+          tr_tool->transform.coeff[0][1] = sol[1];
+          tr_tool->transform.coeff[0][2] = sol[2];
+          tr_tool->transform.coeff[1][0] = sol[3];
+          tr_tool->transform.coeff[1][1] = sol[4];
+          tr_tool->transform.coeff[1][2] = sol[5];
+          tr_tool->transform.coeff[2][0] = sol[6];
+          tr_tool->transform.coeff[2][1] = sol[7];
+          tr_tool->transform.coeff[2][2] = 1;
+        }
+      else
+        {
+          /* this should not happen
+           * reset the matrix so the user sees that something went wrong */
+          gimp_matrix3_identity (&tr_tool->transform);
+        }
+    }
+}
+
+static gchar *
+gimp_handle_transform_tool_get_undo_desc (GimpTransformTool *tr_tool)
+{
+  return g_strdup (C_("undo-type", "Handle transform"));
+}
+
+static TransformAction
+gimp_handle_transform_tool_pick_function (GimpTransformTool *tr_tool,
+                                          const GimpCoords  *coords,
+                                          GdkModifierType    state,
+                                          GimpDisplay       *display)
+{
+  TransformAction i;
+
+  for (i = TRANSFORM_HANDLE_N; i < TRANSFORM_HANDLE_N + 4; i++)
+    {
+      if (tr_tool->handles[i] &&
+          gimp_canvas_item_hit (tr_tool->handles[i], coords->x, coords->y))
+        {
+          return i;
+        }
+    }
+
+  return TRANSFORM_HANDLE_NONE;
+}
+
+static void
+gimp_handle_transform_tool_cursor_update (GimpTransformTool  *tr_tool,
+                                          GimpCursorType     *cursor,
+                                          GimpCursorModifier *modifier)
+{
+  GimpHandleTransformOptions *options;
+  GimpToolCursorType          tool_cursor = GIMP_TOOL_CURSOR_NONE;
+
+  options = GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS (tr_tool);
+
+  *cursor     = GIMP_CURSOR_CROSSHAIR_SMALL;
+  *modifier   = GIMP_CURSOR_MODIFIER_NONE;
+
+  /* do not show modifiers when the tool isn't active */
+  if (! gimp_draw_tool_is_active (GIMP_DRAW_TOOL (tr_tool)))
+    return;
+
+  if (options->handle_mode == GIMP_HANDLE_MODE_TRANSFORM &&
+      tr_tool->function > TRANSFORM_HANDLE_NONE)
+    {
+      switch ((gint) tr_tool->trans_info[NUM])
+        {
+        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 (options->handle_mode == GIMP_HANDLE_MODE_ADD_MOVE)
+    {
+      if (tr_tool->function > TRANSFORM_HANDLE_NONE)
+        *modifier = GIMP_CURSOR_MODIFIER_MOVE;
+      else
+        *modifier = GIMP_CURSOR_MODIFIER_PLUS;
+    }
+  else if (options->handle_mode == GIMP_HANDLE_MODE_REMOVE)
+    {
+      *modifier = GIMP_CURSOR_MODIFIER_MINUS;
+    }
+
+  gimp_tool_control_set_tool_cursor (GIMP_TOOL (tr_tool)->control,
+                                     tool_cursor);
+}
+
+static void
+gimp_handle_transform_tool_draw_gui (GimpTransformTool *tr_tool,
+                                     gint               handle_w,
+                                     gint               handle_h)
+{
+  GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tr_tool);
+  gint          i;
+
+#if 0
+  /* show additional points for debugging */
+  for (i = tr_tool->trans_info[NUM]; i < 4; i++)
+    {
+      gimp_draw_tool_add_handle (draw_tool,
+                                 GIMP_HANDLE_FILLED_CIRCLE,
+                                 tr_tool->trans_info[X0+2*i],
+                                 tr_tool->trans_info[Y0+2*i],
+                                 GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+                                 GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+                                 GIMP_HANDLE_ANCHOR_CENTER);
+      gimp_draw_tool_add_handle (draw_tool,
+                                 GIMP_HANDLE_FILLED_DIAMOND,
+                                 tr_tool->trans_info[OX0+2*i],
+                                 tr_tool->trans_info[OY0+2*i],
+                                 GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+                                 GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+                                 GIMP_HANDLE_ANCHOR_CENTER);
+      }
+
+  for (i = 0; i < tr_tool->trans_info[NUM]; i++)
+    {
+      tr_tool->handles[TRANSFORM_HANDLE_N + i] =
+        gimp_draw_tool_add_handle (draw_tool,
+                                   GIMP_HANDLE_DIAMOND,
+                                   tr_tool->trans_info[OX0+2*i],
+                                   tr_tool->trans_info[OY0+2*i],
+                                   GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+                                   GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+                                   GIMP_HANDLE_ANCHOR_CENTER);
+    }
+#endif
+
+  for (i = 0; i < tr_tool->trans_info[NUM]; i++)
+    {
+      tr_tool->handles[TRANSFORM_HANDLE_N + i] =
+        gimp_draw_tool_add_handle (draw_tool,
+                                   GIMP_HANDLE_CIRCLE,
+                                   tr_tool->trans_info[X0 + 2 * i],
+                                   tr_tool->trans_info[Y0 + 2 * i],
+                                   GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+                                   GIMP_TOOL_HANDLE_SIZE_CIRCLE,
+                                   GIMP_HANDLE_ANCHOR_CENTER);
+    }
+}
+
+/* check if a handle is not on the connection line of two other handles */
+static gboolean
+is_handle_position_valid (GimpTransformTool *tr_tool,
+                          gint               active_handle)
+{
+  gint i, j, k;
+
+  if (tr_tool->trans_info[NUM] < 3)
+    {
+      /* there aren't two other handles */
+      return TRUE;
+    }
+
+  if (tr_tool->trans_info[NUM] == 3)
+    {
+      return ((tr_tool->trans_info[X0] - tr_tool->trans_info[X1]) *
+              (tr_tool->trans_info[Y1] - tr_tool->trans_info[Y2]) !=
+
+              (tr_tool->trans_info[X1] - tr_tool->trans_info[X2]) *
+              (tr_tool->trans_info[Y0] - tr_tool->trans_info[Y1]));
+    }
+
+  /* tr_tool->trans_info[NUM] == 4 */
+  for (i = 0; i < 2; i++)
+    {
+      for (j = i + 1; j < 3; j++)
+        {
+          for (k = j + 1; i < 4; i++)
+            {
+              if (active_handle == i ||
+                  active_handle == j ||
+                  active_handle == k)
+                {
+                  if ((tr_tool->trans_info[X0 + 2 * i] -
+                       tr_tool->trans_info[X0 + 2 * j]) *
+                      (tr_tool->trans_info[Y0 + 2 * j] -
+                       tr_tool->trans_info[Y0 + 2 * k]) ==
+
+                      (tr_tool->trans_info[X0 + 2 * j] -
+                       tr_tool->trans_info[X0 + 2 * k]) *
+                      (tr_tool->trans_info[Y0 + 2 * i] -
+                       tr_tool->trans_info[Y0 + 2 * 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 (GimpTransformTool *tr_tool,
+                   gint               active_handle)
+{
+  gdouble posx = tr_tool->trans_info[X0 + 2 * active_handle];
+  gdouble posy = tr_tool->trans_info[Y0 + 2 * active_handle];
+  gdouble dx, dy;
+
+  for (dx = -0.1; dx < 0.11; dx += 0.1)
+    {
+      tr_tool->trans_info[X0 + 2 * active_handle] = posx + dx;
+
+      for (dy = -0.1; dy < 0.11; dy += 0.1)
+        {
+          tr_tool->trans_info[Y0 + 2 * active_handle] = posy + dy;
+
+          if (is_handle_position_valid (tr_tool, active_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);
+}
+
+
+/* modified gaussian algorithm
+ * solves a system of linear equations
+ *
+ * Example:
+ * 1x + 2y + 4z = 25
+ * 2x + 1y      = 4
+ * 3x + 5y + 2z = 23
+ * Solution: x=1, y=2, z=5
+ *
+ * Input:
+ * matrix = { 1,2,4,25,2,1,0,4,3,5,2,23 }
+ * s = 3 (Number of variables)
+ * Output:
+ * return value == TRUE (TRUE, if there is a single unique solution)
+ * solution == { 1,2,5 } (if the return value is FALSE, the content
+ * of solution is of no use)
+ */
+static gboolean
+mod_gauss (gdouble matrix[],
+           gdouble solution[],
+           gint    s)
+{
+  gint    p[s]; /* row permutation */
+  gint    i, j, r, temp;
+  gdouble q;
+  gint    t = s + 1;
+
+  for (i = 0; i < s; i++)
+    {
+      p[i] = i;
+    }
+
+  for (r = 0; r < s; r++)
+    {
+      /* make sure that (r,r) is not 0 */
+      if (matrix[p[r] * t + r] == 0.0)
+        {
+          /* we need to permutate rows */
+          for (i = r + 1; i <= s; i++)
+            {
+              if (i == s)
+                {
+                  /* if this happens, the linear system has zero or
+                   * more than one solutions.
+                   */
+                  return FALSE;
+                }
+
+              if (matrix[p[i] * t + r] != 0.0)
+                break;
+            }
+
+          temp = p[r];
+          p[r] = p[i];
+          p[i] = temp;
+        }
+
+      /* make (r,r) == 1 */
+      q = 1.0 / matrix[p[r] * t + r];
+      matrix[p[r] * t + r] = 1.0;
+
+      for (j = r + 1; j < t; j++)
+        {
+          matrix[p[r] * t + j] *= q;
+        }
+
+      /* make that all entries in column r are 0 (except (r,r)) */
+      for (i = 0; i < s; i++)
+        {
+          if (i == r)
+            continue;
+
+          for (j = r + 1; j < t ; j++)
+            {
+              matrix[p[i] * t + j] -= matrix[p[r] * t + j] * matrix[p[i] * t + r];
+            }
+
+          /* we don't need to execute the following line
+           * since we won't access this element again:
+           *
+           * matrix[p[i] * t + r] = 0.0;
+           */
+        }
+    }
+
+  for (i = 0; i < s; i++)
+    {
+      solution[i] = matrix[p[i] * t + s];
+    }
+
+  return TRUE;
+}
diff --git a/app/tools/gimphandletransformtool.h b/app/tools/gimphandletransformtool.h
new file mode 100644
index 0000000..df6157e
--- /dev/null
+++ b/app/tools/gimphandletransformtool.h
@@ -0,0 +1,60 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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_HANDLE_TRANSFORM_TOOL_H__
+#define __GIMP_HANDLE_TRANSFORM_TOOL_H__
+
+
+#include "gimptransformtool.h"
+
+
+#define GIMP_TYPE_HANDLE_TRANSFORM_TOOL            (gimp_handle_transform_tool_get_type ())
+#define GIMP_HANDLE_TRANSFORM_TOOL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_HANDLE_TRANSFORM_TOOL, GimpHandleTransformTool))
+#define GIMP_HANDLE_TRANSFORM_TOOL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_HANDLE_TRANSFORM_TOOL, GimpHandleTransformToolClass))
+#define GIMP_IS_HANDLE_TRANSFORM_TOOL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_HANDLE_TRANSFORM_TOOL))
+#define GIMP_IS_HANDLE_TRANSFORM_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_HANDLE_TRANSFORM_TOOL))
+#define GIMP_HANDLE_TRANSFORM_TOOL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_HANDLE_TRANSFORM_TOOL, GimpHandleTransformToolClass))
+
+#define GIMP_HANDLE_TRANSFORM_TOOL_GET_OPTIONS(t)  (GIMP_HANDLE_TRANSFORM_OPTIONS (gimp_tool_get_options 
(GIMP_TOOL (t))))
+
+
+typedef struct _GimpHandleTransformTool      GimpHandleTransformTool;
+typedef struct _GimpHandleTransformToolClass GimpHandleTransformToolClass;
+
+struct _GimpHandleTransformTool
+{
+  GimpTransformTool        parent_instance;
+
+  GtkWidget               *label[3][3];
+  gboolean                 matrix_recalculation;
+
+  GimpTransformHandleMode  saved_handle_mode;
+};
+
+struct _GimpHandleTransformToolClass
+{
+  GimpTransformToolClass  parent_class;
+};
+
+
+void    gimp_handle_transform_tool_register (GimpToolRegisterCallback  callback,
+                                             gpointer                  data);
+
+GType   gimp_handle_transform_tool_get_type (void) G_GNUC_CONST;
+
+
+#endif  /*  __GIMP_HANDLE_TRANSFORM_TOOL_H__  */
diff --git a/app/tools/gimptransformtool.c b/app/tools/gimptransformtool.c
index 885cc4d..34fcc1e 100644
--- a/app/tools/gimptransformtool.c
+++ b/app/tools/gimptransformtool.c
@@ -59,6 +59,7 @@
 #include "display/gimptoolgui.h"
 
 #include "gimptoolcontrol.h"
+#include "gimphandletransformtool.h"
 #include "gimpperspectivetool.h"
 #include "gimpunifiedtransformtool.h"
 #include "gimptransformoptions.h"
@@ -1088,10 +1089,15 @@ gimp_transform_tool_draw (GimpDrawTool *draw_tool)
       if (gimp_transform_options_show_preview (options))
         {
           GimpMatrix3 matrix = tr_tool->transform;
+          gboolean    perspective;
 
           if (options->direction == GIMP_TRANSFORM_BACKWARD)
             gimp_matrix3_invert (&matrix);
 
+          perspective = (GIMP_IS_PERSPECTIVE_TOOL (tr_tool)      ||
+                         GIMP_IS_HANDLE_TRANSFORM_TOOL (tr_tool) ||
+                         GIMP_IS_UNIFIED_TRANSFORM_TOOL (tr_tool));
+
           gimp_draw_tool_add_transform_preview (draw_tool,
                                                 tool->drawable,
                                                 &matrix,
@@ -1099,8 +1105,7 @@ gimp_transform_tool_draw (GimpDrawTool *draw_tool)
                                                 tr_tool->y1,
                                                 tr_tool->x2,
                                                 tr_tool->y2,
-                                                GIMP_IS_PERSPECTIVE_TOOL (tr_tool) ||
-                                                GIMP_IS_UNIFIED_TRANSFORM_TOOL (tr_tool),
+                                                perspective,
                                                 options->preview_opacity);
         }
 
diff --git a/app/tools/gimptransformtool.h b/app/tools/gimptransformtool.h
index 2ee4634..4ef6591 100644
--- a/app/tools/gimptransformtool.h
+++ b/app/tools/gimptransformtool.h
@@ -54,7 +54,7 @@ typedef enum
  * of the enums at the top of each transformation tool, stored in
  * trans_info and related
  */
-#define TRANS_INFO_SIZE 10
+#define TRANS_INFO_SIZE 17
 
 typedef gdouble TransInfo[TRANS_INFO_SIZE];
 
diff --git a/app/tools/tools-enums.c b/app/tools/tools-enums.c
index 61155c7..77b5fb8 100644
--- a/app/tools/tools-enums.c
+++ b/app/tools/tools-enums.c
@@ -74,6 +74,37 @@ gimp_button_release_type_get_type (void)
 }
 
 GType
+gimp_transform_handle_mode_get_type (void)
+{
+  static const GEnumValue values[] =
+  {
+    { GIMP_HANDLE_MODE_ADD_MOVE, "GIMP_HANDLE_MODE_ADD_MOVE", "add-move" },
+    { GIMP_HANDLE_MODE_REMOVE, "GIMP_HANDLE_MODE_REMOVE", "remove" },
+    { GIMP_HANDLE_MODE_TRANSFORM, "GIMP_HANDLE_MODE_TRANSFORM", "transform" },
+    { 0, NULL, NULL }
+  };
+
+  static const GimpEnumDesc descs[] =
+  {
+    { GIMP_HANDLE_MODE_ADD_MOVE, NC_("transform-handle-mode", "Add/Move"), NULL },
+    { GIMP_HANDLE_MODE_REMOVE, NC_("transform-handle-mode", "Remove"), NULL },
+    { GIMP_HANDLE_MODE_TRANSFORM, NC_("transform-handle-mode", "Transform"), 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)
 {
   static const GEnumValue values[] =
diff --git a/app/tools/tools-enums.h b/app/tools/tools-enums.h
index c2d2cc0..60246ef 100644
--- a/app/tools/tools-enums.h
+++ b/app/tools/tools-enums.h
@@ -47,6 +47,18 @@ typedef enum
 } GimpButtonReleaseType;
 
 
+#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_MOVE,   /*< desc="Add/Move"  >*/
+  GIMP_HANDLE_MODE_REMOVE,     /*< desc="Remove"    >*/
+  GIMP_HANDLE_MODE_TRANSFORM,  /*< desc="Transform" >*/
+} GimpTransformHandleMode;
+
+
 #define GIMP_TYPE_RECTANGLE_CONSTRAINT (gimp_rectangle_constraint_get_type ())
 
 GType gimp_rectangle_constraint_get_type (void) G_GNUC_CONST;
diff --git a/app/widgets/gimphelp-ids.h b/app/widgets/gimphelp-ids.h
index 1d6754e..7e7076a 100644
--- a/app/widgets/gimphelp-ids.h
+++ b/app/widgets/gimphelp-ids.h
@@ -281,6 +281,7 @@
 #define GIMP_HELP_TOOL_FOREGROUND_SELECT          "gimp-tool-foreground-select"
 #define GIMP_HELP_TOOL_FUZZY_SELECT               "gimp-tool-fuzzy-select"
 #define GIMP_HELP_TOOL_GEGL                       "gimp-tool-gegl"
+#define GIMP_HELP_TOOL_HANDLE_TRANSFORM           "gimp-tool-handle-transform"
 #define GIMP_HELP_TOOL_HEAL                       "gimp-tool-heal"
 #define GIMP_HELP_TOOL_HUE_SATURATION             "gimp-tool-hue-saturation"
 #define GIMP_HELP_TOOL_INK                        "gimp-tool-ink"
diff --git a/icons/16/gimp-tool-handle-transform.png b/icons/16/gimp-tool-handle-transform.png
new file mode 100644
index 0000000..8bc2ed4
Binary files /dev/null and b/icons/16/gimp-tool-handle-transform.png differ
diff --git a/icons/22/gimp-tool-handle-transform.png b/icons/22/gimp-tool-handle-transform.png
new file mode 100644
index 0000000..797b99b
Binary files /dev/null and b/icons/22/gimp-tool-handle-transform.png differ
diff --git a/icons/22/gimp-tool-handle-transform.xcf b/icons/22/gimp-tool-handle-transform.xcf
new file mode 100644
index 0000000..e69de29
diff --git a/icons/Makefile.am b/icons/Makefile.am
index a6a030e..71b007c 100644
--- a/icons/Makefile.am
+++ b/icons/Makefile.am
@@ -180,6 +180,7 @@ icons16_DATA = \
        16/gimp-tool-foreground-select.png              \
        16/gimp-tool-free-select.png                    \
        16/gimp-tool-fuzzy-select.png                   \
+       16/gimp-tool-handle-transform.png               \
        16/gimp-tool-heal.png                           \
        16/gimp-tool-hue-saturation.png                 \
        16/gimp-tool-ink.png                            \
@@ -290,6 +291,7 @@ icons22_DATA = \
        22/gimp-tool-foreground-select.png      \
        22/gimp-tool-free-select.png            \
        22/gimp-tool-fuzzy-select.png           \
+       22/gimp-tool-handle-transform.png       \
        22/gimp-tool-heal.png                   \
        22/gimp-tool-hue-saturation.png         \
        22/gimp-tool-ink.png                    \
diff --git a/libgimpwidgets/gimpstock.c b/libgimpwidgets/gimpstock.c
index 3202371..5a7e969 100644
--- a/libgimpwidgets/gimpstock.c
+++ b/libgimpwidgets/gimpstock.c
@@ -256,6 +256,7 @@ static const GtkStockItem gimp_stock_items[] =
   { GIMP_STOCK_TOOL_FOREGROUND_SELECT, N_("_Select"), 0, 0, LIBGIMP_DOMAIN },
   { GIMP_STOCK_TOOL_FUZZY_SELECT,        NULL,        0, 0, LIBGIMP_DOMAIN },
   { GIMP_STOCK_TOOL_HUE_SATURATION,      NULL,        0, 0, LIBGIMP_DOMAIN },
+  { GIMP_STOCK_TOOL_HANDLE_TRANSFORM,N_("_Transform"),0, 0, LIBGIMP_DOMAIN },
   { GIMP_STOCK_TOOL_HEAL,                NULL,        0, 0, LIBGIMP_DOMAIN },
   { GIMP_STOCK_TOOL_INK,                 NULL,        0, 0, LIBGIMP_DOMAIN },
   { GIMP_STOCK_TOOL_ISCISSORS,           NULL,        0, 0, LIBGIMP_DOMAIN },
diff --git a/libgimpwidgets/gimpstock.h b/libgimpwidgets/gimpstock.h
index 9793614..ed23e2d 100644
--- a/libgimpwidgets/gimpstock.h
+++ b/libgimpwidgets/gimpstock.h
@@ -125,6 +125,7 @@ G_BEGIN_DECLS
 #define GIMP_STOCK_TOOL_FREE_SELECT         "gimp-tool-free-select"
 #define GIMP_STOCK_TOOL_FOREGROUND_SELECT   "gimp-tool-foreground-select"
 #define GIMP_STOCK_TOOL_FUZZY_SELECT        "gimp-tool-fuzzy-select"
+#define GIMP_STOCK_TOOL_HANDLE_TRANSFORM    "gimp-tool-handle-transform"
 #define GIMP_STOCK_TOOL_HEAL                "gimp-tool-heal"
 #define GIMP_STOCK_TOOL_HUE_SATURATION      "gimp-tool-hue-saturation"
 #define GIMP_STOCK_TOOL_INK                 "gimp-tool-ink"
diff --git a/menus/image-menu.xml.in b/menus/image-menu.xml.in
index c9162b5..a1025b3 100644
--- a/menus/image-menu.xml.in
+++ b/menus/image-menu.xml.in
@@ -602,6 +602,7 @@
         <menuitem action="tools-shear" />
         <menuitem action="tools-perspective" />
         <menuitem action="tools-unified-transform" />
+        <menuitem action="tools-handle-transform" />
         <menuitem action="tools-flip" />
         <menuitem action="tools-cage" />
         <menuitem action="tools-warp" />
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7b5e93d..c34dbce 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -368,6 +368,8 @@ app/tools/gimpforegroundselecttool.c
 app/tools/gimpfreeselecttool.c
 app/tools/gimpfuzzyselecttool.c
 app/tools/gimpgegltool.c
+app/tools/gimphandletransformoptions.c
+app/tools/gimphandletransformtool.c
 app/tools/gimphealtool.c
 app/tools/gimphistogramoptions.c
 app/tools/gimphuesaturationtool.c


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