[gimp] app, icons: add 3D Transform tool



commit 5646e03eb31b493b496ff0fb5d8b64d403ee2e16
Author: Ell <ell_se yahoo com>
Date:   Mon Jan 6 17:31:30 2020 +0200

    app, icons: add 3D Transform tool
    
    Add a new 3D Transform tool, based on GimpToolTransform3DGrid,
    added in the previous commit.  The tool UI provides a notbook with
    three tabs, corresponding to the three GimpToolTransform3DGrid
    modes:
    
      Camera - allows setting the primary vanishing point, as well as
      the camera's focal length, expressed either directly, or as the
      camera's angle of view, relative to the whole image or the
      transformed item.  By default, the vanishing point is aligned
      with the item's center, and the angle of view is fixed relative
      to the item; this essentially means that each item is transformed
      using a local perspective, independent of its position and size
      relative to the image.  A global perspective can be achieved by
      using a common vanishing point and focal length (or an image-
      relative angle of view).
    
      Move   - allows moving the item using X, Y, and Z offsets.
    
      Rotate - allows rotating the item using X, Y, and Z Euler angles.
      The order of rotation of the different axes can be controlled by
      a set of numbered buttons next to the sliders, and the rotation's
      pivot can be controlled using a pivot selector.

 app/dialogs/dialogs.c                              |    1 +
 app/tools/Makefile.am                              |    4 +
 app/tools/gimp-tools.c                             |    2 +
 app/tools/gimptransform3doptions.c                 |  207 ++++
 app/tools/gimptransform3doptions.h                 |   57 ++
 app/tools/gimptransform3dtool.c                    | 1030 ++++++++++++++++++++
 app/tools/gimptransform3dtool.h                    |   67 ++
 app/tools/meson.build                              |    2 +
 app/tools/tools-enums.c                            |   37 +
 app/tools/tools-enums.h                            |   12 +
 icons/Color/16/gimp-tool-transform-3d.png          |  Bin 0 -> 675 bytes
 icons/Color/16/gimp-transform-3d-camera.png        |  Bin 0 -> 440 bytes
 icons/Color/16/gimp-transform-3d-move.png          |  Bin 0 -> 665 bytes
 icons/Color/16/gimp-transform-3d-rotate.png        |  Bin 0 -> 766 bytes
 icons/Color/24/gimp-tool-transform-3d.png          |  Bin 0 -> 1057 bytes
 icons/Color/24/gimp-tool-transform-3d.svg          |  133 +++
 icons/Color/icon-list.mk                           |   10 +
 icons/Color/scalable/gimp-tool-transform-3d.svg    |  132 +++
 icons/Color/scalable/gimp-transform-3d-camera.svg  |   68 ++
 icons/Color/scalable/gimp-transform-3d-move.svg    |  120 +++
 icons/Color/scalable/gimp-transform-3d-rotate.svg  |  114 +++
 .../24/gimp-tool-transform-3d-symbolic.svg         |   87 ++
 icons/Symbolic/icon-list.mk                        |   10 +
 .../scalable/gimp-tool-transform-3d-symbolic.svg   |   85 ++
 .../scalable/gimp-transform-3d-camera-symbolic.svg |   79 ++
 .../scalable/gimp-transform-3d-move-symbolic.svg   |  120 +++
 .../scalable/gimp-transform-3d-rotate-symbolic.svg |  134 +++
 libgimpwidgets/gimpicons.h                         |    5 +
 po/POTFILES.in                                     |    2 +
 29 files changed, 2518 insertions(+)
---
diff --git a/app/dialogs/dialogs.c b/app/dialogs/dialogs.c
index 6cbabfe6ca..32eaf081fd 100644
--- a/app/dialogs/dialogs.c
+++ b/app/dialogs/dialogs.c
@@ -242,6 +242,7 @@ static const GimpDialogFactoryEntry entries[] =
   FOREIGN ("gimp-shear-tool-dialog",               TRUE,  FALSE),
   FOREIGN ("gimp-text-tool-dialog",                TRUE,  TRUE),
   FOREIGN ("gimp-threshold-tool-dialog",           TRUE,  FALSE),
+  FOREIGN ("gimp-transform-3d-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),
diff --git a/app/tools/Makefile.am b/app/tools/Makefile.am
index a65543ce89..bbe45841cd 100644
--- a/app/tools/Makefile.am
+++ b/app/tools/Makefile.am
@@ -214,6 +214,10 @@ libapptools_a_sources = \
        gimptooloptions-gui.h           \
        gimptools-utils.c               \
        gimptools-utils.h               \
+       gimptransform3doptions.c        \
+       gimptransform3doptions.h        \
+       gimptransform3dtool.c           \
+       gimptransform3dtool.h           \
        gimptransformgridoptions.c      \
        gimptransformgridoptions.h      \
        gimptransformgridtool.c         \
diff --git a/app/tools/gimp-tools.c b/app/tools/gimp-tools.c
index 3d8b5ff764..ad1f18c410 100644
--- a/app/tools/gimp-tools.c
+++ b/app/tools/gimp-tools.c
@@ -84,6 +84,7 @@
 #include "gimpsheartool.h"
 #include "gimpsmudgetool.h"
 #include "gimptexttool.h"
+#include "gimptransform3dtool.h"
 #include "gimpunifiedtransformtool.h"
 #include "gimpvectortool.h"
 #include "gimpwarptool.h"
@@ -149,6 +150,7 @@ gimp_tools_init (Gimp *gimp)
     gimp_shear_tool_register,
     gimp_handle_transform_tool_register,
     gimp_perspective_tool_register,
+    gimp_transform_3d_tool_register,
     gimp_flip_tool_register,
     gimp_cage_tool_register,
     gimp_warp_tool_register,
diff --git a/app/tools/gimptransform3doptions.c b/app/tools/gimptransform3doptions.c
new file mode 100644
index 0000000000..cec7f1b66a
--- /dev/null
+++ b/app/tools/gimptransform3doptions.c
@@ -0,0 +1,207 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.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/gimpwidgets-utils.h"
+
+#include "gimptransform3doptions.h"
+
+#include "gimp-intl.h"
+
+
+enum
+{
+  PROP_0,
+  PROP_MODE,
+  PROP_CONSTRAIN_AXIS,
+  PROP_Z_AXIS,
+  PROP_LOCAL_FRAME
+};
+
+
+static void   gimp_transform_3d_options_set_property (GObject      *object,
+                                                      guint         property_id,
+                                                      const GValue *value,
+                                                      GParamSpec   *pspec);
+static void   gimp_transform_3d_options_get_property (GObject      *object,
+                                                      guint         property_id,
+                                                      GValue       *value,
+                                                      GParamSpec   *pspec);
+
+
+G_DEFINE_TYPE (GimpTransform3DOptions, gimp_transform_3d_options,
+               GIMP_TYPE_TRANSFORM_GRID_OPTIONS)
+
+#define parent_class gimp_transform_3d_options_parent_class
+
+
+static void
+gimp_transform_3d_options_class_init (GimpTransform3DOptionsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = gimp_transform_3d_options_set_property;
+  object_class->get_property = gimp_transform_3d_options_get_property;
+
+  GIMP_CONFIG_PROP_ENUM (object_class, PROP_MODE,
+                         "mode",
+                         _("Mode"),
+                         _("Transform mode"),
+                         GIMP_TYPE_TRANSFORM_3D_MODE,
+                         GIMP_TRANSFORM_3D_MODE_CAMERA,
+                         GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_CONSTRAIN_AXIS,
+                            "constrain-axis",
+                            NULL,
+                            _("Constrain transformation to a single axis"),
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_Z_AXIS,
+                            "z-axis",
+                            NULL,
+                            _("Transform along the Z axis"),
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+
+  GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_LOCAL_FRAME,
+                            "local-frame",
+                            NULL,
+                            _("Transform in the local frame of reference"),
+                            FALSE,
+                            GIMP_PARAM_STATIC_STRINGS);
+}
+
+static void
+gimp_transform_3d_options_init (GimpTransform3DOptions *options)
+{
+}
+
+static void
+gimp_transform_3d_options_set_property (GObject      *object,
+                                        guint         property_id,
+                                        const GValue *value,
+                                        GParamSpec   *pspec)
+{
+  GimpTransform3DOptions *options = GIMP_TRANSFORM_3D_OPTIONS (object);
+
+  switch (property_id)
+    {
+    case PROP_MODE:
+      options->mode = g_value_get_enum (value);
+      break;
+
+    case PROP_CONSTRAIN_AXIS:
+      options->constrain_axis = g_value_get_boolean (value);
+      break;
+    case PROP_Z_AXIS:
+      options->z_axis = g_value_get_boolean (value);
+      break;
+    case PROP_LOCAL_FRAME:
+      options->local_frame = g_value_get_boolean (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_transform_3d_options_get_property (GObject    *object,
+                                        guint       property_id,
+                                        GValue     *value,
+                                        GParamSpec *pspec)
+{
+  GimpTransform3DOptions *options = GIMP_TRANSFORM_3D_OPTIONS (object);
+
+  switch (property_id)
+    {
+    case PROP_MODE:
+      g_value_set_enum (value, options->mode);
+      break;
+
+    case PROP_CONSTRAIN_AXIS:
+      g_value_set_boolean (value, options->constrain_axis);
+      break;
+    case PROP_Z_AXIS:
+      g_value_set_boolean (value, options->z_axis);
+      break;
+    case PROP_LOCAL_FRAME:
+      g_value_set_boolean (value, options->local_frame);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+GtkWidget *
+gimp_transform_3d_options_gui (GimpToolOptions *tool_options)
+{
+  GObject         *config = G_OBJECT (tool_options);
+  GtkWidget       *vbox   = gimp_transform_grid_options_gui (tool_options);
+  GtkWidget       *button;
+  gchar           *label;
+  GdkModifierType  extend_mask    = gimp_get_extend_selection_mask ();
+  GdkModifierType  constrain_mask = gimp_get_constrain_behavior_mask ();
+
+  label = g_strdup_printf (_("Constrain axis (%s)"),
+                           gimp_get_mod_string (extend_mask));
+
+  button = gimp_prop_check_button_new (config, "constrain-axis", label);
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  g_free (label);
+
+  label = g_strdup_printf (_("Z axis (%s)"),
+                           gimp_get_mod_string (constrain_mask));
+
+  button = gimp_prop_check_button_new (config, "z-axis", label);
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  g_free (label);
+
+  label = g_strdup_printf (_("Local frame (%s)"),
+                           gimp_get_mod_string (GDK_MOD1_MASK));
+
+  button = gimp_prop_check_button_new (config, "local-frame", label);
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+  gtk_widget_show (button);
+
+  g_free (label);
+
+  return vbox;
+}
diff --git a/app/tools/gimptransform3doptions.h b/app/tools/gimptransform3doptions.h
new file mode 100644
index 0000000000..b5aa038490
--- /dev/null
+++ b/app/tools/gimptransform3doptions.h
@@ -0,0 +1,57 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TRANSFORM_3D_OPTIONS_H__
+#define __GIMP_TRANSFORM_3D_OPTIONS_H__
+
+
+#include "gimptransformgridoptions.h"
+
+
+#define GIMP_TYPE_TRANSFORM_3D_OPTIONS            (gimp_transform_3d_options_get_type ())
+#define GIMP_TRANSFORM_3D_OPTIONS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_TRANSFORM_3D_OPTIONS, GimpTransform3DOptions))
+#define GIMP_TRANSFORM_3D_OPTIONS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_TRANSFORM_3D_OPTIONS, GimpTransform3DOptionsClass))
+#define GIMP_IS_TRANSFORM_3D_OPTIONS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_TRANSFORM_3D_OPTIONS))
+#define GIMP_IS_TRANSFORM_3D_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), 
GIMP_TYPE_TRANSFORM_3D_OPTIONS))
+#define GIMP_TRANSFORM_3D_OPTIONS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_TRANSFORM_3D_OPTIONS, GimpTransform3DOptionsClass))
+
+
+typedef struct _GimpTransform3DOptions      GimpTransform3DOptions;
+typedef struct _GimpTransform3DOptionsClass GimpTransform3DOptionsClass;
+
+struct _GimpTransform3DOptions
+{
+  GimpTransformGridOptions  parent_instance;
+
+  GimpTransform3DMode       mode;
+  gboolean                  constrain_axis;
+  gboolean                  z_axis;
+  gboolean                  local_frame;
+};
+
+struct _GimpTransform3DOptionsClass
+{
+  GimpTransformGridOptionsClass  parent_class;
+};
+
+
+GType       gimp_transform_3d_options_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_transform_3d_options_gui      (GimpToolOptions *tool_options);
+
+
+#endif /* __GIMP_TRANSFORM_3D_OPTIONS_H__ */
diff --git a/app/tools/gimptransform3dtool.c b/app/tools/gimptransform3dtool.c
new file mode 100644
index 0000000000..3f3fc83cd8
--- /dev/null
+++ b/app/tools/gimptransform3dtool.c
@@ -0,0 +1,1030 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "tools-types.h"
+
+#include "core/gimp-transform-3d-utils.h"
+#include "core/gimpimage.h"
+
+#include "widgets/gimphelp-ids.h"
+#include "widgets/gimppivotselector.h"
+#include "widgets/gimpspinscale.h"
+#include "widgets/gimpwidgets-utils.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplayshell.h"
+#include "display/gimpdisplayshell-transform.h"
+#include "display/gimptoolgui.h"
+#include "display/gimptooltransform3dgrid.h"
+
+#include "gimptoolcontrol.h"
+#include "gimptransform3doptions.h"
+#include "gimptransform3dtool.h"
+
+#include "gimp-intl.h"
+
+
+/*  index into trans_info array  */
+enum
+{
+  VANISHING_POINT_X,
+  VANISHING_POINT_Y,
+  LENS_MODE,
+  LENS_VALUE,
+  OFFSET_X,
+  OFFSET_Y,
+  OFFSET_Z,
+  ROTATION_ORDER,
+  ANGLE_X,
+  ANGLE_Y,
+  ANGLE_Z,
+  PIVOT_X,
+  PIVOT_Y,
+  PIVOT_Z
+};
+
+
+/*  local function prototypes  */
+
+static void             gimp_transform_3d_tool_modifier_key           (GimpTool              *tool,
+                                                                       GdkModifierType        key,
+                                                                       gboolean               press,
+                                                                       GdkModifierType        state,
+                                                                       GimpDisplay           *display);
+
+static gboolean         gimp_transform_3d_tool_info_to_matrix         (GimpTransformGridTool *tg_tool,
+                                                                       GimpMatrix3           *transform);
+static void             gimp_transform_3d_tool_dialog                 (GimpTransformGridTool *tg_tool);
+static void             gimp_transform_3d_tool_dialog_update          (GimpTransformGridTool *tg_tool);
+static void             gimp_transform_3d_tool_prepare                (GimpTransformGridTool *tg_tool);
+static GimpToolWidget * gimp_transform_3d_tool_get_widget             (GimpTransformGridTool *tg_tool);
+static void             gimp_transform_3d_tool_update_widget          (GimpTransformGridTool *tg_tool);
+static void             gimp_transform_3d_tool_widget_changed         (GimpTransformGridTool *tg_tool);
+
+static void             gimp_transform_3d_tool_dialog_changed         (GObject               *object,
+                                                                       GimpTransform3DTool   *t3d);
+static void             gimp_transform_3d_tool_lens_mode_changed      (GtkComboBox           *combo,
+                                                                       GimpTransform3DTool   *t3d);
+static void             gimp_transform_3d_tool_rotation_order_clicked (GtkButton             *button,
+                                                                       GimpTransform3DTool   *t3d);
+static void             gimp_transform_3d_tool_pivot_changed          (GimpPivotSelector     *selector,
+                                                                       GimpTransform3DTool   *t3d);
+
+static gdouble          gimp_transform_3d_tool_get_focal_length       (GimpTransform3DTool   *t3d);
+
+
+G_DEFINE_TYPE (GimpTransform3DTool, gimp_transform_3d_tool, GIMP_TYPE_TRANSFORM_GRID_TOOL)
+
+#define parent_class gimp_transform_3d_tool_parent_class
+
+
+void
+gimp_transform_3d_tool_register (GimpToolRegisterCallback  callback,
+                                 gpointer                  data)
+{
+  (* callback) (GIMP_TYPE_TRANSFORM_3D_TOOL,
+                GIMP_TYPE_TRANSFORM_3D_OPTIONS,
+                gimp_transform_3d_options_gui,
+                GIMP_CONTEXT_PROP_MASK_BACKGROUND,
+                "gimp-transform-3d-tool",
+                _("3D Transform"),
+                _("3D Transform Tool: Apply a 3D transformation to the layer, selection or path"),
+                N_("_3D Transform"), "<shift>W",
+                NULL, GIMP_ICON_TOOL_TRANSFORM_3D,
+                GIMP_ICON_TOOL_TRANSFORM_3D,
+                data);
+}
+
+static void
+gimp_transform_3d_tool_class_init (GimpTransform3DToolClass *klass)
+{
+  GimpToolClass              *tool_class = GIMP_TOOL_CLASS (klass);
+  GimpTransformToolClass     *tr_class   = GIMP_TRANSFORM_TOOL_CLASS (klass);
+  GimpTransformGridToolClass *tg_class   = GIMP_TRANSFORM_GRID_TOOL_CLASS (klass);
+
+  tool_class->modifier_key  = gimp_transform_3d_tool_modifier_key;
+
+  tg_class->info_to_matrix  = gimp_transform_3d_tool_info_to_matrix;
+  tg_class->dialog          = gimp_transform_3d_tool_dialog;
+  tg_class->dialog_update   = gimp_transform_3d_tool_dialog_update;
+  tg_class->prepare         = gimp_transform_3d_tool_prepare;
+  tg_class->get_widget      = gimp_transform_3d_tool_get_widget;
+  tg_class->update_widget   = gimp_transform_3d_tool_update_widget;
+  tg_class->widget_changed  = gimp_transform_3d_tool_widget_changed;
+
+  tr_class->undo_desc       = C_("undo-type", "3D Transform");
+  tr_class->progress_text   = _("3D transformation");
+}
+
+static void
+gimp_transform_3d_tool_init (GimpTransform3DTool *t3d)
+{
+}
+
+static void
+gimp_transform_3d_tool_modifier_key (GimpTool        *tool,
+                                     GdkModifierType  key,
+                                     gboolean         press,
+                                     GdkModifierType  state,
+                                     GimpDisplay     *display)
+{
+  GimpTransform3DOptions *options = GIMP_TRANSFORM_3D_TOOL_GET_OPTIONS (tool);
+
+  if (key == gimp_get_extend_selection_mask ())
+    {
+      g_object_set (options,
+                    "constrain-axis", ! options->constrain_axis,
+                    NULL);
+    }
+  else if (key == gimp_get_constrain_behavior_mask ())
+    {
+      g_object_set (options,
+                    "z-axis", ! options->z_axis,
+                    NULL);
+    }
+  else if (key == GDK_MOD1_MASK)
+    {
+      g_object_set (options,
+                    "local-frame", ! options->local_frame,
+                    NULL);
+    }
+}
+
+static gboolean
+gimp_transform_3d_tool_info_to_matrix (GimpTransformGridTool *tg_tool,
+                                       GimpMatrix3           *transform)
+{
+  GimpTransform3DTool *t3d = GIMP_TRANSFORM_3D_TOOL (tg_tool);
+
+  gimp_transform_3d_matrix (transform,
+
+                            tg_tool->trans_info[VANISHING_POINT_X],
+                            tg_tool->trans_info[VANISHING_POINT_Y],
+                            -gimp_transform_3d_tool_get_focal_length (t3d),
+
+                            tg_tool->trans_info[OFFSET_X],
+                            tg_tool->trans_info[OFFSET_Y],
+                            tg_tool->trans_info[OFFSET_Z],
+
+                            tg_tool->trans_info[ROTATION_ORDER],
+                            tg_tool->trans_info[ANGLE_X],
+                            tg_tool->trans_info[ANGLE_Y],
+                            tg_tool->trans_info[ANGLE_Z],
+
+                            tg_tool->trans_info[PIVOT_X],
+                            tg_tool->trans_info[PIVOT_Y],
+                            tg_tool->trans_info[PIVOT_Z]);
+
+  return TRUE;
+}
+
+static void
+gimp_transform_3d_tool_dialog (GimpTransformGridTool *tg_tool)
+{
+  GimpTransform3DTool    *t3d   = GIMP_TRANSFORM_3D_TOOL (tg_tool);
+  GimpTransform3DOptions *options   = GIMP_TRANSFORM_3D_TOOL_GET_OPTIONS (tg_tool);
+  GtkWidget              *notebook;
+  GtkWidget              *label;
+  GtkWidget              *vbox;
+  GtkWidget              *frame;
+  GtkWidget              *vbox2;
+  GtkWidget              *se;
+  GtkWidget              *spinbutton;
+  GtkWidget              *combo;
+  GtkWidget              *scale;
+  GtkWidget              *grid;
+  GtkWidget              *button;
+  GtkWidget              *selector;
+  gint                    i;
+
+  /* main notebook */
+  notebook = gtk_notebook_new ();
+  gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);
+  gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (tg_tool->gui)),
+                      notebook, FALSE, FALSE, 0);
+  gtk_widget_show (notebook);
+
+  t3d->notebook = notebook;
+
+  /* camera page */
+  label = gtk_image_new_from_icon_name (GIMP_ICON_TRANSFORM_3D_CAMERA,
+                                        GTK_ICON_SIZE_MENU);
+  gimp_help_set_help_data (label, _("Camera"), NULL);
+  gtk_widget_show (label);
+
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
+  gtk_widget_show (vbox);
+
+  /* vanishing-point frame */
+  frame = gimp_frame_new (_("Vanishing Point"));
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_container_add (GTK_CONTAINER (frame), vbox2);
+  gtk_widget_show (vbox2);
+
+  /* vanishing-point size entry */
+  se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a", TRUE, TRUE, FALSE, 6,
+                            GIMP_SIZE_ENTRY_UPDATE_SIZE);
+  gtk_grid_set_row_spacing (GTK_GRID (se), 2);
+  gtk_grid_set_column_spacing (GTK_GRID (se), 2);
+  gtk_box_pack_start (GTK_BOX (vbox2), se, FALSE, FALSE, 0);
+  gtk_widget_show (se);
+
+  t3d->vanishing_point_se = se;
+
+  spinbutton = gimp_spin_button_new_with_range (0.0, 0.0, 1.0);
+  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+  gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 6);
+  gimp_size_entry_add_field (GIMP_SIZE_ENTRY (se),
+                             GTK_SPIN_BUTTON (spinbutton), NULL);
+  gtk_grid_attach (GTK_GRID (se), spinbutton, 1, 0, 1, 1);
+  gtk_widget_show (spinbutton);
+
+  gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (se), _("_X:"), 0, 0, 0.0);
+  gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (se), _("_Y:"), 1, 0, 0.0);
+
+  gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (se), 0, 2);
+  gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (se), 1, 2);
+
+  g_signal_connect (se, "value-changed",
+                    G_CALLBACK (gimp_transform_3d_tool_dialog_changed),
+                    t3d);
+
+  /* lens frame */
+  frame = gimp_frame_new (NULL);
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  /* lens-mode combo */
+  combo = gimp_enum_combo_box_new (GIMP_TYPE_TRANSFORM_3D_LENS_MODE);
+  gtk_frame_set_label_widget (GTK_FRAME (frame), combo);
+  gtk_widget_show (combo);
+
+  t3d->lens_mode_combo = combo;
+
+  g_signal_connect (combo, "changed",
+                    G_CALLBACK (gimp_transform_3d_tool_lens_mode_changed),
+                    t3d);
+
+  vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_container_add (GTK_CONTAINER (frame), vbox2);
+  gtk_widget_show (vbox2);
+
+  /* focal-length size entry */
+  se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a", TRUE, FALSE, FALSE, 6,
+                            GIMP_SIZE_ENTRY_UPDATE_SIZE);
+  gtk_grid_set_row_spacing (GTK_GRID (se), 2);
+  gtk_box_pack_start (GTK_BOX (vbox2), se, FALSE, FALSE, 0);
+
+  t3d->focal_length_se = se;
+
+  gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (se), 0, 2);
+
+  gimp_size_entry_set_value_boundaries (GIMP_SIZE_ENTRY (se), 0,
+                                        0.0, G_MAXDOUBLE);
+
+  g_signal_connect (se, "value-changed",
+                    G_CALLBACK (gimp_transform_3d_tool_dialog_changed),
+                    t3d);
+
+  /* angle-of-view spin scale */
+  t3d->angle_of_view_adj = (GtkAdjustment *)
+    gtk_adjustment_new (0, 0, 180, 1, 10, 0);
+  scale = gimp_spin_scale_new (t3d->angle_of_view_adj,
+                               _("Angle"), 2);
+  gtk_box_pack_start (GTK_BOX (vbox2), scale, FALSE, FALSE, 0);
+  gtk_widget_show (scale);
+
+  t3d->angle_of_view_scale = scale;
+
+  g_signal_connect (t3d->angle_of_view_adj, "value-changed",
+                    G_CALLBACK (gimp_transform_3d_tool_dialog_changed),
+                    t3d);
+
+  /* move page */
+  label = gtk_image_new_from_icon_name (GIMP_ICON_TRANSFORM_3D_MOVE,
+                                        GTK_ICON_SIZE_MENU);
+  gimp_help_set_help_data (label, _("Move"), NULL);
+  gtk_widget_show (label);
+
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
+  gtk_widget_show (vbox);
+
+  /* offset frame */
+  frame = gimp_frame_new (_("Offset"));
+  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+  gtk_widget_show (frame);
+
+  vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+  gtk_container_add (GTK_CONTAINER (frame), vbox2);
+  gtk_widget_show (vbox2);
+
+  /* offset size entry */
+  se = gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a", TRUE, TRUE, FALSE, 6,
+                            GIMP_SIZE_ENTRY_UPDATE_SIZE);
+  gtk_grid_set_row_spacing (GTK_GRID (se), 2);
+  gtk_grid_set_column_spacing (GTK_GRID (se), 2);
+  gtk_box_pack_start (GTK_BOX (vbox2), se, FALSE, FALSE, 0);
+  gtk_widget_show (se);
+
+  t3d->offset_se = se;
+
+  grid = gtk_grid_new ();
+  gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
+  gtk_grid_set_column_spacing (GTK_GRID (grid), 2);
+  gtk_grid_attach (GTK_GRID (se), grid, 0, 0, 2, 1);
+  gtk_widget_show (grid);
+
+  spinbutton = gimp_spin_button_new_with_range (0.0, 0.0, 1.0);
+  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+  gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 6);
+  gimp_size_entry_add_field (GIMP_SIZE_ENTRY (se),
+                             GTK_SPIN_BUTTON (spinbutton), NULL);
+  gtk_grid_attach (GTK_GRID (grid), spinbutton, 1, 1, 1, 1);
+  gtk_widget_show (spinbutton);
+
+  spinbutton = gimp_spin_button_new_with_range (0.0, 0.0, 1.0);
+  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+  gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 6);
+  gimp_size_entry_add_field (GIMP_SIZE_ENTRY (se),
+                             GTK_SPIN_BUTTON (spinbutton), NULL);
+  gtk_grid_attach (GTK_GRID (grid), spinbutton, 1, 0, 1, 1);
+  gtk_widget_show (spinbutton);
+
+  label = gtk_label_new_with_mnemonic (_("_X:"));
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
+  gtk_widget_show (label);
+
+  label = gtk_label_new_with_mnemonic (_("_Y:"));
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
+  gtk_widget_show (label);
+
+  label = gtk_label_new_with_mnemonic (_("_Z:"));
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_grid_attach (GTK_GRID (se), label, 0, 1, 1, 1);
+  gtk_widget_show (label);
+
+  gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (se), 0,
+                                         -GIMP_MAX_IMAGE_SIZE,
+                                         GIMP_MAX_IMAGE_SIZE);
+  gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (se), 1,
+                                         -GIMP_MAX_IMAGE_SIZE,
+                                         GIMP_MAX_IMAGE_SIZE);
+  gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (se), 2,
+                                         -GIMP_MAX_IMAGE_SIZE,
+                                         GIMP_MAX_IMAGE_SIZE);
+
+  gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (se), 0, 2);
+  gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (se), 1, 2);
+  gimp_size_entry_set_refval_digits (GIMP_SIZE_ENTRY (se), 2, 2);
+
+  g_signal_connect (se, "value-changed",
+                    G_CALLBACK (gimp_transform_3d_tool_dialog_changed),
+                    t3d);
+
+  /* rotate page */
+  label = gtk_image_new_from_icon_name (GIMP_ICON_TRANSFORM_3D_ROTATE,
+                                        GTK_ICON_SIZE_MENU);
+  gimp_help_set_help_data (label, _("Rotate"), NULL);
+  gtk_widget_show (label);
+
+  /* angle frame */
+  frame = gimp_frame_new (_("Angle"));
+  gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame, label);
+  gtk_widget_show (frame);
+
+  grid = gtk_grid_new ();
+  gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
+  gtk_grid_set_column_spacing (GTK_GRID (grid), 2);
+  gtk_container_add (GTK_CONTAINER (frame), grid);
+  gtk_widget_show (grid);
+
+  for (i = 0; i < 3; i++)
+    {
+      const gchar *labels[3] = {_("X"), _("Y"), _("Z")};
+
+      /* rotation-order button */
+      button = gtk_button_new ();
+      gimp_help_set_help_data (button, _("Rotation axis order"), NULL);
+      gtk_grid_attach (GTK_GRID (grid), button, 0, i, 1, 1);
+      gtk_widget_show (button);
+
+      t3d->rotation_order_buttons[i] = button;
+
+      g_signal_connect (button, "clicked",
+                        G_CALLBACK (gimp_transform_3d_tool_rotation_order_clicked),
+                        t3d);
+
+      /* angle spin scale */
+      t3d->angle_adj[i] = (GtkAdjustment *)
+        gtk_adjustment_new (0, -180, 180, 1, 10, 0);
+      scale = gimp_spin_scale_new (t3d->angle_adj[i],
+                                   labels[i], 2);
+      gtk_widget_set_hexpand (GTK_WIDGET (scale), TRUE);
+      gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (scale), TRUE);
+      gtk_grid_attach (GTK_GRID (grid), scale, 1, i, 1, 1);
+      gtk_widget_show (scale);
+
+      g_signal_connect (t3d->angle_adj[i], "value-changed",
+                        G_CALLBACK (gimp_transform_3d_tool_dialog_changed),
+                        t3d);
+    }
+
+  /* pivot selector */
+  selector = gimp_pivot_selector_new (0.0, 0.0, 0.0, 0.0);
+  gtk_grid_attach (GTK_GRID (grid), selector, 2, 0, 1, 3);
+  gtk_widget_show (selector);
+
+  t3d->pivot_selector = selector;
+
+  g_signal_connect (selector, "changed",
+                    G_CALLBACK (gimp_transform_3d_tool_pivot_changed),
+                    t3d);
+
+  g_object_bind_property (options,        "mode",
+                          t3d->notebook,  "page",
+                          G_BINDING_SYNC_CREATE |
+                          G_BINDING_BIDIRECTIONAL);
+}
+
+static void
+gimp_transform_3d_tool_dialog_update (GimpTransformGridTool *tg_tool)
+{
+  GimpTransform3DTool     *t3d     = GIMP_TRANSFORM_3D_TOOL (tg_tool);
+  Gimp3DTrasnformLensMode  lens_mode;
+  gint                     permutation[3];
+  gint                     i;
+
+  t3d->updating = TRUE;
+
+  /* vanishing-point size entry */
+  gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (t3d->vanishing_point_se),
+                              0, tg_tool->trans_info[VANISHING_POINT_X]);
+  gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (t3d->vanishing_point_se),
+                              1, tg_tool->trans_info[VANISHING_POINT_Y]);
+
+  lens_mode = tg_tool->trans_info[LENS_MODE];
+
+  /* lens-mode combo */
+  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (t3d->lens_mode_combo),
+                                 lens_mode);
+
+  /* focal-length size entry / angle-of-view spin scale */
+  gtk_widget_set_visible (t3d->focal_length_se,
+                          lens_mode ==
+                          GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH);
+  gtk_widget_set_visible (t3d->angle_of_view_scale,
+                          lens_mode !=
+                          GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH);
+
+  switch (lens_mode)
+    {
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH:
+      gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (t3d->focal_length_se),
+                                  0, tg_tool->trans_info[LENS_VALUE]);
+      break;
+
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE:
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM:
+      gtk_adjustment_set_value (
+        t3d->angle_of_view_adj,
+        gimp_rad_to_deg (tg_tool->trans_info[LENS_VALUE]));
+      break;
+    }
+
+  /* offset size entry */
+  gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (t3d->offset_se),
+                              0, tg_tool->trans_info[OFFSET_X]);
+  gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (t3d->offset_se),
+                              1, tg_tool->trans_info[OFFSET_Y]);
+  gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (t3d->offset_se),
+                              2, tg_tool->trans_info[OFFSET_Z]);
+
+  /* rotation-order buttons */
+  gimp_transform_3d_rotation_order_to_permutation (
+    tg_tool->trans_info[ROTATION_ORDER], permutation);
+
+  for (i = 0; i < 3; i++)
+    {
+      gchar *label;
+
+      label = g_strdup_printf ("%d", i + 1);
+
+      gtk_button_set_label (
+        GTK_BUTTON (t3d->rotation_order_buttons[permutation[i]]), label);
+
+      g_free (label);
+    }
+
+  /* angle spin scales */
+  gtk_adjustment_set_value (t3d->angle_adj[0],
+                            gimp_rad_to_deg (tg_tool->trans_info[ANGLE_X]));
+  gtk_adjustment_set_value (t3d->angle_adj[1],
+                            gimp_rad_to_deg (tg_tool->trans_info[ANGLE_Y]));
+  gtk_adjustment_set_value (t3d->angle_adj[2],
+                            gimp_rad_to_deg (tg_tool->trans_info[ANGLE_Z]));
+
+  /* pivot selector */
+  gimp_pivot_selector_set_position (GIMP_PIVOT_SELECTOR (t3d->pivot_selector),
+                                    tg_tool->trans_info[PIVOT_X],
+                                    tg_tool->trans_info[PIVOT_Y]);
+
+  t3d->updating = FALSE;
+}
+
+static void
+gimp_transform_3d_tool_prepare (GimpTransformGridTool *tg_tool)
+{
+  GimpTool            *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool   *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpTransform3DTool *t3d     = GIMP_TRANSFORM_3D_TOOL (tg_tool);
+  GimpDisplay         *display = tool->display;
+  GimpDisplayShell    *shell   = gimp_display_get_shell (display);
+  GimpImage           *image   = gimp_display_get_image (display);
+  GimpSizeEntry       *se;
+  gdouble              xres;
+  gdouble              yres;
+  gint                 width;
+  gint                 height;
+
+  gimp_image_get_resolution (image, &xres, &yres);
+
+  width  = gimp_image_get_width  (image);
+  height = gimp_image_get_height (image);
+
+  tg_tool->trans_info[VANISHING_POINT_X] = (tr_tool->x1 + tr_tool->x2) / 2.0;
+  tg_tool->trans_info[VANISHING_POINT_Y] = (tr_tool->y1 + tr_tool->y2) / 2.0;
+
+  tg_tool->trans_info[LENS_MODE]         = GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM;
+  tg_tool->trans_info[LENS_VALUE]        = gimp_deg_to_rad (45.0);
+
+  tg_tool->trans_info[OFFSET_X]          = 0.0;
+  tg_tool->trans_info[OFFSET_Y]          = 0.0;
+  tg_tool->trans_info[OFFSET_Z]          = 0.0;
+
+  tg_tool->trans_info[ROTATION_ORDER]    =
+    gimp_transform_3d_permutation_to_rotation_order ((const gint[]) {0, 1, 2});
+  tg_tool->trans_info[ANGLE_X]           = 0.0;
+  tg_tool->trans_info[ANGLE_Y]           = 0.0;
+  tg_tool->trans_info[ANGLE_Z]           = 0.0;
+
+  tg_tool->trans_info[PIVOT_X]           = (tr_tool->x1 + tr_tool->x2) / 2.0;
+  tg_tool->trans_info[PIVOT_Y]           = (tr_tool->y1 + tr_tool->y2) / 2.0;
+  tg_tool->trans_info[PIVOT_Z]           = 0.0;
+
+  t3d->updating = TRUE;
+
+  /* vanishing-point size entry */
+  se = GIMP_SIZE_ENTRY (t3d->vanishing_point_se);
+
+  gimp_size_entry_set_unit (se, shell->unit);
+
+  gimp_size_entry_set_resolution (se, 0, xres, FALSE);
+  gimp_size_entry_set_resolution (se, 1, yres, FALSE);
+
+  gimp_size_entry_set_refval_boundaries (se, 0,
+                                         -GIMP_MAX_IMAGE_SIZE,
+                                         GIMP_MAX_IMAGE_SIZE);
+  gimp_size_entry_set_refval_boundaries (se, 1,
+                                         -GIMP_MAX_IMAGE_SIZE,
+                                         GIMP_MAX_IMAGE_SIZE);
+
+  gimp_size_entry_set_size (se, 0, tr_tool->x1, tr_tool->x2);
+  gimp_size_entry_set_size (se, 1, tr_tool->y1, tr_tool->y2);
+
+  /* focal-length size entry */
+  se = GIMP_SIZE_ENTRY (t3d->focal_length_se);
+
+  gimp_size_entry_set_unit (se, shell->unit);
+
+  gimp_size_entry_set_resolution (se, 0, width >= height ? xres : yres, FALSE);
+
+  /* offset size entry */
+  se = GIMP_SIZE_ENTRY (t3d->offset_se);
+
+  gimp_size_entry_set_unit (se, shell->unit);
+
+  gimp_size_entry_set_resolution (se, 0, xres, FALSE);
+  gimp_size_entry_set_resolution (se, 1, yres, FALSE);
+  gimp_size_entry_set_resolution (se, 2, width >= height ? xres : yres, FALSE);
+
+  gimp_size_entry_set_size (se, 0, 0, width);
+  gimp_size_entry_set_size (se, 1, 0, height);
+  gimp_size_entry_set_size (se, 2, 0, MAX (width, height));
+
+  /* pivot selector */
+  gimp_pivot_selector_set_bounds (GIMP_PIVOT_SELECTOR (t3d->pivot_selector),
+                                  tr_tool->x1, tr_tool->y1,
+                                  tr_tool->x2, tr_tool->y2);
+
+  t3d->updating = FALSE;
+}
+
+static GimpToolWidget *
+gimp_transform_3d_tool_get_widget (GimpTransformGridTool *tg_tool)
+{
+  GimpTool               *tool    = GIMP_TOOL (tg_tool);
+  GimpTransformTool      *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpTransform3DTool    *t3d     = GIMP_TRANSFORM_3D_TOOL (tg_tool);
+  GimpTransform3DOptions *options = GIMP_TRANSFORM_3D_TOOL_GET_OPTIONS (tg_tool);
+  GimpDisplayShell       *shell   = gimp_display_get_shell (tool->display);
+  GimpToolWidget         *widget;
+  gint                    i;
+
+  static const gchar *bound_properties[] =
+  {
+    "mode",
+    "constrain-axis",
+    "z-axis",
+    "local-frame",
+  };
+
+  widget = gimp_tool_transform_3d_grid_new (
+    shell,
+    tr_tool->x1,
+    tr_tool->y1,
+    tr_tool->x2,
+    tr_tool->y2,
+    tg_tool->trans_info[VANISHING_POINT_X],
+    tg_tool->trans_info[VANISHING_POINT_Y],
+    -gimp_transform_3d_tool_get_focal_length (t3d));
+
+  for (i = 0; i < G_N_ELEMENTS (bound_properties); i++)
+    {
+      g_object_bind_property (options, bound_properties[i],
+                              widget,  bound_properties[i],
+                              G_BINDING_SYNC_CREATE |
+                              G_BINDING_BIDIRECTIONAL);
+    }
+
+  return widget;
+}
+
+static void
+gimp_transform_3d_tool_update_widget (GimpTransformGridTool *tg_tool)
+{
+  GimpTransformTool   *tr_tool = GIMP_TRANSFORM_TOOL (tg_tool);
+  GimpTransform3DTool *t3d     = GIMP_TRANSFORM_3D_TOOL (tg_tool);
+  GimpMatrix3          transform;
+
+  GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->update_widget (tg_tool);
+
+  gimp_transform_grid_tool_info_to_matrix (tg_tool, &transform);
+
+  g_object_set (
+    tg_tool->widget,
+
+    "x1",             (gdouble) tr_tool->x1,
+    "y1",             (gdouble) tr_tool->y1,
+    "x2",             (gdouble) tr_tool->x2,
+    "y2",             (gdouble) tr_tool->y2,
+
+    "camera-x",       tg_tool->trans_info[VANISHING_POINT_X],
+    "camera-y",       tg_tool->trans_info[VANISHING_POINT_Y],
+    "camera-z",       -gimp_transform_3d_tool_get_focal_length (t3d),
+
+    "offset-x",       tg_tool->trans_info[OFFSET_X],
+    "offset-y",       tg_tool->trans_info[OFFSET_Y],
+    "offset-z",       tg_tool->trans_info[OFFSET_Z],
+
+    "rotation-order", (gint) tg_tool->trans_info[ROTATION_ORDER],
+    "angle-x",        tg_tool->trans_info[ANGLE_X],
+    "angle-y",        tg_tool->trans_info[ANGLE_Y],
+    "angle-z",        tg_tool->trans_info[ANGLE_Z],
+
+    "pivot-3d-x",     tg_tool->trans_info[PIVOT_X],
+    "pivot-3d-y",     tg_tool->trans_info[PIVOT_Y],
+    "pivot-3d-z",     tg_tool->trans_info[PIVOT_Z],
+
+    "transform",      &transform,
+
+    NULL);
+}
+
+static void
+gimp_transform_3d_tool_widget_changed (GimpTransformGridTool *tg_tool)
+{
+  g_object_get (
+    tg_tool->widget,
+
+    "camera-x", &tg_tool->trans_info[VANISHING_POINT_X],
+    "camera-y", &tg_tool->trans_info[VANISHING_POINT_Y],
+
+    "offset-x", &tg_tool->trans_info[OFFSET_X],
+    "offset-y", &tg_tool->trans_info[OFFSET_Y],
+    "offset-z", &tg_tool->trans_info[OFFSET_Z],
+
+    "angle-x",  &tg_tool->trans_info[ANGLE_X],
+    "angle-y",  &tg_tool->trans_info[ANGLE_Y],
+    "angle-z",  &tg_tool->trans_info[ANGLE_Z],
+
+    NULL);
+
+  GIMP_TRANSFORM_GRID_TOOL_CLASS (parent_class)->widget_changed (tg_tool);
+}
+
+static void
+gimp_transform_3d_tool_dialog_changed (GObject             *object,
+                                       GimpTransform3DTool *t3d)
+{
+  GimpTool              *tool    = GIMP_TOOL (t3d);
+  GimpTransformTool     *tr_tool = GIMP_TRANSFORM_TOOL (t3d);
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (t3d);
+
+  if (t3d->updating)
+    return;
+
+  /* vanishing-point size entry */
+  tg_tool->trans_info[VANISHING_POINT_X] = gimp_size_entry_get_refval (
+    GIMP_SIZE_ENTRY (t3d->vanishing_point_se), 0);
+  tg_tool->trans_info[VANISHING_POINT_Y] = gimp_size_entry_get_refval (
+    GIMP_SIZE_ENTRY (t3d->vanishing_point_se), 1);
+
+  /* focal-length size entry / angle-of-view spin scale */
+  switch ((gint) tg_tool->trans_info[LENS_MODE])
+    {
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH:
+      tg_tool->trans_info[LENS_VALUE] = gimp_size_entry_get_refval (
+        GIMP_SIZE_ENTRY (t3d->focal_length_se), 0);
+      break;
+
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE:
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM:
+      tg_tool->trans_info[LENS_VALUE] = gimp_deg_to_rad (
+        gtk_adjustment_get_value (t3d->angle_of_view_adj));
+      break;
+    }
+
+  /* offset size entry */
+  tg_tool->trans_info[OFFSET_X] = gimp_size_entry_get_refval (
+    GIMP_SIZE_ENTRY (t3d->offset_se), 0);
+  tg_tool->trans_info[OFFSET_Y] = gimp_size_entry_get_refval (
+    GIMP_SIZE_ENTRY (t3d->offset_se), 1);
+  tg_tool->trans_info[OFFSET_Z] = gimp_size_entry_get_refval (
+    GIMP_SIZE_ENTRY (t3d->offset_se), 2);
+
+  /* angle spin scales */
+  tg_tool->trans_info[ANGLE_X] = gimp_deg_to_rad (gtk_adjustment_get_value (
+    t3d->angle_adj[0]));
+  tg_tool->trans_info[ANGLE_Y] = gimp_deg_to_rad (gtk_adjustment_get_value (
+    t3d->angle_adj[1]));
+  tg_tool->trans_info[ANGLE_Z] = gimp_deg_to_rad (gtk_adjustment_get_value (
+    t3d->angle_adj[2]));
+
+  gimp_transform_grid_tool_push_internal_undo (tg_tool, TRUE);
+
+  gimp_transform_tool_recalc_matrix (tr_tool, tool->display);
+}
+
+static void
+gimp_transform_3d_tool_lens_mode_changed (GtkComboBox         *combo,
+                                          GimpTransform3DTool *t3d)
+{
+  GimpTool              *tool    = GIMP_TOOL (t3d);
+  GimpTransformTool     *tr_tool = GIMP_TRANSFORM_TOOL (t3d);
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (t3d);
+  GimpDisplay           *display = tool->display;
+  GimpImage             *image   = gimp_display_get_image (display);
+  gdouble                x1      = 0.0;
+  gdouble                y1      = 0.0;
+  gdouble                x2      = 0.0;
+  gdouble                y2      = 0.0;
+  gint                   width;
+  gint                   height;
+  gint                   lens_mode;
+  gdouble                focal_length;
+
+  if (t3d->updating)
+    return;
+
+  if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &lens_mode))
+    return;
+
+  width  = gimp_image_get_width  (image);
+  height = gimp_image_get_height (image);
+
+  focal_length = gimp_transform_3d_tool_get_focal_length (t3d);
+
+  tg_tool->trans_info[LENS_MODE] = lens_mode;
+
+  switch (lens_mode)
+    {
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH:
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE:
+      x1 = 0.0;
+      y1 = 0.0;
+
+      x2 = width;
+      y2 = height;
+      break;
+
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM:
+      x1 = tr_tool->x1;
+      y1 = tr_tool->y1;
+
+      x2 = tr_tool->x2;
+      y2 = tr_tool->y2;
+      break;
+    }
+
+  switch (lens_mode)
+    {
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH:
+      tg_tool->trans_info[LENS_VALUE] = focal_length;
+      break;
+
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE:
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM:
+      tg_tool->trans_info[LENS_VALUE] =
+        gimp_transform_3d_focal_length_to_angle_of_view (
+          focal_length, x2 - x1, y2 - y1);
+      break;
+    }
+
+  /* vanishing-point size entry */
+  gimp_size_entry_set_size (GIMP_SIZE_ENTRY (t3d->vanishing_point_se), 0,
+                            x1, x2);
+  gimp_size_entry_set_size (GIMP_SIZE_ENTRY (t3d->vanishing_point_se), 1,
+                            y1, y2);
+
+  gimp_transform_grid_tool_push_internal_undo (tg_tool, TRUE);
+
+  gimp_transform_tool_recalc_matrix (tr_tool, tool->display);
+}
+
+static void
+gimp_transform_3d_tool_rotation_order_clicked (GtkButton           *button,
+                                               GimpTransform3DTool *t3d)
+{
+  GimpTool              *tool = GIMP_TOOL (t3d);
+  GimpTransformTool     *tr_tool = GIMP_TRANSFORM_TOOL (t3d);
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (t3d);
+  GimpMatrix4            matrix;
+  gint                   permutation[3];
+  gint                   b;
+  gint                   i;
+
+  for (b = 0; b < 3; b++)
+    {
+      if (GTK_BUTTON (t3d->rotation_order_buttons[b]) == button)
+        break;
+    }
+
+  gimp_transform_3d_rotation_order_to_permutation (
+    tg_tool->trans_info[ROTATION_ORDER], permutation);
+
+  if (permutation[0] == b)
+    {
+      gint temp;
+
+      temp           = permutation[1];
+      permutation[1] = permutation[2];
+      permutation[2] = temp;
+    }
+  else
+    {
+      gint temp;
+
+      temp           = permutation[0];
+      permutation[0] = b;
+
+      for (i = 1; i < 3; i++)
+        {
+          if (permutation[i] == b)
+            {
+              permutation[i] = temp;
+
+              break;
+            }
+        }
+    }
+
+  gimp_matrix4_identity (&matrix);
+
+  gimp_transform_3d_matrix4_rotate_euler (&matrix,
+                                          tg_tool->trans_info[ROTATION_ORDER],
+                                          tg_tool->trans_info[ANGLE_X],
+                                          tg_tool->trans_info[ANGLE_Y],
+                                          tg_tool->trans_info[ANGLE_Z],
+                                          0.0, 0.0, 0.0);
+
+  tg_tool->trans_info[ROTATION_ORDER] =
+    gimp_transform_3d_permutation_to_rotation_order (permutation);
+
+  gimp_transform_3d_matrix4_rotate_euler_decompose (
+    &matrix,
+    tg_tool->trans_info[ROTATION_ORDER],
+    &tg_tool->trans_info[ANGLE_X],
+    &tg_tool->trans_info[ANGLE_Y],
+    &tg_tool->trans_info[ANGLE_Z]);
+
+  gimp_transform_grid_tool_push_internal_undo (tg_tool, TRUE);
+
+  gimp_transform_tool_recalc_matrix (tr_tool, tool->display);
+}
+
+static void
+gimp_transform_3d_tool_pivot_changed (GimpPivotSelector   *selector,
+                                      GimpTransform3DTool *t3d)
+{
+  GimpTool              *tool = GIMP_TOOL (t3d);
+  GimpTransformTool     *tr_tool = GIMP_TRANSFORM_TOOL (t3d);
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (t3d);
+  GimpMatrix4            matrix;
+  gdouble                offset_x;
+  gdouble                offset_y;
+  gdouble                offset_z;
+
+  if (t3d->updating)
+    return;
+
+  gimp_matrix4_identity (&matrix);
+
+  gimp_transform_3d_matrix4_rotate_euler (&matrix,
+                                          tg_tool->trans_info[ROTATION_ORDER],
+                                          tg_tool->trans_info[ANGLE_X],
+                                          tg_tool->trans_info[ANGLE_Y],
+                                          tg_tool->trans_info[ANGLE_Z],
+                                          tg_tool->trans_info[PIVOT_X],
+                                          tg_tool->trans_info[PIVOT_Y],
+                                          tg_tool->trans_info[PIVOT_Z]);
+
+  gimp_pivot_selector_get_position (GIMP_PIVOT_SELECTOR (t3d->pivot_selector),
+                                    &tg_tool->trans_info[PIVOT_X],
+                                    &tg_tool->trans_info[PIVOT_Y]);
+
+  gimp_matrix4_transform_point (&matrix,
+                                tg_tool->trans_info[PIVOT_X],
+                                tg_tool->trans_info[PIVOT_Y],
+                                tg_tool->trans_info[PIVOT_Z],
+                                &offset_x, &offset_y, &offset_z);
+
+  tg_tool->trans_info[OFFSET_X] += offset_x - tg_tool->trans_info[PIVOT_X];
+  tg_tool->trans_info[OFFSET_Y] += offset_y - tg_tool->trans_info[PIVOT_Y];
+  tg_tool->trans_info[OFFSET_Z] += offset_z - tg_tool->trans_info[PIVOT_Z];
+
+  gimp_transform_grid_tool_push_internal_undo (tg_tool, TRUE);
+
+  gimp_transform_tool_recalc_matrix (tr_tool, tool->display);
+}
+
+static gdouble
+gimp_transform_3d_tool_get_focal_length (GimpTransform3DTool *t3d)
+{
+  GimpTool              *tool    = GIMP_TOOL (t3d);
+  GimpTransformTool     *tr_tool = GIMP_TRANSFORM_TOOL (t3d);
+  GimpTransformGridTool *tg_tool = GIMP_TRANSFORM_GRID_TOOL (t3d);
+  GimpImage             *image   = gimp_display_get_image (tool->display);
+  gdouble                width   = 0.0;
+  gdouble                height  = 0.0;
+
+  switch ((int) tg_tool->trans_info[LENS_MODE])
+    {
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH:
+      return tg_tool->trans_info[LENS_VALUE];
+
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE:
+      width  = gimp_image_get_width  (image);
+      height = gimp_image_get_height (image);
+      break;
+
+    case GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM:
+      width  = tr_tool->x2 - tr_tool->x1;
+      height = tr_tool->y2 - tr_tool->y1;
+      break;
+    }
+
+  return gimp_transform_3d_angle_of_view_to_focal_length (
+    tg_tool->trans_info[LENS_VALUE], width, height);
+}
diff --git a/app/tools/gimptransform3dtool.h b/app/tools/gimptransform3dtool.h
new file mode 100644
index 0000000000..636befffeb
--- /dev/null
+++ b/app/tools/gimptransform3dtool.h
@@ -0,0 +1,67 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TRANSFORM_3D_TOOL_H__
+#define __GIMP_TRANSFORM_3D_TOOL_H__
+
+
+#include "gimptransformgridtool.h"
+
+
+#define GIMP_TYPE_TRANSFORM_3D_TOOL           (gimp_transform_3d_tool_get_type ())
+#define GIMP_TRANSFORM_3D_TOOL(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GIMP_TYPE_TRANSFORM_3D_TOOL, GimpTransform3DTool))
+#define GIMP_TRANSFORM_3D_TOOL_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), 
GIMP_TYPE_TRANSFORM_3D_TOOL, GimpTransform3DToolClass))
+#define GIMP_IS_TRANSFORM_3D_TOOL(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GIMP_TYPE_TRANSFORM_3D_TOOL))
+#define GIMP_TRANSFORM_3D_TOOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), 
GIMP_TYPE_TRANSFORM_3D_TOOL, GimpTransform3DToolClass))
+
+#define GIMP_TRANSFORM_3D_TOOL_GET_OPTIONS(t) (GIMP_TRANSFORM_3D_OPTIONS (gimp_tool_get_options (GIMP_TOOL 
(t))))
+
+
+typedef struct _GimpTransform3DTool      GimpTransform3DTool;
+typedef struct _GimpTransform3DToolClass GimpTransform3DToolClass;
+
+struct _GimpTransform3DTool
+{
+  GimpTransformGridTool  parent_instance;
+
+  gboolean               updating;
+
+  GtkWidget             *notebook;
+  GtkWidget             *vanishing_point_se;
+  GtkWidget             *lens_mode_combo;
+  GtkWidget             *focal_length_se;
+  GtkWidget             *angle_of_view_scale;
+  GtkAdjustment         *angle_of_view_adj;
+  GtkWidget             *offset_se;
+  GtkWidget             *rotation_order_buttons[3];
+  GtkAdjustment         *angle_adj[3];
+  GtkWidget             *pivot_selector;
+};
+
+struct _GimpTransform3DToolClass
+{
+  GimpTransformGridToolClass parent_class;
+};
+
+
+void    gimp_transform_3d_tool_register (GimpToolRegisterCallback  callback,
+                                         gpointer                  data);
+
+GType   gimp_transform_3d_tool_get_type (void) G_GNUC_CONST;
+
+
+#endif  /*  __GIMP_TRANSFORM_3D_TOOL_H__  */
diff --git a/app/tools/meson.build b/app/tools/meson.build
index b4c4bfd530..7e05777657 100644
--- a/app/tools/meson.build
+++ b/app/tools/meson.build
@@ -115,6 +115,8 @@ libapptools_sources = [
   'gimptoolcontrol.c',
   'gimptooloptions-gui.c',
   'gimptools-utils.c',
+  'gimptransform3doptions.c',
+  'gimptransform3dtool.c',
   'gimptransformgridoptions.c',
   'gimptransformgridtool.c',
   'gimptransformgridtoolundo.c',
diff --git a/app/tools/tools-enums.c b/app/tools/tools-enums.c
index a7afa38a74..5e637e97e0 100644
--- a/app/tools/tools-enums.c
+++ b/app/tools/tools-enums.c
@@ -261,6 +261,43 @@ gimp_matting_preview_mode_get_type (void)
   return type;
 }
 
+GType
+gimp_transform_3d_lens_mode_get_type (void)
+{
+  static const GEnumValue values[] =
+  {
+    { GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH, "GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH", "focal-length" },
+    { GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE, "GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE", "fov-image" },
+    { GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM, "GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM", "fov-item" },
+    { 0, NULL, NULL }
+  };
+
+  static const GimpEnumDesc descs[] =
+  {
+    { GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH, NC_("3-dtrasnform-lens-mode", "Focal length"), NULL },
+    { GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE, NC_("3-dtrasnform-lens-mode", "Field of view (relative to 
image)"), NULL },
+    /* Translators: this is an abbreviated version of "Field of view (relative to image)".
+       Keep it short. */
+    { GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE, NC_("3-dtrasnform-lens-mode", "FOV (image)"), NULL },
+    { GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM, NC_("3-dtrasnform-lens-mode", "Field of view (relative to 
item)"), NULL },
+    /* Translators: this is an abbreviated version of "Field of view (relative to item)".
+       Keep it short. */
+    { GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM, NC_("3-dtrasnform-lens-mode", "FOV (item)"), NULL },
+    { 0, NULL, NULL }
+  };
+
+  static GType type = 0;
+
+  if (G_UNLIKELY (! type))
+    {
+      type = g_enum_register_static ("Gimp3DTrasnformLensMode", values);
+      gimp_type_set_translation_context (type, "3-dtrasnform-lens-mode");
+      gimp_enum_set_value_descriptions (type, descs);
+    }
+
+  return type;
+}
+
 GType
 gimp_warp_behavior_get_type (void)
 {
diff --git a/app/tools/tools-enums.h b/app/tools/tools-enums.h
index 58d409bdd9..f994bc15bd 100644
--- a/app/tools/tools-enums.h
+++ b/app/tools/tools-enums.h
@@ -138,6 +138,18 @@ typedef enum
 } GimpMattingPreviewMode;
 
 
+#define GIMP_TYPE_TRANSFORM_3D_LENS_MODE (gimp_transform_3d_lens_mode_get_type ())
+
+GType gimp_transform_3d_lens_mode_get_type (void) G_GNUC_CONST;
+
+typedef enum /*< lowercase_name=gimp_transform_3d_lens_mode >*/
+{
+  GIMP_TRANSFORM_3D_LENS_MODE_FOCAL_LENGTH, /*< desc="Focal length" >*/
+  GIMP_TRANSFORM_3D_LENS_MODE_FOV_IMAGE,    /*< desc="Field of view (relative to image)", abbrev="FOV 
(image)" >*/
+  GIMP_TRANSFORM_3D_LENS_MODE_FOV_ITEM,     /*< desc="Field of view (relative to item)", abbrev="FOV (item)" 
*/
+} Gimp3DTrasnformLensMode;
+
+
 #define GIMP_TYPE_WARP_BEHAVIOR (gimp_warp_behavior_get_type ())
 
 GType gimp_warp_behavior_get_type (void) G_GNUC_CONST;
diff --git a/icons/Color/16/gimp-tool-transform-3d.png b/icons/Color/16/gimp-tool-transform-3d.png
new file mode 100644
index 0000000000..24b0a6591b
Binary files /dev/null and b/icons/Color/16/gimp-tool-transform-3d.png differ
diff --git a/icons/Color/16/gimp-transform-3d-camera.png b/icons/Color/16/gimp-transform-3d-camera.png
new file mode 100644
index 0000000000..cfa519ee52
Binary files /dev/null and b/icons/Color/16/gimp-transform-3d-camera.png differ
diff --git a/icons/Color/16/gimp-transform-3d-move.png b/icons/Color/16/gimp-transform-3d-move.png
new file mode 100644
index 0000000000..f91733f571
Binary files /dev/null and b/icons/Color/16/gimp-transform-3d-move.png differ
diff --git a/icons/Color/16/gimp-transform-3d-rotate.png b/icons/Color/16/gimp-transform-3d-rotate.png
new file mode 100644
index 0000000000..f82932c14c
Binary files /dev/null and b/icons/Color/16/gimp-transform-3d-rotate.png differ
diff --git a/icons/Color/24/gimp-tool-transform-3d.png b/icons/Color/24/gimp-tool-transform-3d.png
new file mode 100644
index 0000000000..06d952e15a
Binary files /dev/null and b/icons/Color/24/gimp-tool-transform-3d.png differ
diff --git a/icons/Color/24/gimp-tool-transform-3d.svg b/icons/Color/24/gimp-tool-transform-3d.svg
new file mode 100644
index 0000000000..5881adef4e
--- /dev/null
+++ b/icons/Color/24/gimp-tool-transform-3d.svg
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="24"
+   height="24"
+   viewBox="0 0 6.3499998 6.3500002"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-tool-transform-3d.svg"
+   inkscape:export-xdpi="96"
+   inkscape:export-ydpi="96">
+  <defs
+     id="defs2">
+    <inkscape:path-effect
+       effect="perspective-envelope"
+       up_left_point="-0.75390625,0.24804688"
+       up_right_point="15.753906,3.1240237"
+       down_left_point="-0.75390625,16.753906"
+       down_right_point="15.753906,13.877929"
+       id="path-effect8778"
+       is_visible="true"
+       deform_type="perspective"
+       horizontal_mirror="true"
+       vertical_mirror="false"
+       overflow_perspective="false" />
+    <inkscape:path-effect
+       effect="perspective-envelope"
+       up_left_point="-0.75390625,0.24804688"
+       up_right_point="15.753906,3.1240238"
+       down_left_point="-0.75390625,16.753906"
+       down_right_point="15.753906,13.877929"
+       id="path-effect8778-5"
+       is_visible="true"
+       deform_type="perspective"
+       horizontal_mirror="true"
+       vertical_mirror="false"
+       overflow_perspective="false" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="33.541667"
+     inkscape:cx="12"
+     inkscape:cy="12"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1"
+     inkscape:lockguides="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid815"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-290.64998)">
+    <g
+       id="g15517"
+       transform="matrix(1.5,0,0,1.5,0,-148.5)">
+      <path
+         sodipodi:nodetypes="cccccc"
+         inkscape:path-effect="#path-effect8778-5"
+         inkscape:connector-curvature="0"
+         inkscape:original-d="M -0.75390625,0.24804688 V 1 16.753906 H 15.753906 V 0.24804688 Z"
+         id="rect8574-3"
+         transform="matrix(0.19233318,0,0,0.25647458,0.67416786,292.70303)"
+         d="m -0.75390625,0.24804688 c 0,0 0,0.75195312 0,0.75195312 0,0 0,15.753906 0,15.753906 0,0 
16.50781225,-2.875977 16.50781225,-2.875977 0,0 0,-10.7539052 0,-10.7539052 0,0 -16.50781225,-2.87597692 
-16.50781225,-2.87597692 z"
+         
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;strok
 e-width:
 
1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+      <g
+         id="g15509">
+        <path
+           inkscape:connector-curvature="0"
+           
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;str
 oke-widt
 
h:0.39687499;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.35294118;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           d="m 0.95343015,293.23329 v 0.85576 l 0.84749345,0.0775 v -0.77205 z m 0,1.22163 v 0.85628 l 
0.84749345,-0.0419 v -0.77256 z m 0.84749345,1.14518 -0.84749345,0.077 v 0.85628 l 0.84749345,-0.16123 z"
+           id="rect8574-3-6"
+           sodipodi:nodetypes="ccccccccccccccc" />
+        <path
+           inkscape:connector-curvature="0"
+           
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;str
 oke-widt
 
h:0.33072916;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.35294118;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           d="m 2.1151162,293.45395 v 0.74104 l 0.6444053,0.0589 v -0.67748 z m 0,1.05837 v 0.74156 l 
0.6444053,-0.0315 v -0.678 z m 0.6444053,0.99988 -0.6444053,0.0589 v 0.74156 l 0.6444053,-0.12247 z"
+           id="rect8574-3-6-2"
+           sodipodi:nodetypes="ccccccccccccccc" />
+        <path
+           inkscape:connector-curvature="0"
+           
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;str
 oke-widt
 
h:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.35294118;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           d="m 3.0029172,293.62293 v 0.65319 l 0.5059125,0.0465 v -0.60358 z m 0,0.93329 v 0.6537 l 
0.5059125,-0.0248 v -0.6041 z m 0.5059125,0.88728 -0.5059125,0.0465 v 0.65371 l 0.5059125,-0.0961 z"
+           id="rect8574-3-6-9"
+           sodipodi:nodetypes="ccccccccccccccc" />
+      </g>
+      <path
+         inkscape:path-effect="#path-effect8778"
+         inkscape:connector-curvature="0"
+         inkscape:original-d="M -0.75390625,0.24804688 V 1 16.753906 H 15.753906 V 0.24804688 Z M 
0.75390625,1.7539062 H 4.2519531 V 5.2519531 H 0.75390625 Z m 4.99609375,0 h 3.5 v 3.4980469 h -3.5 z m 
4.998047,0 h 3.498047 V 5.2519531 H 10.748047 Z M 0.75390625,6.75 H 4.2519531 v 3.5 H 0.75390625 Z M 
5.75,6.75 h 3.5 v 3.5 h -3.5 z m 4.998047,0 h 3.498047 v 3.5 H 10.748047 Z M 0.75390625,11.748047 H 4.2519531 
v 3.5 H 0.75390625 Z m 4.99609375,0 h 3.5 v 3.5 h -3.5 z m 4.998047,0 h 3.498047 v 3.5 h -3.498047 z"
+         id="rect8574"
+         transform="matrix(0.19233318,0,0,0.25647458,0.67416786,292.70303)"
+         d="m -0.75390625,0.24804688 c 0,0 0,0.75195312 0,0.75195312 0,0 0,15.753906 0,15.753906 0,0 
16.50781225,-2.875977 16.50781225,-2.875977 0,0 0,-10.7539053 0,-10.7539053 0,0 -16.50781225,-2.87597682 
-16.50781225,-2.87597682 z M 1.4525936,2.068179 c 0,0 4.4045654,0.6273444 4.4045654,0.6273444 0,0 0,3.0098615 
0,3.0098615 0,0 -4.4045654,-0.302095 -4.4045654,-0.302095 0,0 0,-3.3351109 0,-3.3351109 z M 
7.491225,2.9282643 c 0,0 3.350875,0.4772668 3.350875,0.4772668 0,0 0,2.641755 0,2.641755 0,0 
-3.350875,-0.2298258 -3.350875,-0.2298258 0,0 0,-2.889196 0,-2.889196 z m 4.615892,0.6574437 c 0,0 
2.632122,0.3748944 2.632122,0.3748944 0,0 0,2.353976 0,2.353976 0,0 -2.632122,-0.1805288 -2.632122,-0.1805288 
0,0 0,-2.5483416 0,-2.5483416 z M 1.4525936,6.831559 c 0,0 4.4045654,0.1628063 4.4045654,0.1628063 0,0 
0,3.0115417 0,3.0115417 0,0 -4.4045654,0.162625 -4.4045654,0.162625 0,0 0,-3.336973 0,-3.336973 z M 
7.491225,7.0547654 c 0,0 3.350875,0.1238586 3.350875,0.1238586 0,0 0,2.64323
 01 0,2.6
 432301 0,0 -3.350875,0.1237204 -3.350875,0.1237204 0,0 0,-2.8908091 0,-2.8908091 z m 4.615892,0.1706175 c 
0,0 2.632122,0.097291 2.632122,0.097291 0,0 0,2.3552903 0,2.3552903 0,0 -2.632122,0.097183 -2.632122,0.097183 
0,0 0,-2.5497644 0,-2.5497644 z M 1.4525936,11.596801 c 0,0 4.4045654,-0.301913 4.4045654,-0.301913 0,0 
0,3.011542 0,3.011542 0,0 -4.4045654,0.627344 -4.4045654,0.627344 0,0 0,-3.336973 0,-3.336973 z M 
7.491225,11.18288 c 0,0 3.350875,-0.229688 3.350875,-0.229688 0,0 0,2.64323 0,2.64323 0,0 -3.350875,0.477267 
-3.350875,0.477267 0,0 0,-2.890809 0,-2.890809 z m 4.615892,-0.316399 c 0,0 2.632122,-0.180421 
2.632122,-0.180421 0,0 0,2.355291 0,2.355291 0,0 -2.632122,0.374894 -2.632122,0.374894 0,0 0,-2.549764 
0,-2.549764 z"
+         
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:none;strok
 e-width:
 
1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+    </g>
+  </g>
+</svg>
diff --git a/icons/Color/icon-list.mk b/icons/Color/icon-list.mk
index 3f4df8b36e..e79b30e5bb 100644
--- a/icons/Color/icon-list.mk
+++ b/icons/Color/icon-list.mk
@@ -327,9 +327,13 @@ scalable_images = \
        scalable/gimp-tools.svg                                 \
        scalable/gimp-tool-text.svg                             \
        scalable/gimp-tool-threshold.svg                        \
+       scalable/gimp-tool-transform-3d.svg                     \
        scalable/gimp-tool-unified-transform.svg                \
        scalable/gimp-tool-warp.svg                             \
        scalable/gimp-tool-zoom.svg                             \
+       scalable/gimp-transform-3d-camera.svg                   \
+       scalable/gimp-transform-3d-move.svg                     \
+       scalable/gimp-transform-3d-rotate.svg                   \
        scalable/gimp-transparency.svg                          \
        scalable/gimp-undo-history.svg                          \
        scalable/gimp-user-manual.svg                           \
@@ -502,6 +506,7 @@ vector24_images = \
        24/gimp-tool-shear.svg                                  \
        24/gimp-tool-smudge.svg                                 \
        24/gimp-tool-threshold.svg                              \
+       24/gimp-tool-transform-3d.svg                           \
        24/gimp-tool-unified-transform.svg                      \
        24/gimp-tool-text.svg                                   \
        24/gimp-tool-warp.svg                                   \
@@ -801,11 +806,15 @@ icons16_images = \
        16/gimp-tool-smudge.png                                 \
        16/gimp-tool-text.png                                   \
        16/gimp-tool-threshold.png                              \
+       16/gimp-tool-transform-3d.png                           \
        16/gimp-tool-unified-transform.png                      \
        16/gimp-tool-warp.png                                   \
        16/gimp-tool-zoom.png                                   \
        16/gimp-tools.png                                       \
        16/gimp-transparency.png                                \
+       16/gimp-transform-3d-camera.svg                         \
+       16/gimp-transform-3d-move.svg                           \
+       16/gimp-transform-3d-rotate.svg                         \
        16/gimp-undo-history.png                                \
        16/gimp-user-manual.png                                 \
        16/gimp-video.png                                       \
@@ -1012,6 +1021,7 @@ icons24_images = \
        24/gimp-tool-smudge.png                                 \
        24/gimp-tool-text.png                                   \
        24/gimp-tool-threshold.png                              \
+       24/gimp-tool-transform-3d.png                           \
        24/gimp-tool-unified-transform.png                      \
        24/gimp-tool-warp.png                                   \
        24/gimp-tool-zoom.png                                   \
diff --git a/icons/Color/scalable/gimp-tool-transform-3d.svg b/icons/Color/scalable/gimp-tool-transform-3d.svg
new file mode 100644
index 0000000000..30ad31c216
--- /dev/null
+++ b/icons/Color/scalable/gimp-tool-transform-3d.svg
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="16"
+   height="16"
+   viewBox="0 0 4.2333332 4.2333335"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-tool-transform-3d.svg"
+   inkscape:export-xdpi="96"
+   inkscape:export-ydpi="96">
+  <defs
+     id="defs2">
+    <inkscape:path-effect
+       effect="perspective-envelope"
+       up_left_point="-0.75390625,0.24804688"
+       up_right_point="15.753906,3.1240237"
+       down_left_point="-0.75390625,16.753906"
+       down_right_point="15.753906,13.877929"
+       id="path-effect8778"
+       is_visible="true"
+       deform_type="perspective"
+       horizontal_mirror="true"
+       vertical_mirror="false"
+       overflow_perspective="false" />
+    <inkscape:path-effect
+       effect="perspective-envelope"
+       up_left_point="-0.75390625,0.24804688"
+       up_right_point="15.753906,3.1240238"
+       down_left_point="-0.75390625,16.753906"
+       down_right_point="15.753906,13.877929"
+       id="path-effect8778-5"
+       is_visible="true"
+       deform_type="perspective"
+       horizontal_mirror="true"
+       vertical_mirror="false"
+       overflow_perspective="false" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="50.3125"
+     inkscape:cx="7.9503106"
+     inkscape:cy="8"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1"
+     inkscape:lockguides="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid815"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-292.76665)">
+    <g
+       id="g15517">
+      <path
+         sodipodi:nodetypes="cccccc"
+         inkscape:path-effect="#path-effect8778-5"
+         inkscape:connector-curvature="0"
+         inkscape:original-d="M -0.75390625,0.24804688 V 1 16.753906 H 15.753906 V 0.24804688 Z"
+         id="rect8574-3"
+         transform="matrix(0.19233318,0,0,0.25647458,0.67416786,292.70303)"
+         d="m -0.75390625,0.24804688 c 0,0 0,0.75195312 0,0.75195312 0,0 0,15.753906 0,15.753906 0,0 
16.50781225,-2.875977 16.50781225,-2.875977 0,0 0,-10.7539052 0,-10.7539052 0,0 -16.50781225,-2.87597692 
-16.50781225,-2.87597692 z"
+         
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:none;strok
 e-width:
 
1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+      <g
+         id="g15509">
+        <path
+           inkscape:connector-curvature="0"
+           
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;str
 oke-widt
 
h:0.39687499;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.35294118;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           d="m 0.95343015,293.23329 v 0.85576 l 0.84749345,0.0775 v -0.77205 z m 0,1.22163 v 0.85628 l 
0.84749345,-0.0419 v -0.77256 z m 0.84749345,1.14518 -0.84749345,0.077 v 0.85628 l 0.84749345,-0.16123 z"
+           id="rect8574-3-6"
+           sodipodi:nodetypes="ccccccccccccccc" />
+        <path
+           inkscape:connector-curvature="0"
+           
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;str
 oke-widt
 
h:0.33072917;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.35294118;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           d="m 2.1151162,293.45395 v 0.74104 l 0.6444053,0.0589 v -0.67748 z m 0,1.05837 v 0.74156 l 
0.6444053,-0.0315 v -0.678 z m 0.6444053,0.99988 -0.6444053,0.0589 v 0.74156 l 0.6444053,-0.12247 z"
+           id="rect8574-3-6-2"
+           sodipodi:nodetypes="ccccccccccccccc" />
+        <path
+           inkscape:connector-curvature="0"
+           
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;str
 oke-widt
 
h:0.26458333;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.35294118;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           d="m 3.0029172,293.62293 v 0.65319 l 0.5059125,0.0465 v -0.60358 z m 0,0.93329 v 0.6537 l 
0.5059125,-0.0248 v -0.6041 z m 0.5059125,0.88728 -0.5059125,0.0465 v 0.65371 l 0.5059125,-0.0961 z"
+           id="rect8574-3-6-9"
+           sodipodi:nodetypes="ccccccccccccccc" />
+      </g>
+      <path
+         inkscape:path-effect="#path-effect8778"
+         inkscape:connector-curvature="0"
+         inkscape:original-d="M -0.75390625,0.24804688 V 1 16.753906 H 15.753906 V 0.24804688 Z M 
0.75390625,1.7539062 H 4.2519531 V 5.2519531 H 0.75390625 Z m 4.99609375,0 h 3.5 v 3.4980469 h -3.5 z m 
4.998047,0 h 3.498047 V 5.2519531 H 10.748047 Z M 0.75390625,6.75 H 4.2519531 v 3.5 H 0.75390625 Z M 
5.75,6.75 h 3.5 v 3.5 h -3.5 z m 4.998047,0 h 3.498047 v 3.5 H 10.748047 Z M 0.75390625,11.748047 H 4.2519531 
v 3.5 H 0.75390625 Z m 4.99609375,0 h 3.5 v 3.5 h -3.5 z m 4.998047,0 h 3.498047 v 3.5 h -3.498047 z"
+         id="rect8574"
+         transform="matrix(0.19233318,0,0,0.25647458,0.67416786,292.70303)"
+         d="m -0.75390625,0.24804688 c 0,0 0,0.75195312 0,0.75195312 0,0 0,15.753906 0,15.753906 0,0 
16.50781225,-2.875977 16.50781225,-2.875977 0,0 0,-10.7539053 0,-10.7539053 0,0 -16.50781225,-2.87597682 
-16.50781225,-2.87597682 z M 1.4525936,2.068179 c 0,0 4.4045654,0.6273444 4.4045654,0.6273444 0,0 0,3.0098615 
0,3.0098615 0,0 -4.4045654,-0.302095 -4.4045654,-0.302095 0,0 0,-3.3351109 0,-3.3351109 z M 
7.491225,2.9282643 c 0,0 3.350875,0.4772668 3.350875,0.4772668 0,0 0,2.641755 0,2.641755 0,0 
-3.350875,-0.2298258 -3.350875,-0.2298258 0,0 0,-2.889196 0,-2.889196 z m 4.615892,0.6574437 c 0,0 
2.632122,0.3748944 2.632122,0.3748944 0,0 0,2.353976 0,2.353976 0,0 -2.632122,-0.1805288 -2.632122,-0.1805288 
0,0 0,-2.5483416 0,-2.5483416 z M 1.4525936,6.831559 c 0,0 4.4045654,0.1628063 4.4045654,0.1628063 0,0 
0,3.0115417 0,3.0115417 0,0 -4.4045654,0.162625 -4.4045654,0.162625 0,0 0,-3.336973 0,-3.336973 z M 
7.491225,7.0547654 c 0,0 3.350875,0.1238586 3.350875,0.1238586 0,0 0,2.64323
 01 0,2.6
 432301 0,0 -3.350875,0.1237204 -3.350875,0.1237204 0,0 0,-2.8908091 0,-2.8908091 z m 4.615892,0.1706175 c 
0,0 2.632122,0.097291 2.632122,0.097291 0,0 0,2.3552903 0,2.3552903 0,0 -2.632122,0.097183 -2.632122,0.097183 
0,0 0,-2.5497644 0,-2.5497644 z M 1.4525936,11.596801 c 0,0 4.4045654,-0.301913 4.4045654,-0.301913 0,0 
0,3.011542 0,3.011542 0,0 -4.4045654,0.627344 -4.4045654,0.627344 0,0 0,-3.336973 0,-3.336973 z M 
7.491225,11.18288 c 0,0 3.350875,-0.229688 3.350875,-0.229688 0,0 0,2.64323 0,2.64323 0,0 -3.350875,0.477267 
-3.350875,0.477267 0,0 0,-2.890809 0,-2.890809 z m 4.615892,-0.316399 c 0,0 2.632122,-0.180421 
2.632122,-0.180421 0,0 0,2.355291 0,2.355291 0,0 -2.632122,0.374894 -2.632122,0.374894 0,0 0,-2.549764 
0,-2.549764 z"
+         
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:none;strok
 e-width:
 
1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+    </g>
+  </g>
+</svg>
diff --git a/icons/Color/scalable/gimp-transform-3d-camera.svg 
b/icons/Color/scalable/gimp-transform-3d-camera.svg
new file mode 100644
index 0000000000..c819c36aee
--- /dev/null
+++ b/icons/Color/scalable/gimp-transform-3d-camera.svg
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="16"
+   height="16"
+   viewBox="0 0 4.2333332 4.2333335"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-transform-3d-camera.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="50.312497"
+     inkscape:cx="9.3812271"
+     inkscape:cy="9.0216884"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid863"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-292.76666)">
+    <path
+       inkscape:connector-curvature="0"
+       id="rect9996"
+       d="m 0.26458333,293.82499 v 2.11667 H 2.6458333 v -0.88212 l 1.3229167,0.88212 v -2.11667 l 
-1.3229167,0.88212 v -0.88212 z"
+       
style="fill:#729fcf;fill-opacity:1;stroke:#204a87;stroke-width:0.26458333;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 />
+  </g>
+</svg>
diff --git a/icons/Color/scalable/gimp-transform-3d-move.svg b/icons/Color/scalable/gimp-transform-3d-move.svg
new file mode 100644
index 0000000000..e5bdac4c23
--- /dev/null
+++ b/icons/Color/scalable/gimp-transform-3d-move.svg
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="16"
+   height="16"
+   viewBox="0 0 4.2333332 4.2333335"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-transform-3d-move.svg">
+  <defs
+     id="defs2">
+    <inkscape:path-effect
+       effect="transform_2pts"
+       start="1,294.88333"
+       end="4.2333331,294.88333"
+       id="path-effect9373"
+       is_visible="true"
+       first_knot="1"
+       last_knot="2"
+       helper_size="3"
+       stretch="1.25"
+       offset="6"
+       elastic="true"
+       from_original_width="true"
+       flip_vertical="false"
+       flip_horizontal="false"
+       lock_lenght="false"
+       lock_angle="false" />
+    <marker
+       inkscape:stockid="Arrow1Mstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Mstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path2579"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         
style="fill-rule:evenodd;stroke:#bebebe;stroke-width:1pt;stroke-opacity:1;fill:#bebebe;fill-opacity:1"
+         transform="scale(0.4) translate(10,0)" />
+    </marker>
+    <linearGradient
+       gradientTransform="matrix(0.23151041,0,0,0.23151043,1.0890012,293.03123)"
+       gradientUnits="userSpaceOnUse"
+       y2="305.36914"
+       x2="311.70312"
+       y1="305.36914"
+       x1="295.70312"
+       id="linearGradient7060-0"
+       xlink:href="#linearGradient19282-4" />
+    <linearGradient
+       gradientTransform="matrix(0.34682586,0,0,0.30620888,-53.35239,564.0385)"
+       osb:paint="solid"
+       id="linearGradient19282-4">
+      <stop
+         id="stop19284-0"
+         offset="0"
+         style="stop-color:#bebebe;stop-opacity:1;" />
+    </linearGradient>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="50.3125"
+     inkscape:cx="8"
+     inkscape:cy="8"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid863"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-292.76666)">
+    <path
+       inkscape:connector-curvature="0"
+       
style="display:inline;fill:#729fcf;fill-opacity:1;stroke:#204a87;stroke-width:0.13229167;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+       d="m 2.1216418,293.03125 -0.6995064,0.69815 h 0.4702579 v 0.93101 c -0.3110135,-0.003 
-0.622175,-0.004 -0.93327863,-0.007 v -0.46166 l -0.69453134,0.68887 0.69453134,0.6986 v -0.46438 c 
0.31109443,-1e-5 0.62218183,-7e-4 0.93327863,0 v 0.46302 0.46393 H 1.4221354 l 0.6995064,0.69362 
0.6895561,-0.69362 H 2.3481772 v -0.46393 -0.46302 c 0.3086821,2.1e-4 0.6173641,9.2e-4 0.9260416,0 v 0.46438 
l 0.6945312,-0.6986 -0.6945312,-0.68866 v 0.46213 c -0.3086545,0.006 -0.617246,0.008 -0.9260416,0.008 v 
-0.93282 h 0.4630207 z"
+       id="path74740" />
+  </g>
+</svg>
diff --git a/icons/Color/scalable/gimp-transform-3d-rotate.svg 
b/icons/Color/scalable/gimp-transform-3d-rotate.svg
new file mode 100644
index 0000000000..8c0d2f3586
--- /dev/null
+++ b/icons/Color/scalable/gimp-transform-3d-rotate.svg
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="16"
+   height="16"
+   viewBox="0 0 4.2333332 4.2333335"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-transform-3d-rotate.svg">
+  <defs
+     id="defs2">
+    <inkscape:path-effect
+       effect="transform_2pts"
+       start="1,294.88333"
+       end="4.2333331,294.88333"
+       id="path-effect9373"
+       is_visible="true"
+       first_knot="1"
+       last_knot="2"
+       helper_size="3"
+       stretch="1.25"
+       offset="6"
+       elastic="true"
+       from_original_width="true"
+       flip_vertical="false"
+       flip_horizontal="false"
+       lock_lenght="false"
+       lock_angle="false" />
+    <marker
+       inkscape:stockid="Arrow1Mstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Mstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path2579"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         
style="fill-rule:evenodd;stroke:#bebebe;stroke-width:1pt;stroke-opacity:1;fill:#bebebe;fill-opacity:1"
+         transform="scale(0.4) translate(10,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="35.57631"
+     inkscape:cx="1.1769946"
+     inkscape:cy="7.8071068"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid863"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-292.76666)"
+     style="display:inline">
+    <g
+       id="g3386">
+      <circle
+         r="0.5291667"
+         cx="2.1166666"
+         cy="294.88333"
+         id="path3956"
+         
style="fill:#729fcf;fill-opacity:1;stroke:#204a87;stroke-width:0.13229167;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 />
+      <g
+         style="stroke-width:0.13229167;stroke-miterlimit:4;stroke-dasharray:none;stroke-linejoin:miter"
+         id="g12965">
+        <path
+           id="path3958"
+           transform="matrix(0.26458333,0,0,0.26458333,0,292.76666)"
+           d="M 8.0019531 2.2480469 L 8.0019531 3.7539062 C 9.7230717 3.7539062 11.271044 4.7829618 
11.929688 6.3730469 C 12.588331 7.9631697 12.222873 9.7849453 11.005859 11.001953 C 9.7888447 12.218999 
7.9611996 12.584402 6.3710938 11.925781 C 4.781594 11.267374 3.7507855 9.7203323 3.75 8 L 5.5 8 L 3 4.0507812 
L 0.5 8 L 2.2519531 8 C 2.2527461 10.322721 3.6567033 12.42358 5.8027344 13.3125 C 7.9493722 14.201672 
10.427347 13.709405 12.070312 12.066406 C 13.713278 10.423446 14.19776 7.9454864 13.308594 5.7988281 C 
12.419427 3.6522077 10.325457 2.2480469 8.0019531 2.2480469 z "
+           
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;
 stroke-w
 
idth:0.50000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
 />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/icons/Symbolic/24/gimp-tool-transform-3d-symbolic.svg 
b/icons/Symbolic/24/gimp-tool-transform-3d-symbolic.svg
new file mode 100644
index 0000000000..c983032cc4
--- /dev/null
+++ b/icons/Symbolic/24/gimp-tool-transform-3d-symbolic.svg
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="24"
+   height="24"
+   viewBox="0 0 6.3499998 6.3500002"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-tool-transform-3d.svg"
+   inkscape:export-xdpi="96"
+   inkscape:export-ydpi="96">
+  <defs
+     id="defs2">
+    <inkscape:path-effect
+       effect="perspective-envelope"
+       up_left_point="-0.75390625,0.24804688"
+       up_right_point="15.753906,3.1240237"
+       down_left_point="-0.75390625,16.753906"
+       down_right_point="15.753906,13.877929"
+       id="path-effect8778"
+       is_visible="true"
+       deform_type="perspective"
+       horizontal_mirror="true"
+       vertical_mirror="false"
+       overflow_perspective="false" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="33.541667"
+     inkscape:cx="12"
+     inkscape:cy="12"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1"
+     inkscape:lockguides="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid815"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-290.64998)">
+    <path
+       
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-
 width:1.
 
5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m -0.75390625,0.24804688 c 0,0 0,0.75195312 0,0.75195312 0,0 0,15.753906 0,15.753906 0,0 
16.50781225,-2.875977 16.50781225,-2.875977 0,0 0,-10.7539053 0,-10.7539053 0,0 -16.50781225,-2.87597682 
-16.50781225,-2.87597682 z M 1.4525936,2.068179 c 0,0 4.4045654,0.6273444 4.4045654,0.6273444 0,0 0,3.0098615 
0,3.0098615 0,0 -4.4045654,-0.302095 -4.4045654,-0.302095 0,0 0,-3.3351109 0,-3.3351109 z M 
7.491225,2.9282643 c 0,0 3.350875,0.4772668 3.350875,0.4772668 0,0 0,2.641755 0,2.641755 0,0 
-3.350875,-0.2298258 -3.350875,-0.2298258 0,0 0,-2.889196 0,-2.889196 z m 4.615892,0.6574437 c 0,0 
2.632122,0.3748944 2.632122,0.3748944 0,0 0,2.353976 0,2.353976 0,0 -2.632122,-0.1805288 -2.632122,-0.1805288 
0,0 0,-2.5483416 0,-2.5483416 z M 1.4525936,6.831559 c 0,0 4.4045654,0.1628063 4.4045654,0.1628063 0,0 
0,3.0115417 0,3.0115417 0,0 -4.4045654,0.162625 -4.4045654,0.162625 0,0 0,-3.336973 0,-3.336973 z M 
7.491225,7.0547654 c 0,0 3.350875,0.1238586 3.350875,0.1238586 0,0 0,2.6432301
  0,2.643
 2301 0,0 -3.350875,0.1237204 -3.350875,0.1237204 0,0 0,-2.8908091 0,-2.8908091 z m 4.615892,0.1706175 c 0,0 
2.632122,0.097291 2.632122,0.097291 0,0 0,2.3552903 0,2.3552903 0,0 -2.632122,0.097183 -2.632122,0.097183 0,0 
0,-2.5497644 0,-2.5497643 z M 1.4525936,11.596801 c 0,0 4.4045654,-0.301913 4.4045654,-0.301913 0,0 
0,3.011542 0,3.011542 0,0 -4.4045654,0.627344 -4.4045654,0.627344 0,0 0,-3.336973 0,-3.336973 z M 
7.491225,11.18288 c 0,0 3.350875,-0.229688 3.350875,-0.229688 0,0 0,2.64323 0,2.64323 0,0 -3.350875,0.477267 
-3.350875,0.477267 0,0 0,-2.890809 0,-2.890809 z m 4.615892,-0.316399 c 0,0 2.632122,-0.180421 
2.632122,-0.180421 0,0 0,2.355291 0,2.355291 0,0 -2.632122,0.374894 -2.632122,0.374894 0,0 0,-2.549764 
0,-2.549764 z"
+       transform="matrix(0.28849977,0,0,0.38471187,1.0112518,290.55455)"
+       id="rect8574"
+       inkscape:original-d="M -0.75390625,0.24804688 V 1 16.753906 H 15.753906 V 0.24804688 Z M 
0.75390625,1.7539062 H 4.2519531 V 5.2519531 H 0.75390625 Z m 4.99609375,0 h 3.5 v 3.4980469 h -3.5 z m 
4.998047,0 h 3.498047 V 5.2519531 H 10.748047 Z M 0.75390625,6.75 H 4.2519531 v 3.5 H 0.75390625 Z M 
5.75,6.75 h 3.5 v 3.5 h -3.5 z m 4.998047,0 h 3.498047 v 3.5 H 10.748047 Z M 0.75390625,11.748047 H 4.2519531 
v 3.5 H 0.75390625 Z m 4.99609375,0 h 3.5 v 3.5 h -3.5 z m 4.998047,0 h 3.498047 v 3.5 h -3.498047 z"
+       inkscape:connector-curvature="0"
+       inkscape:path-effect="#path-effect8778" />
+  </g>
+</svg>
diff --git a/icons/Symbolic/icon-list.mk b/icons/Symbolic/icon-list.mk
index 36f4fda37a..4930fd8618 100644
--- a/icons/Symbolic/icon-list.mk
+++ b/icons/Symbolic/icon-list.mk
@@ -323,9 +323,13 @@ scalable_images = \
        scalable/gimp-tools-symbolic.svg                                        \
        scalable/gimp-tool-text-symbolic.svg                            \
        scalable/gimp-tool-threshold-symbolic.svg                       \
+       scalable/gimp-tool-transform-3d-symbolic.svg                    \
        scalable/gimp-tool-unified-transform-symbolic.svg               \
        scalable/gimp-tool-warp-symbolic.svg                            \
        scalable/gimp-tool-zoom-symbolic.svg                            \
+       scalable/gimp-transform-3d-camera-symbolic.svg                  \
+       scalable/gimp-transform-3d-move-symbolic.svg                    \
+       scalable/gimp-transform-3d-rotate-symbolic.svg                  \
        scalable/gimp-transparency-symbolic.svg                         \
        scalable/gimp-undo-history-symbolic.svg                         \
        scalable/gimp-user-manual-symbolic.svg                          \
@@ -498,6 +502,7 @@ vector24_images = \
        24/gimp-tool-shear-symbolic.svg                                 \
        24/gimp-tool-smudge-symbolic.svg                                        \
        24/gimp-tool-threshold-symbolic.svg                             \
+       24/gimp-tool-transform-3d-symbolic.svg                          \
        24/gimp-tool-unified-transform-symbolic.svg                     \
        24/gimp-tool-text-symbolic.svg                                  \
        24/gimp-tool-warp-symbolic.svg                                  \
@@ -793,10 +798,14 @@ icons16_images = \
        16/gimp-tool-smudge-symbolic.symbolic.png                               \
        16/gimp-tool-text-symbolic.symbolic.png                                 \
        16/gimp-tool-threshold-symbolic.symbolic.png                            \
+       16/gimp-tool-transform-3d-symbolic.symbolic.png                         \
        16/gimp-tool-unified-transform-symbolic.symbolic.png                    \
        16/gimp-tool-warp-symbolic.symbolic.png                                 \
        16/gimp-tool-zoom-symbolic.symbolic.png                                 \
        16/gimp-tools-symbolic.symbolic.png                                     \
+       16/gimp-transform-3d-camera-symbolic.symbolic.png                       \
+       16/gimp-transform-3d-move-symbolic.symbolic.png                         \
+       16/gimp-transform-3d-rotate-symbolic.symbolic.png                       \
        16/gimp-transparency-symbolic.symbolic.png                              \
        16/gimp-undo-history-symbolic.symbolic.png                              \
        16/gimp-user-manual-symbolic.symbolic.png                               \
@@ -1004,6 +1013,7 @@ icons24_images = \
        24/gimp-tool-smudge-symbolic.symbolic.png                               \
        24/gimp-tool-text-symbolic.symbolic.png                                 \
        24/gimp-tool-threshold-symbolic.symbolic.png                            \
+       24/gimp-tool-transform-3d-symbolic.symbolic.png                         \
        24/gimp-tool-unified-transform-symbolic.symbolic.png                    \
        24/gimp-tool-warp-symbolic.symbolic.png                                 \
        24/gimp-tool-zoom-symbolic.symbolic.png                                 \
diff --git a/icons/Symbolic/scalable/gimp-tool-transform-3d-symbolic.svg 
b/icons/Symbolic/scalable/gimp-tool-transform-3d-symbolic.svg
new file mode 100644
index 0000000000..f1b47e5db4
--- /dev/null
+++ b/icons/Symbolic/scalable/gimp-tool-transform-3d-symbolic.svg
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="16"
+   height="16"
+   viewBox="0 0 4.2333332 4.2333335"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-tool-transform-3d.svg">
+  <defs
+     id="defs2">
+    <inkscape:path-effect
+       effect="perspective-envelope"
+       up_left_point="-0.75390625,0.24804688"
+       up_right_point="15.753906,3.1240236"
+       down_left_point="-0.75390625,16.753906"
+       down_right_point="15.753906,13.877929"
+       id="path-effect8778"
+       is_visible="true"
+       deform_type="perspective"
+       horizontal_mirror="true"
+       vertical_mirror="false"
+       overflow_perspective="false" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="50.3125"
+     inkscape:cx="6.1416149"
+     inkscape:cy="7.9801242"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1"
+     inkscape:lockguides="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid815"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-292.76665)">
+    <path
+       
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-
 width:1.
 
5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       d="m -0.75390625,0.24804688 c 0,0 0,0.75195312 0,0.75195312 0,0 0,15.753906 0,15.753906 0,0 
16.50781225,-2.875977 16.50781225,-2.875977 0,0 0,-10.7539054 0,-10.7539054 0,0 -16.50781225,-2.87597672 
-16.50781225,-2.87597672 z M 1.4525936,2.068179 c 0,0 4.4045654,0.6273444 4.4045654,0.6273444 0,0 0,3.0098615 
0,3.0098615 0,0 -4.4045654,-0.302095 -4.4045654,-0.302095 0,0 0,-3.3351109 0,-3.3351109 z M 
7.491225,2.9282642 c 0,0 3.350875,0.4772668 3.350875,0.4772668 0,0 0,2.6417551 0,2.6417551 0,0 
-3.350875,-0.2298258 -3.350875,-0.2298258 0,0 0,-2.8891961 0,-2.8891961 z m 4.615892,0.6574437 c 0,0 
2.632122,0.3748944 2.632122,0.3748944 0,0 0,2.353976 0,2.353976 0,0 -2.632122,-0.1805288 -2.632122,-0.1805288 
0,0 0,-2.5483416 0,-2.5483416 z M 1.4525936,6.831559 c 0,0 4.4045654,0.1628063 4.4045654,0.1628063 0,0 
0,3.0115417 0,3.0115417 0,0 -4.4045654,0.162625 -4.4045654,0.162625 0,0 0,-3.336973 0,-3.336973 z M 
7.491225,7.0547653 c 0,0 3.350875,0.1238587 3.350875,0.1238587 0,0 0,2.643
 2301 0,2
 .6432301 0,0 -3.350875,0.1237205 -3.350875,0.1237205 0,0 0,-2.8908093 0,-2.8908093 z m 4.615892,0.1706176 c 
0,0 2.632122,0.097291 2.632122,0.097291 0,0 0,2.3552903 0,2.3552903 0,0 -2.632122,0.097183 -2.632122,0.097183 
0,0 0,-2.5497644 0,-2.5497643 z M 1.4525936,11.596801 c 0,0 4.4045654,-0.301913 4.4045654,-0.301913 0,0 
0,3.011542 0,3.011542 0,0 -4.4045654,0.627344 -4.4045654,0.627344 0,0 0,-3.336973 0,-3.336973 z M 
7.491225,11.18288 c 0,0 3.350875,-0.229688 3.350875,-0.229688 0,0 0,2.64323 0,2.64323 0,0 -3.350875,0.477267 
-3.350875,0.477267 0,0 0,-2.890809 0,-2.890809 z m 4.615892,-0.316399 c 0,0 2.632122,-0.180421 
2.632122,-0.180421 0,0 0,2.355291 0,2.355291 0,0 -2.632122,0.374894 -2.632122,0.374894 0,0 0,-2.549764 
0,-2.549764 z"
+       transform="matrix(0.19233318,0,0,0.25647458,0.67416786,292.70303)"
+       id="rect8574"
+       inkscape:original-d="M -0.75390625,0.24804688 V 1 16.753906 H 15.753906 V 0.24804688 Z M 
0.75390625,1.7539062 H 4.2519531 V 5.2519531 H 0.75390625 Z m 4.99609375,0 h 3.5 v 3.4980469 h -3.5 z m 
4.998047,0 h 3.498047 V 5.2519531 H 10.748047 Z M 0.75390625,6.75 H 4.2519531 v 3.5 H 0.75390625 Z M 
5.75,6.75 h 3.5 v 3.5 h -3.5 z m 4.998047,0 h 3.498047 v 3.5 H 10.748047 Z M 0.75390625,11.748047 H 4.2519531 
v 3.5 H 0.75390625 Z m 4.99609375,0 h 3.5 v 3.5 h -3.5 z m 4.998047,0 h 3.498047 v 3.5 h -3.498047 z"
+       inkscape:connector-curvature="0"
+       inkscape:path-effect="#path-effect8778" />
+  </g>
+</svg>
diff --git a/icons/Symbolic/scalable/gimp-transform-3d-camera-symbolic.svg 
b/icons/Symbolic/scalable/gimp-transform-3d-camera-symbolic.svg
new file mode 100644
index 0000000000..bd02242233
--- /dev/null
+++ b/icons/Symbolic/scalable/gimp-transform-3d-camera-symbolic.svg
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="16"
+   height="16"
+   viewBox="0 0 4.2333332 4.2333335"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-camera.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="50.312497"
+     inkscape:cx="9.3812271"
+     inkscape:cy="9.0216884"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid863"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-292.76666)">
+    <g
+       id="g10002">
+      <rect
+         y="293.82498"
+         x="0.26458332"
+         height="2.1166668"
+         width="2.3812499"
+         id="rect9996"
+         
style="fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:0.23932463;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 />
+      <path
+         sodipodi:nodetypes="cccc"
+         inkscape:connector-curvature="0"
+         id="path9998"
+         d="m 2.38125,294.88333 1.5875,-1.05834 v 2.11667 z"
+         
style="fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
 />
+    </g>
+  </g>
+</svg>
diff --git a/icons/Symbolic/scalable/gimp-transform-3d-move-symbolic.svg 
b/icons/Symbolic/scalable/gimp-transform-3d-move-symbolic.svg
new file mode 100644
index 0000000000..69697c4f1c
--- /dev/null
+++ b/icons/Symbolic/scalable/gimp-transform-3d-move-symbolic.svg
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="16"
+   height="16"
+   viewBox="0 0 4.2333332 4.2333335"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-transform-3d-move.svg">
+  <defs
+     id="defs2">
+    <inkscape:path-effect
+       effect="transform_2pts"
+       start="1,294.88333"
+       end="4.2333331,294.88333"
+       id="path-effect9373"
+       is_visible="true"
+       first_knot="1"
+       last_knot="2"
+       helper_size="3"
+       stretch="1.25"
+       offset="6"
+       elastic="true"
+       from_original_width="true"
+       flip_vertical="false"
+       flip_horizontal="false"
+       lock_lenght="false"
+       lock_angle="false" />
+    <marker
+       inkscape:stockid="Arrow1Mstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Mstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path2579"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         
style="fill-rule:evenodd;stroke:#bebebe;stroke-width:1pt;stroke-opacity:1;fill:#bebebe;fill-opacity:1"
+         transform="scale(0.4) translate(10,0)" />
+    </marker>
+    <linearGradient
+       gradientTransform="matrix(0.23151041,0,0,0.23151043,1.0890012,293.03123)"
+       gradientUnits="userSpaceOnUse"
+       y2="305.36914"
+       x2="311.70312"
+       y1="305.36914"
+       x1="295.70312"
+       id="linearGradient7060-0"
+       xlink:href="#linearGradient19282-4" />
+    <linearGradient
+       gradientTransform="matrix(0.34682586,0,0,0.30620888,-53.35239,564.0385)"
+       osb:paint="solid"
+       id="linearGradient19282-4">
+      <stop
+         id="stop19284-0"
+         offset="0"
+         style="stop-color:#bebebe;stop-opacity:1;" />
+    </linearGradient>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="50.3125"
+     inkscape:cx="8"
+     inkscape:cy="8"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid863"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-292.76666)">
+    <path
+       inkscape:connector-curvature="0"
+       
style="display:inline;fill:url(#linearGradient7060-0);fill-opacity:1;stroke:none;stroke-width:0.37041667"
+       d="m 2.1216418,293.03125 -0.6995064,0.69815 h 0.4702579 v 0.93101 c -0.3110135,-0.003 
-0.622175,-0.004 -0.93327863,-0.007 v -0.46166 l -0.69453134,0.68887 0.69453134,0.6986 v -0.46438 c 
0.31109443,-1e-5 0.62218183,-7e-4 0.93327863,0 v 0.46302 0.46393 H 1.4221354 l 0.6995064,0.69362 
0.6895561,-0.69362 H 2.3481772 v -0.46393 -0.46302 c 0.3086821,2.1e-4 0.6173641,9.2e-4 0.9260416,0 v 0.46438 
l 0.6945312,-0.6986 -0.6945312,-0.68866 v 0.46213 c -0.3086545,0.006 -0.617246,0.008 -0.9260416,0.008 v 
-0.93282 h 0.4630207 z"
+       id="path74740" />
+  </g>
+</svg>
diff --git a/icons/Symbolic/scalable/gimp-transform-3d-rotate-symbolic.svg 
b/icons/Symbolic/scalable/gimp-transform-3d-rotate-symbolic.svg
new file mode 100644
index 0000000000..1d08b86e01
--- /dev/null
+++ b/icons/Symbolic/scalable/gimp-transform-3d-rotate-symbolic.svg
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="16"
+   height="16"
+   viewBox="0 0 4.2333332 4.2333335"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+   sodipodi:docname="gimp-3d-rotate.svg">
+  <defs
+     id="defs2">
+    <inkscape:path-effect
+       effect="transform_2pts"
+       start="1,294.88333"
+       end="4.2333331,294.88333"
+       id="path-effect9373"
+       is_visible="true"
+       first_knot="1"
+       last_knot="2"
+       helper_size="3"
+       stretch="1.25"
+       offset="6"
+       elastic="true"
+       from_original_width="true"
+       flip_vertical="false"
+       flip_horizontal="false"
+       lock_lenght="false"
+       lock_angle="false" />
+    <marker
+       inkscape:stockid="Arrow1Mstart"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Mstart"
+       style="overflow:visible"
+       inkscape:isstock="true">
+      <path
+         id="path2579"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         
style="fill-rule:evenodd;stroke:#bebebe;stroke-width:1pt;stroke-opacity:1;fill:#bebebe;fill-opacity:1"
+         transform="scale(0.4) translate(10,0)" />
+    </marker>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="35.57631"
+     inkscape:cx="7.4733193"
+     inkscape:cy="7.8071068"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     units="px"
+     inkscape:window-width="1920"
+     inkscape:window-height="1008"
+     inkscape:window-x="0"
+     inkscape:window-y="35"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid863"
+       empspacing="4" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-292.76666)"
+     style="display:inline">
+    <circle
+       
style="fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:0.10583334;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3956"
+       cy="294.88333"
+       cx="2.1166666"
+       r="0.5291667" />
+    <g
+       id="g12965">
+      <path
+         
style="display:inline;fill:none;fill-opacity:1;stroke:#bebebe;stroke-width:0.39687499;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path3958"
+         sodipodi:type="arc"
+         sodipodi:cx="2.1166666"
+         sodipodi:cy="294.88333"
+         sodipodi:rx="1.3229166"
+         sodipodi:ry="1.3229166"
+         sodipodi:start="4.712389"
+         sodipodi:end="3.1415927"
+         d="m 2.1166666,293.56041 a 1.3229166,1.3229166 0 0 1 1.2222156,0.81666 1.3229166,1.3229166 0 0 1 
-0.2867724,1.4417 1.3229166,1.3229166 0 0 1 -1.4417016,0.28678 1.3229166,1.3229166 0 0 1 -0.81665827,-1.22222"
+         sodipodi:open="true" />
+      <path
+         sodipodi:type="star"
+         
style="display:inline;fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:0.27536848;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         id="path4166"
+         sodipodi:sides="3"
+         sodipodi:cx="0.79374999"
+         sodipodi:cy="294.48645"
+         sodipodi:r1="0.79375464"
+         sodipodi:r2="0.27536848"
+         sodipodi:arg1="-1.5707963"
+         sodipodi:arg2="-0.52359879"
+         inkscape:flatsided="true"
+         inkscape:rounded="0"
+         inkscape:randomized="0"
+         d="m 0.79375001,293.6927 0.68741169,1.19063 -1.3748234,0 z"
+         transform="matrix(0.96224482,0,0,0.87770259,0.02996819,36.063473)"
+         inkscape:transform-center-y="-0.12084757" />
+    </g>
+  </g>
+</svg>
diff --git a/libgimpwidgets/gimpicons.h b/libgimpwidgets/gimpicons.h
index 546f40151a..e47fb7acc5 100644
--- a/libgimpwidgets/gimpicons.h
+++ b/libgimpwidgets/gimpicons.h
@@ -388,10 +388,15 @@ G_BEGIN_DECLS
 #define GIMP_ICON_TOOL_SMUDGE               "gimp-tool-smudge"
 #define GIMP_ICON_TOOL_TEXT                 "gimp-tool-text"
 #define GIMP_ICON_TOOL_THRESHOLD            "gimp-tool-threshold"
+#define GIMP_ICON_TOOL_TRANSFORM_3D         "gimp-tool-transform-3d"
 #define GIMP_ICON_TOOL_UNIFIED_TRANSFORM    "gimp-tool-unified-transform"
 #define GIMP_ICON_TOOL_WARP                 "gimp-tool-warp"
 #define GIMP_ICON_TOOL_ZOOM                 "gimp-tool-zoom"
 
+#define GIMP_ICON_TRANSFORM_3D_CAMERA       "gimp-transform-3d-camera"
+#define GIMP_ICON_TRANSFORM_3D_MOVE         "gimp-transform-3d-move"
+#define GIMP_ICON_TRANSFORM_3D_ROTATE       "gimp-transform-3d-rotate"
+
 #define GIMP_ICON_VIEW_REFRESH              "view-refresh"
 #define GIMP_ICON_VIEW_FULLSCREEN           "view-fullscreen"
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8faa5b2cbc..33dfd07a7e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -502,6 +502,8 @@ app/tools/gimptool.c
 app/tools/gimptooloptions-gui.c
 app/tools/gimptransformgridoptions.c
 app/tools/gimptransformgridtool.c
+app/tools/gimptransform3doptions.c
+app/tools/gimptransform3dtool.c
 app/tools/gimptransformoptions.c
 app/tools/gimptransformtool.c
 app/tools/gimpunifiedtransformtool.c


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