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.
This commit is contained in:
857
app/display/gimptoolgyroscope.c
Normal file
857
app/display/gimptoolgyroscope.c
Normal file
@ -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);
|
||||
}
|
Reference in New Issue
Block a user