[gimp] app: add GimpToolGyroscope



commit 634d5ae57d7178eb5f8f451e2040b02cf542d9c3
Author: Ell <ell_se yahoo com>
Date:   Tue Apr 10 09:41:41 2018 -0400

    app: add GimpToolGyroscope
    
    GimpToolGyroscope is a tool widget providing canvas interaction for
    3D rotation.  The widget doesn't preset a UI, but rather
    facilitates panning, rotation, and zoom, by dragging the canvas, or
    using the keyboard.
    
    Rotation is expressed using yaw/pitch/roll angles (performed in
    this order).  A zoom factor can be specified, which affects the
    magnitude of the rotation per distance traveled.  The widget can
    operate in inverse mode, performing an inverse transformation.

 app/display/Makefile.am         |    2 +
 app/display/gimptoolgyroscope.c |  857 +++++++++++++++++++++++++++++++++++++++
 app/display/gimptoolgyroscope.h |   58 +++
 po/POTFILES.in                  |    1 +
 4 files changed, 918 insertions(+), 0 deletions(-)
---
diff --git a/app/display/Makefile.am b/app/display/Makefile.am
index e4f9b48..5cc0a7d 100644
--- a/app/display/Makefile.am
+++ b/app/display/Makefile.am
@@ -178,6 +178,8 @@ libappdisplay_a_sources = \
        gimptoolgui.h                           \
        gimptoolcompass.c                       \
        gimptoolcompass.h                       \
+       gimptoolgyroscope.c                     \
+       gimptoolgyroscope.h                     \
        gimptoolhandlegrid.c                    \
        gimptoolhandlegrid.h                    \
        gimptoolline.c                          \
diff --git a/app/display/gimptoolgyroscope.c b/app/display/gimptoolgyroscope.c
new file mode 100644
index 0000000..cf1c35e
--- /dev/null
+++ b/app/display/gimptoolgyroscope.c
@@ -0,0 +1,857 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptoolgyroscope.c
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "libgimpmath/gimpmath.h"
+
+#include "display-types.h"
+
+#include "widgets/gimpwidgets-utils.h"
+
+#include "gimpdisplayshell.h"
+#include "gimpdisplayshell-transform.h"
+#include "gimptoolgyroscope.h"
+
+#include "gimp-intl.h"
+
+
+#define EPSILON    1e-6
+#define DEG_TO_RAD (G_PI / 180.0)
+
+
+typedef enum
+{
+  MODE_NONE,
+  MODE_PAN,
+  MODE_ROTATE,
+  MODE_ZOOM
+} Mode;
+
+typedef enum
+{
+  CONSTRAINT_NONE,
+  CONSTRAINT_UNKNOWN,
+  CONSTRAINT_HORIZONTAL,
+  CONSTRAINT_VERTICAL
+} Constraint;
+
+enum
+{
+  PROP_0,
+  PROP_YAW,
+  PROP_PITCH,
+  PROP_ROLL,
+  PROP_ZOOM,
+  PROP_INVERT,
+  PROP_SPEED,
+  PROP_PIVOT_X,
+  PROP_PIVOT_Y
+};
+
+struct _GimpToolGyroscopePrivate
+{
+  gdouble    yaw;
+  gdouble    pitch;
+  gdouble    roll;
+  gdouble    zoom;
+
+  gdouble    orig_yaw;
+  gdouble    orig_pitch;
+  gdouble    orig_roll;
+  gdouble    orig_zoom;
+
+  gboolean   invert;
+
+  gdouble    speed;
+
+  gdouble    pivot_x;
+  gdouble    pivot_y;
+
+  Mode       mode;
+  Constraint constraint;
+
+  gdouble    last_x;
+  gdouble    last_y;
+
+  gdouble    last_angle;
+  gdouble    curr_angle;
+
+  gdouble    last_zoom;
+};
+
+
+/*  local function prototypes  */
+
+static void       gimp_tool_gyroscope_set_property    (GObject               *object,
+                                                       guint                  property_id,
+                                                       const GValue          *value,
+                                                       GParamSpec            *pspec);
+static void       gimp_tool_gyroscope_get_property    (GObject               *object,
+                                                       guint                  property_id,
+                                                       GValue                *value,
+                                                       GParamSpec            *pspec);
+
+static gint       gimp_tool_gyroscope_button_press    (GimpToolWidget        *widget,
+                                                       const GimpCoords      *coords,
+                                                       guint32                time,
+                                                       GdkModifierType        state,
+                                                       GimpButtonPressType    press_type);
+static void       gimp_tool_gyroscope_button_release  (GimpToolWidget        *widget,
+                                                       const GimpCoords      *coords,
+                                                       guint32                time,
+                                                       GdkModifierType        state,
+                                                       GimpButtonReleaseType  release_type);
+static void       gimp_tool_gyroscope_motion          (GimpToolWidget        *widget,
+                                                       const GimpCoords      *coords,
+                                                       guint32                time,
+                                                       GdkModifierType        state);
+static void       gimp_tool_gyroscope_hover           (GimpToolWidget        *widget,
+                                                       const GimpCoords      *coords,
+                                                       GdkModifierType        state,
+                                                       gboolean               proximity);
+static gboolean   gimp_tool_gyroscope_key_press       (GimpToolWidget        *widget,
+                                                       GdkEventKey           *kevent);
+static void       gimp_tool_gyroscope_motion_modifier (GimpToolWidget        *widget,
+                                                       GdkModifierType        key,
+                                                       gboolean               press,
+                                                       GdkModifierType        state);
+static gboolean   gimp_tool_gyroscope_get_cursor      (GimpToolWidget        *widget,
+                                                       const GimpCoords      *coords,
+                                                       GdkModifierType        state,
+                                                       GimpCursorType        *cursor,
+                                                       GimpToolCursorType    *tool_cursor,
+                                                       GimpCursorModifier    *modifier);
+
+static void       gimp_tool_gyroscope_update_status   (GimpToolGyroscope      *gyroscope,
+                                                       GdkModifierType         state);
+
+static void       gimp_tool_gyroscope_save            (GimpToolGyroscope      *gyroscope);
+static void       gimp_tool_gyroscope_restore         (GimpToolGyroscope      *gyroscope);
+
+static void       gimp_tool_gyroscope_rotate          (GimpToolGyroscope      *gyroscope,
+                                                       const GimpVector3      *axis);
+static void       gimp_tool_gyroscope_rotate_vector   (GimpVector3            *vector,
+                                                       const GimpVector3      *axis);
+
+
+G_DEFINE_TYPE (GimpToolGyroscope, gimp_tool_gyroscope, GIMP_TYPE_TOOL_WIDGET)
+
+#define parent_class gimp_tool_gyroscope_parent_class
+
+
+static void
+gimp_tool_gyroscope_class_init (GimpToolGyroscopeClass *klass)
+{
+  GObjectClass        *object_class = G_OBJECT_CLASS (klass);
+  GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);
+
+  object_class->set_property    = gimp_tool_gyroscope_set_property;
+  object_class->get_property    = gimp_tool_gyroscope_get_property;
+
+  widget_class->button_press    = gimp_tool_gyroscope_button_press;
+  widget_class->button_release  = gimp_tool_gyroscope_button_release;
+  widget_class->motion          = gimp_tool_gyroscope_motion;
+  widget_class->hover           = gimp_tool_gyroscope_hover;
+  widget_class->key_press       = gimp_tool_gyroscope_key_press;
+  widget_class->motion_modifier = gimp_tool_gyroscope_motion_modifier;
+  widget_class->get_cursor      = gimp_tool_gyroscope_get_cursor;
+
+  g_object_class_install_property (object_class, PROP_YAW,
+                                   g_param_spec_double ("yaw", NULL, NULL,
+                                                        -G_MAXDOUBLE,
+                                                        +G_MAXDOUBLE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_PITCH,
+                                   g_param_spec_double ("pitch", NULL, NULL,
+                                                        -G_MAXDOUBLE,
+                                                        +G_MAXDOUBLE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_ROLL,
+                                   g_param_spec_double ("roll", NULL, NULL,
+                                                        -G_MAXDOUBLE,
+                                                        +G_MAXDOUBLE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_ZOOM,
+                                   g_param_spec_double ("zoom", NULL, NULL,
+                                                        0.0,
+                                                        +G_MAXDOUBLE,
+                                                        1.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_INVERT,
+                                   g_param_spec_boolean ("invert", NULL, NULL,
+                                                         FALSE,
+                                                         GIMP_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_SPEED,
+                                   g_param_spec_double ("speed", NULL, NULL,
+                                                        -G_MAXDOUBLE,
+                                                        +G_MAXDOUBLE,
+                                                        1.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_PIVOT_X,
+                                   g_param_spec_double ("pivot-x", NULL, NULL,
+                                                        -G_MAXDOUBLE,
+                                                        +G_MAXDOUBLE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_object_class_install_property (object_class, PROP_PIVOT_Y,
+                                   g_param_spec_double ("pivot-y", NULL, NULL,
+                                                        -G_MAXDOUBLE,
+                                                        +G_MAXDOUBLE,
+                                                        0.0,
+                                                        GIMP_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
+
+  g_type_class_add_private (klass, sizeof (GimpToolGyroscopePrivate));
+}
+
+static void
+gimp_tool_gyroscope_init (GimpToolGyroscope *gyroscope)
+{
+  gyroscope->private = G_TYPE_INSTANCE_GET_PRIVATE (gyroscope,
+                                                    GIMP_TYPE_TOOL_GYROSCOPE,
+                                                    GimpToolGyroscopePrivate);
+}
+
+static void
+gimp_tool_gyroscope_set_property (GObject      *object,
+                                  guint         property_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GimpToolGyroscope        *gyroscope = GIMP_TOOL_GYROSCOPE (object);
+  GimpToolGyroscopePrivate *private = gyroscope->private;
+
+  switch (property_id)
+    {
+    case PROP_YAW:
+      private->yaw = g_value_get_double (value);
+      break;
+    case PROP_PITCH:
+      private->pitch = g_value_get_double (value);
+      break;
+    case PROP_ROLL:
+      private->roll = g_value_get_double (value);
+      break;
+    case PROP_ZOOM:
+      private->zoom = g_value_get_double (value);
+      break;
+    case PROP_INVERT:
+      private->invert = g_value_get_boolean (value);
+      break;
+    case PROP_SPEED:
+      private->speed = g_value_get_double (value);
+      break;
+    case PROP_PIVOT_X:
+      private->pivot_x = g_value_get_double (value);
+      break;
+    case PROP_PIVOT_Y:
+      private->pivot_y = g_value_get_double (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gimp_tool_gyroscope_get_property (GObject    *object,
+                                  guint       property_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GimpToolGyroscope        *gyroscope = GIMP_TOOL_GYROSCOPE (object);
+  GimpToolGyroscopePrivate *private = gyroscope->private;
+
+  switch (property_id)
+    {
+    case PROP_YAW:
+      g_value_set_double (value, private->yaw);
+      break;
+    case PROP_PITCH:
+      g_value_set_double (value, private->pitch);
+      break;
+    case PROP_ROLL:
+      g_value_set_double (value, private->roll);
+      break;
+    case PROP_ZOOM:
+      g_value_set_double (value, private->zoom);
+      break;
+    case PROP_INVERT:
+      g_value_set_boolean (value, private->invert);
+      break;
+    case PROP_SPEED:
+      g_value_set_double (value, private->speed);
+      break;
+    case PROP_PIVOT_X:
+      g_value_set_double (value, private->pivot_x);
+      break;
+    case PROP_PIVOT_Y:
+      g_value_set_double (value, private->pivot_y);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
+
+static gint
+gimp_tool_gyroscope_button_press (GimpToolWidget      *widget,
+                                  const GimpCoords    *coords,
+                                  guint32              time,
+                                  GdkModifierType      state,
+                                  GimpButtonPressType  press_type)
+{
+  GimpToolGyroscope        *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
+  GimpToolGyroscopePrivate *private   = gyroscope->private;
+
+  gimp_tool_gyroscope_save (gyroscope);
+
+  if (state & GDK_MOD1_MASK)
+    {
+      private->mode = MODE_ZOOM;
+
+      private->last_zoom = private->zoom;
+    }
+  else if (state & gimp_get_extend_selection_mask ())
+    {
+      private->mode = MODE_ROTATE;
+
+      private->last_angle = atan2 (coords->y - private->pivot_y,
+                                   coords->x - private->pivot_x);
+      private->curr_angle = private->last_angle;
+    }
+  else
+    {
+      private->mode = MODE_PAN;
+
+      if (state & gimp_get_constrain_behavior_mask ())
+        private->constraint = CONSTRAINT_UNKNOWN;
+      else
+        private->constraint = CONSTRAINT_NONE;
+    }
+
+  private->last_x = coords->x;
+  private->last_y = coords->y;
+
+  gimp_tool_gyroscope_update_status (gyroscope, state);
+
+  return 1;
+}
+
+static void
+gimp_tool_gyroscope_button_release (GimpToolWidget        *widget,
+                                    const GimpCoords      *coords,
+                                    guint32                time,
+                                    GdkModifierType        state,
+                                    GimpButtonReleaseType  release_type)
+{
+  GimpToolGyroscope        *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
+  GimpToolGyroscopePrivate *private   = gyroscope->private;
+
+  if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
+    gimp_tool_gyroscope_restore (gyroscope);
+
+  private->mode = MODE_NONE;
+
+  gimp_tool_gyroscope_update_status (gyroscope, state);
+}
+
+static void
+gimp_tool_gyroscope_motion (GimpToolWidget   *widget,
+                            const GimpCoords *coords,
+                            guint32           time,
+                            GdkModifierType   state)
+{
+  GimpToolGyroscope        *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
+  GimpToolGyroscopePrivate *private   = gyroscope->private;
+  GimpDisplayShell         *shell     = gimp_tool_widget_get_shell (widget);
+  GimpVector3               axis      = {};
+
+  switch (private->mode)
+    {
+      case MODE_PAN:
+        {
+          gdouble x1     = private->last_x;
+          gdouble y1     = private->last_y;
+          gdouble x2     = coords->x;
+          gdouble y2     = coords->y;
+          gdouble factor = 1.0 / private->zoom;
+
+          if (private->constraint != CONSTRAINT_NONE)
+            {
+              gimp_display_shell_rotate_xy_f (shell, x1, y1, &x1, &y1);
+              gimp_display_shell_rotate_xy_f (shell, x2, y2, &x2, &y2);
+
+              if (private->constraint == CONSTRAINT_UNKNOWN)
+                {
+                  if (fabs (x2 - x1) > fabs (y2 - y1))
+                    private->constraint = CONSTRAINT_HORIZONTAL;
+                  else if (fabs (y2 - y1) > fabs (x2 - x1))
+                    private->constraint = CONSTRAINT_VERTICAL;
+                }
+
+              if (private->constraint == CONSTRAINT_HORIZONTAL)
+                y2 = y1;
+              else if (private->constraint == CONSTRAINT_VERTICAL)
+                x2 = x1;
+
+              gimp_display_shell_unrotate_xy_f (shell, x1, y1, &x1, &y1);
+              gimp_display_shell_unrotate_xy_f (shell, x2, y2, &x2, &y2);
+            }
+
+          if (private->invert)
+            factor = 1.0 / factor;
+
+          gimp_vector3_set (&axis, y2 - y1, x2 - x1, 0.0);
+          gimp_vector3_mul (&axis, factor * private->speed);
+        }
+      break;
+
+    case MODE_ROTATE:
+      {
+        gdouble angle;
+
+        angle = atan2 (coords->y - private->pivot_y,
+                       coords->x - private->pivot_x);
+
+        private->curr_angle = angle;
+
+        angle -= private->last_angle;
+
+        if (state & gimp_get_constrain_behavior_mask ())
+          angle = RINT (angle / (G_PI / 6.0)) * (G_PI / 6.0);
+
+        gimp_vector3_set (&axis, 0.0, 0.0, angle);
+
+        private->last_angle += angle;
+      }
+    break;
+
+    case MODE_ZOOM:
+      {
+        gdouble x1, y1;
+        gdouble x2, y2;
+        gdouble zoom;
+
+        gimp_display_shell_transform_xy_f (shell,
+                                           private->last_x, private->last_y,
+                                           &x1,             &y1);
+        gimp_display_shell_transform_xy_f (shell,
+                                           coords->x,       coords->y,
+                                           &x2,             &y2);
+
+        zoom = (y1 - y2) * shell->scale_y / 128.0;
+
+        private->last_zoom *= pow (2.0, zoom);
+
+        zoom = log (private->last_zoom / private->zoom) / G_LN2;
+
+        if (state & gimp_get_constrain_behavior_mask ())
+          zoom = RINT (zoom * 2.0) / 2.0;
+
+        g_object_set (gyroscope,
+                      "zoom", private->zoom * pow (2.0, zoom),
+                      NULL);
+      }
+      break;
+
+    case MODE_NONE:
+      g_return_if_reached ();
+  }
+
+  private->last_x = coords->x;
+  private->last_y = coords->y;
+
+  gimp_tool_gyroscope_rotate (gyroscope, &axis);
+}
+
+static void
+gimp_tool_gyroscope_hover (GimpToolWidget   *widget,
+                           const GimpCoords *coords,
+                           GdkModifierType   state,
+                           gboolean          proximity)
+{
+  gimp_tool_gyroscope_update_status (GIMP_TOOL_GYROSCOPE (widget), state);
+}
+
+static gboolean
+gimp_tool_gyroscope_key_press (GimpToolWidget *widget,
+                               GdkEventKey    *kevent)
+{
+  GimpToolGyroscope        *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
+  GimpToolGyroscopePrivate *private   = gyroscope->private;
+  GimpDisplayShell         *shell     = gimp_tool_widget_get_shell (widget);
+  GimpVector3               axis      = {};
+  gboolean                  fast;
+  gboolean                  result    = FALSE;
+
+  fast = (kevent->state & gimp_get_constrain_behavior_mask ());
+
+  if (kevent->state & GDK_MOD1_MASK)
+    {
+      /* zoom */
+      gdouble zoom = 0.0;
+
+      switch (kevent->keyval)
+        {
+        case GDK_KEY_Up:
+          zoom   = fast ? +1.0 : +1.0 / 8.0;
+          result = TRUE;
+          break;
+
+        case GDK_KEY_Down:
+          zoom   = fast ? -1.0 : -1.0 / 8.0;
+          result = TRUE;
+          break;
+        }
+
+      if (zoom)
+        {
+          g_object_set (gyroscope,
+                        "zoom", private->zoom * pow (2.0, zoom),
+                        NULL);
+        }
+    }
+  else if (kevent->state & gimp_get_extend_selection_mask ())
+    {
+      /* rotate */
+      gdouble angle = 0.0;
+
+      switch (kevent->keyval)
+        {
+        case GDK_KEY_Left:
+          angle  = fast ? +15.0 : +1.0;
+          result = TRUE;
+          break;
+
+        case GDK_KEY_Right:
+          angle  = fast ? -15.0 : -1.0;
+          result = TRUE;
+          break;
+        }
+
+      if (shell->flip_horizontally ^ shell->flip_vertically)
+        angle = -angle;
+
+      gimp_vector3_set (&axis, 0.0, 0.0, angle * DEG_TO_RAD);
+    }
+  else
+    {
+      /* pan */
+      gdouble x0 = 0.0;
+      gdouble y0 = 0.0;
+      gdouble x  = 0.0;
+      gdouble y  = 0.0;
+
+      switch (kevent->keyval)
+        {
+        case GDK_KEY_Left:
+          x      = fast ? +15.0 : +1.0;
+          result = TRUE;
+          break;
+
+        case GDK_KEY_Right:
+          x      = fast ? -15.0 : -1.0;
+          result = TRUE;
+          break;
+
+        case GDK_KEY_Up:
+          y      = fast ? +15.0 : +1.0;
+          result = TRUE;
+          break;
+
+        case GDK_KEY_Down:
+          y      = fast ? -15.0 : -1.0;
+          result = TRUE;
+          break;
+        }
+
+      gimp_display_shell_unrotate_xy_f (shell, x0, y0, &x0, &y0);
+      gimp_display_shell_unrotate_xy_f (shell, x,  y,  &x,  &y);
+
+      gimp_vector3_set (&axis,
+                        (y - y0) * DEG_TO_RAD,
+                        (x - x0) * DEG_TO_RAD,
+                        0.0);
+    }
+
+  gimp_tool_gyroscope_rotate (gyroscope, &axis);
+
+  return result;
+}
+
+static void
+gimp_tool_gyroscope_motion_modifier (GimpToolWidget  *widget,
+                                     GdkModifierType  key,
+                                     gboolean         press,
+                                     GdkModifierType  state)
+{
+  GimpToolGyroscope        *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
+  GimpToolGyroscopePrivate *private   = gyroscope->private;
+
+  gimp_tool_gyroscope_update_status (gyroscope, state);
+
+  if (key == gimp_get_constrain_behavior_mask ())
+    {
+      switch (private->mode)
+      {
+        case MODE_PAN:
+          if (state & gimp_get_constrain_behavior_mask ())
+            private->constraint = CONSTRAINT_UNKNOWN;
+          else
+            private->constraint = CONSTRAINT_NONE;
+          break;
+
+        case MODE_ROTATE:
+          if (! (state & gimp_get_constrain_behavior_mask ()))
+            private->last_angle = private->curr_angle;
+          break;
+
+        case MODE_ZOOM:
+          if (! (state & gimp_get_constrain_behavior_mask ()))
+            private->last_zoom = private->zoom;
+          break;
+
+        case MODE_NONE:
+          break;
+      }
+    }
+}
+
+static gboolean
+gimp_tool_gyroscope_get_cursor (GimpToolWidget     *widget,
+                                const GimpCoords   *coords,
+                                GdkModifierType     state,
+                                GimpCursorType     *cursor,
+                                GimpToolCursorType *tool_cursor,
+                                GimpCursorModifier *modifier)
+{
+  if (state & GDK_MOD1_MASK)
+    *modifier = GIMP_CURSOR_MODIFIER_ZOOM;
+  else if (state & gimp_get_extend_selection_mask ())
+    *modifier = GIMP_CURSOR_MODIFIER_ROTATE;
+  else
+    *modifier = GIMP_CURSOR_MODIFIER_MOVE;
+
+  return TRUE;
+}
+
+static void
+gimp_tool_gyroscope_update_status (GimpToolGyroscope *gyroscope,
+                                   GdkModifierType    state)
+{
+  GimpToolGyroscopePrivate *private = gyroscope->private;
+  gchar                    *status;
+
+  if (private->mode == MODE_ZOOM ||
+      (private->mode == MODE_NONE &&
+       state & GDK_MOD1_MASK))
+    {
+      status = gimp_suggest_modifiers (_("Click-Drag to zoom"),
+                                       gimp_get_toggle_behavior_mask () &
+                                       ~state,
+                                       NULL,
+                                       _("%s for constrained steps"),
+                                       NULL);
+    }
+  else if (private->mode == MODE_ROTATE ||
+           (private->mode == MODE_NONE &&
+            state & gimp_get_extend_selection_mask ()))
+    {
+      status = gimp_suggest_modifiers (_("Click-Drag to rotate"),
+                                       gimp_get_toggle_behavior_mask () &
+                                       ~state,
+                                       NULL,
+                                       _("%s for constrained angles"),
+                                       NULL);
+    }
+  else
+    {
+      status = gimp_suggest_modifiers (_("Click-Drag to pan"),
+                                       (((gimp_get_extend_selection_mask () |
+                                          GDK_MOD1_MASK)                    *
+                                         private->mode == MODE_NONE)        |
+                                        gimp_get_toggle_behavior_mask  ())  &
+                                        ~state,
+                                       _("%s to rotate"),
+                                       _("%s for a constrained axis"),
+                                       _("%s to zoom"));
+    }
+
+  gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (gyroscope), status);
+
+  g_free (status);
+}
+
+static void
+gimp_tool_gyroscope_save (GimpToolGyroscope *gyroscope)
+{
+  GimpToolGyroscopePrivate *private = gyroscope->private;
+
+  private->orig_yaw   = private->yaw;
+  private->orig_pitch = private->pitch;
+  private->orig_roll  = private->roll;
+  private->orig_zoom  = private->zoom;
+}
+
+static void
+gimp_tool_gyroscope_restore (GimpToolGyroscope *gyroscope)
+{
+  GimpToolGyroscopePrivate *private = gyroscope->private;
+
+  g_object_set (gyroscope,
+                "yaw",   private->yaw,
+                "pitch", private->pitch,
+                "roll",  private->roll,
+                "zoom",  private->zoom,
+                NULL);
+}
+
+static void
+gimp_tool_gyroscope_rotate (GimpToolGyroscope *gyroscope,
+                            const GimpVector3 *axis)
+{
+  GimpToolGyroscopePrivate *private = gyroscope->private;
+  GimpVector3               real_axis;
+  GimpVector3               basis[2];
+  gdouble                   yaw;
+  gdouble                   pitch;
+  gdouble                   roll;
+  gint                      i;
+
+  if (gimp_vector3_length (axis) < EPSILON)
+    return;
+
+  real_axis = *axis;
+
+  if (private->invert)
+    gimp_vector3_neg (&real_axis);
+
+  for (i = 0; i < 2; i++)
+    {
+      gimp_vector3_set (&basis[i], i == 0, i == 1, 0.0);
+
+      if (private->invert)
+        gimp_tool_gyroscope_rotate_vector (&basis[i], &real_axis);
+
+      gimp_tool_gyroscope_rotate_vector (
+        &basis[i], &(GimpVector3) {0.0, private->yaw * DEG_TO_RAD, 0.0});
+      gimp_tool_gyroscope_rotate_vector (
+        &basis[i], &(GimpVector3) {private->pitch * DEG_TO_RAD, 0.0, 0.0});
+      gimp_tool_gyroscope_rotate_vector (
+        &basis[i], &(GimpVector3) {0.0, 0.0, private->roll * DEG_TO_RAD});
+
+      if (! private->invert)
+        gimp_tool_gyroscope_rotate_vector (&basis[i], &real_axis);
+    }
+
+  roll = atan2 (basis[1].x, basis[1].y);
+
+  for (i = 0; i < 2; i++)
+    {
+      gimp_tool_gyroscope_rotate_vector (
+        &basis[i], &(GimpVector3) {0.0, 0.0, -roll});
+    }
+
+  pitch = atan2 (-basis[1].z, basis[1].y);
+
+  for (i = 0; i < 1; i++)
+    {
+      gimp_tool_gyroscope_rotate_vector (
+        &basis[i], &(GimpVector3) {-pitch, 0.0, 0.0});
+    }
+
+  yaw = atan2 (basis[0].z, basis[0].x);
+
+  g_object_set (gyroscope,
+                "yaw",   yaw   / DEG_TO_RAD,
+                "pitch", pitch / DEG_TO_RAD,
+                "roll",  roll  / DEG_TO_RAD,
+                NULL);
+}
+
+static void
+gimp_tool_gyroscope_rotate_vector (GimpVector3       *vector,
+                                   const GimpVector3 *axis)
+{
+  GimpVector3 normalized_axis;
+  GimpVector3 projection;
+  GimpVector3 u;
+  GimpVector3 v;
+  gdouble     angle;
+
+  angle = gimp_vector3_length (axis);
+
+  if (angle < EPSILON)
+    return;
+
+  normalized_axis = gimp_vector3_mul_val (*axis, 1.0 / angle);
+
+  projection = gimp_vector3_mul_val (
+    normalized_axis,
+    gimp_vector3_inner_product (vector, &normalized_axis));
+
+  u = gimp_vector3_sub_val (*vector, projection);
+  v = gimp_vector3_cross_product (&u, &normalized_axis);
+
+  gimp_vector3_mul (&u, cos (angle));
+  gimp_vector3_mul (&v, sin (angle));
+
+  gimp_vector3_add (vector, &u, &v);
+  gimp_vector3_add (vector, vector, &projection);
+}
+
+
+/*  public functions  */
+
+
+GimpToolWidget *
+gimp_tool_gyroscope_new (GimpDisplayShell *shell)
+{
+  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
+
+  return g_object_new (GIMP_TYPE_TOOL_GYROSCOPE,
+                       "shell", shell,
+                       NULL);
+}
diff --git a/app/display/gimptoolgyroscope.h b/app/display/gimptoolgyroscope.h
new file mode 100644
index 0000000..da00620
--- /dev/null
+++ b/app/display/gimptoolgyroscope.h
@@ -0,0 +1,58 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimptoolgyroscope.h
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_TOOL_GYROSCOPE_H__
+#define __GIMP_TOOL_GYROSCOPE_H__
+
+
+#include "gimptoolwidget.h"
+
+
+#define GIMP_TYPE_TOOL_GYROSCOPE            (gimp_tool_gyroscope_get_type ())
+#define GIMP_TOOL_GYROSCOPE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_TOOL_GYROSCOPE, 
GimpToolGyroscope))
+#define GIMP_TOOL_GYROSCOPE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_TOOL_GYROSCOPE, 
GimpToolGyroscopeClass))
+#define GIMP_IS_TOOL_GYROSCOPE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_TOOL_GYROSCOPE))
+#define GIMP_IS_TOOL_GYROSCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_TOOL_GYROSCOPE))
+#define GIMP_TOOL_GYROSCOPE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_TOOL_GYROSCOPE, 
GimpToolGyroscopeClass))
+
+
+typedef struct _GimpToolGyroscope        GimpToolGyroscope;
+typedef struct _GimpToolGyroscopePrivate GimpToolGyroscopePrivate;
+typedef struct _GimpToolGyroscopeClass   GimpToolGyroscopeClass;
+
+struct _GimpToolGyroscope
+{
+  GimpToolWidget            parent_instance;
+
+  GimpToolGyroscopePrivate *private;
+};
+
+struct _GimpToolGyroscopeClass
+{
+  GimpToolWidgetClass  parent_class;
+};
+
+
+GType            gimp_tool_gyroscope_get_type (void) G_GNUC_CONST;
+
+GimpToolWidget * gimp_tool_gyroscope_new      (GimpDisplayShell *shell);
+
+
+#endif /* __GIMP_TOOL_GYROSCOPE_H__ */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 959aca8..a538526 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -260,6 +260,7 @@ app/display/gimpdisplayshell-utils.c
 app/display/gimpnavigationeditor.c
 app/display/gimpstatusbar.c
 app/display/gimptoolcompass.c
+app/display/gimptoolgyroscope.c
 app/display/gimptoolhandlegrid.c
 app/display/gimptoolline.c
 app/display/gimptoolpath.c


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