885 lines
29 KiB
C
885 lines
29 KiB
C
/* PIKA - Photo and Image Kooker Application
|
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
|
*
|
|
* Original copyright, applying to most contents (license remains unchanged):
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* pikatoolgyroscope.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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
#include "libpikabase/pikabase.h"
|
|
#include "libpikamath/pikamath.h"
|
|
|
|
#include "display-types.h"
|
|
|
|
#include "widgets/pikawidgets-utils.h"
|
|
|
|
#include "pikadisplayshell.h"
|
|
#include "pikadisplayshell-transform.h"
|
|
#include "pikatoolgyroscope.h"
|
|
|
|
#include "pika-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 _PikaToolGyroscopePrivate
|
|
{
|
|
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 pika_tool_gyroscope_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void pika_tool_gyroscope_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static gint pika_tool_gyroscope_button_press (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaButtonPressType press_type);
|
|
static void pika_tool_gyroscope_button_release (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaButtonReleaseType release_type);
|
|
static void pika_tool_gyroscope_motion (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state);
|
|
static PikaHit pika_tool_gyroscope_hit (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
GdkModifierType state,
|
|
gboolean proximity);
|
|
static void pika_tool_gyroscope_hover (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
GdkModifierType state,
|
|
gboolean proximity);
|
|
static gboolean pika_tool_gyroscope_key_press (PikaToolWidget *widget,
|
|
GdkEventKey *kevent);
|
|
static void pika_tool_gyroscope_motion_modifier (PikaToolWidget *widget,
|
|
GdkModifierType key,
|
|
gboolean press,
|
|
GdkModifierType state);
|
|
static gboolean pika_tool_gyroscope_get_cursor (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
GdkModifierType state,
|
|
PikaCursorType *cursor,
|
|
PikaToolCursorType *tool_cursor,
|
|
PikaCursorModifier *modifier);
|
|
|
|
static void pika_tool_gyroscope_update_status (PikaToolGyroscope *gyroscope,
|
|
GdkModifierType state);
|
|
|
|
static void pika_tool_gyroscope_save (PikaToolGyroscope *gyroscope);
|
|
static void pika_tool_gyroscope_restore (PikaToolGyroscope *gyroscope);
|
|
|
|
static void pika_tool_gyroscope_rotate (PikaToolGyroscope *gyroscope,
|
|
const PikaVector3 *axis);
|
|
static void pika_tool_gyroscope_rotate_vector (PikaVector3 *vector,
|
|
const PikaVector3 *axis);
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (PikaToolGyroscope, pika_tool_gyroscope,
|
|
PIKA_TYPE_TOOL_WIDGET)
|
|
|
|
#define parent_class pika_tool_gyroscope_parent_class
|
|
|
|
|
|
static void
|
|
pika_tool_gyroscope_class_init (PikaToolGyroscopeClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
PikaToolWidgetClass *widget_class = PIKA_TOOL_WIDGET_CLASS (klass);
|
|
|
|
object_class->set_property = pika_tool_gyroscope_set_property;
|
|
object_class->get_property = pika_tool_gyroscope_get_property;
|
|
|
|
widget_class->button_press = pika_tool_gyroscope_button_press;
|
|
widget_class->button_release = pika_tool_gyroscope_button_release;
|
|
widget_class->motion = pika_tool_gyroscope_motion;
|
|
widget_class->hit = pika_tool_gyroscope_hit;
|
|
widget_class->hover = pika_tool_gyroscope_hover;
|
|
widget_class->key_press = pika_tool_gyroscope_key_press;
|
|
widget_class->motion_modifier = pika_tool_gyroscope_motion_modifier;
|
|
widget_class->get_cursor = pika_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,
|
|
PIKA_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,
|
|
PIKA_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,
|
|
PIKA_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,
|
|
PIKA_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property (object_class, PROP_INVERT,
|
|
g_param_spec_boolean ("invert", NULL, NULL,
|
|
FALSE,
|
|
PIKA_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,
|
|
PIKA_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,
|
|
PIKA_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,
|
|
PIKA_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_init (PikaToolGyroscope *gyroscope)
|
|
{
|
|
gyroscope->private = pika_tool_gyroscope_get_instance_private (gyroscope);
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
PikaToolGyroscope *gyroscope = PIKA_TOOL_GYROSCOPE (object);
|
|
PikaToolGyroscopePrivate *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
|
|
pika_tool_gyroscope_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
PikaToolGyroscope *gyroscope = PIKA_TOOL_GYROSCOPE (object);
|
|
PikaToolGyroscopePrivate *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
|
|
pika_tool_gyroscope_button_press (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaButtonPressType press_type)
|
|
{
|
|
PikaToolGyroscope *gyroscope = PIKA_TOOL_GYROSCOPE (widget);
|
|
PikaToolGyroscopePrivate *private = gyroscope->private;
|
|
|
|
pika_tool_gyroscope_save (gyroscope);
|
|
|
|
if (state & GDK_MOD1_MASK)
|
|
{
|
|
private->mode = MODE_ZOOM;
|
|
|
|
private->last_zoom = private->zoom;
|
|
}
|
|
else if (state & pika_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 & pika_get_constrain_behavior_mask ())
|
|
private->constraint = CONSTRAINT_UNKNOWN;
|
|
else
|
|
private->constraint = CONSTRAINT_NONE;
|
|
}
|
|
|
|
private->last_x = coords->x;
|
|
private->last_y = coords->y;
|
|
|
|
pika_tool_gyroscope_update_status (gyroscope, state);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_button_release (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state,
|
|
PikaButtonReleaseType release_type)
|
|
{
|
|
PikaToolGyroscope *gyroscope = PIKA_TOOL_GYROSCOPE (widget);
|
|
PikaToolGyroscopePrivate *private = gyroscope->private;
|
|
|
|
if (release_type == PIKA_BUTTON_RELEASE_CANCEL)
|
|
pika_tool_gyroscope_restore (gyroscope);
|
|
|
|
private->mode = MODE_NONE;
|
|
|
|
pika_tool_gyroscope_update_status (gyroscope, state);
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_motion (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
guint32 time,
|
|
GdkModifierType state)
|
|
{
|
|
PikaToolGyroscope *gyroscope = PIKA_TOOL_GYROSCOPE (widget);
|
|
PikaToolGyroscopePrivate *private = gyroscope->private;
|
|
PikaDisplayShell *shell = pika_tool_widget_get_shell (widget);
|
|
PikaVector3 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)
|
|
{
|
|
pika_display_shell_rotate_xy_f (shell, x1, y1, &x1, &y1);
|
|
pika_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;
|
|
|
|
pika_display_shell_unrotate_xy_f (shell, x1, y1, &x1, &y1);
|
|
pika_display_shell_unrotate_xy_f (shell, x2, y2, &x2, &y2);
|
|
}
|
|
|
|
if (private->invert)
|
|
factor = 1.0 / factor;
|
|
|
|
pika_vector3_set (&axis, y2 - y1, x2 - x1, 0.0);
|
|
pika_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 & pika_get_constrain_behavior_mask ())
|
|
angle = RINT (angle / (G_PI / 6.0)) * (G_PI / 6.0);
|
|
|
|
pika_vector3_set (&axis, 0.0, 0.0, angle);
|
|
|
|
private->last_angle += angle;
|
|
}
|
|
break;
|
|
|
|
case MODE_ZOOM:
|
|
{
|
|
gdouble x1, y1;
|
|
gdouble x2, y2;
|
|
gdouble zoom;
|
|
|
|
pika_display_shell_transform_xy_f (shell,
|
|
private->last_x, private->last_y,
|
|
&x1, &y1);
|
|
pika_display_shell_transform_xy_f (shell,
|
|
coords->x, coords->y,
|
|
&x2, &y2);
|
|
|
|
zoom = (y1 - y2) * shell->scale_y / 128.0;
|
|
|
|
if (private->invert)
|
|
zoom = -zoom;
|
|
|
|
private->last_zoom *= pow (2.0, zoom);
|
|
|
|
zoom = log (private->last_zoom / private->zoom) / G_LN2;
|
|
|
|
if (state & pika_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;
|
|
|
|
pika_tool_gyroscope_rotate (gyroscope, &axis);
|
|
}
|
|
|
|
static PikaHit
|
|
pika_tool_gyroscope_hit (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
GdkModifierType state,
|
|
gboolean proximity)
|
|
{
|
|
return PIKA_HIT_INDIRECT;
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_hover (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
GdkModifierType state,
|
|
gboolean proximity)
|
|
{
|
|
pika_tool_gyroscope_update_status (PIKA_TOOL_GYROSCOPE (widget), state);
|
|
}
|
|
|
|
static gboolean
|
|
pika_tool_gyroscope_key_press (PikaToolWidget *widget,
|
|
GdkEventKey *kevent)
|
|
{
|
|
PikaToolGyroscope *gyroscope = PIKA_TOOL_GYROSCOPE (widget);
|
|
PikaToolGyroscopePrivate *private = gyroscope->private;
|
|
PikaDisplayShell *shell = pika_tool_widget_get_shell (widget);
|
|
PikaVector3 axis = {};
|
|
gboolean fast;
|
|
gboolean result = FALSE;
|
|
|
|
fast = (kevent->state & pika_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 (private->invert)
|
|
zoom = -zoom;
|
|
|
|
if (zoom)
|
|
{
|
|
g_object_set (gyroscope,
|
|
"zoom", private->zoom * pow (2.0, zoom),
|
|
NULL);
|
|
}
|
|
}
|
|
else if (kevent->state & pika_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;
|
|
|
|
pika_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;
|
|
gdouble factor = 1.0 / private->zoom;
|
|
|
|
if (private->invert)
|
|
factor = 1.0 / factor;
|
|
|
|
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;
|
|
}
|
|
|
|
pika_display_shell_unrotate_xy_f (shell, x0, y0, &x0, &y0);
|
|
pika_display_shell_unrotate_xy_f (shell, x, y, &x, &y);
|
|
|
|
pika_vector3_set (&axis,
|
|
(y - y0) * DEG_TO_RAD,
|
|
(x - x0) * DEG_TO_RAD,
|
|
0.0);
|
|
pika_vector3_mul (&axis, factor);
|
|
}
|
|
|
|
pika_tool_gyroscope_rotate (gyroscope, &axis);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_motion_modifier (PikaToolWidget *widget,
|
|
GdkModifierType key,
|
|
gboolean press,
|
|
GdkModifierType state)
|
|
{
|
|
PikaToolGyroscope *gyroscope = PIKA_TOOL_GYROSCOPE (widget);
|
|
PikaToolGyroscopePrivate *private = gyroscope->private;
|
|
|
|
pika_tool_gyroscope_update_status (gyroscope, state);
|
|
|
|
if (key == pika_get_constrain_behavior_mask ())
|
|
{
|
|
switch (private->mode)
|
|
{
|
|
case MODE_PAN:
|
|
if (state & pika_get_constrain_behavior_mask ())
|
|
private->constraint = CONSTRAINT_UNKNOWN;
|
|
else
|
|
private->constraint = CONSTRAINT_NONE;
|
|
break;
|
|
|
|
case MODE_ROTATE:
|
|
if (! (state & pika_get_constrain_behavior_mask ()))
|
|
private->last_angle = private->curr_angle;
|
|
break;
|
|
|
|
case MODE_ZOOM:
|
|
if (! (state & pika_get_constrain_behavior_mask ()))
|
|
private->last_zoom = private->zoom;
|
|
break;
|
|
|
|
case MODE_NONE:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
pika_tool_gyroscope_get_cursor (PikaToolWidget *widget,
|
|
const PikaCoords *coords,
|
|
GdkModifierType state,
|
|
PikaCursorType *cursor,
|
|
PikaToolCursorType *tool_cursor,
|
|
PikaCursorModifier *modifier)
|
|
{
|
|
if (state & GDK_MOD1_MASK)
|
|
*modifier = PIKA_CURSOR_MODIFIER_ZOOM;
|
|
else if (state & pika_get_extend_selection_mask ())
|
|
*modifier = PIKA_CURSOR_MODIFIER_ROTATE;
|
|
else
|
|
*modifier = PIKA_CURSOR_MODIFIER_MOVE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_update_status (PikaToolGyroscope *gyroscope,
|
|
GdkModifierType state)
|
|
{
|
|
PikaToolGyroscopePrivate *private = gyroscope->private;
|
|
gchar *status;
|
|
|
|
if (private->mode == MODE_ZOOM ||
|
|
(private->mode == MODE_NONE &&
|
|
state & GDK_MOD1_MASK))
|
|
{
|
|
status = pika_suggest_modifiers (_("Click-Drag to zoom"),
|
|
pika_get_toggle_behavior_mask () &
|
|
~state,
|
|
NULL,
|
|
_("%s for constrained steps"),
|
|
NULL);
|
|
}
|
|
else if (private->mode == MODE_ROTATE ||
|
|
(private->mode == MODE_NONE &&
|
|
state & pika_get_extend_selection_mask ()))
|
|
{
|
|
status = pika_suggest_modifiers (_("Click-Drag to rotate"),
|
|
pika_get_toggle_behavior_mask () &
|
|
~state,
|
|
NULL,
|
|
_("%s for constrained angles"),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
status = pika_suggest_modifiers (_("Click-Drag to pan"),
|
|
(((pika_get_extend_selection_mask () |
|
|
GDK_MOD1_MASK) *
|
|
(private->mode == MODE_NONE)) |
|
|
pika_get_toggle_behavior_mask ()) &
|
|
~state,
|
|
_("%s to rotate"),
|
|
_("%s for a constrained axis"),
|
|
_("%s to zoom"));
|
|
}
|
|
|
|
pika_tool_widget_set_status (PIKA_TOOL_WIDGET (gyroscope), status);
|
|
|
|
g_free (status);
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_save (PikaToolGyroscope *gyroscope)
|
|
{
|
|
PikaToolGyroscopePrivate *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
|
|
pika_tool_gyroscope_restore (PikaToolGyroscope *gyroscope)
|
|
{
|
|
PikaToolGyroscopePrivate *private = gyroscope->private;
|
|
|
|
g_object_set (gyroscope,
|
|
"yaw", private->orig_yaw,
|
|
"pitch", private->orig_pitch,
|
|
"roll", private->orig_roll,
|
|
"zoom", private->orig_zoom,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
pika_tool_gyroscope_rotate (PikaToolGyroscope *gyroscope,
|
|
const PikaVector3 *axis)
|
|
{
|
|
PikaToolGyroscopePrivate *private = gyroscope->private;
|
|
PikaVector3 real_axis;
|
|
PikaVector3 basis[2];
|
|
gdouble yaw;
|
|
gdouble pitch;
|
|
gdouble roll;
|
|
gint i;
|
|
|
|
if (pika_vector3_length (axis) < EPSILON)
|
|
return;
|
|
|
|
real_axis = *axis;
|
|
|
|
if (private->invert)
|
|
pika_vector3_neg (&real_axis);
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
pika_vector3_set (&basis[i], i == 0, i == 1, 0.0);
|
|
|
|
if (private->invert)
|
|
pika_tool_gyroscope_rotate_vector (&basis[i], &real_axis);
|
|
|
|
pika_tool_gyroscope_rotate_vector (
|
|
&basis[i], &(PikaVector3) {0.0, private->yaw * DEG_TO_RAD, 0.0});
|
|
pika_tool_gyroscope_rotate_vector (
|
|
&basis[i], &(PikaVector3) {private->pitch * DEG_TO_RAD, 0.0, 0.0});
|
|
pika_tool_gyroscope_rotate_vector (
|
|
&basis[i], &(PikaVector3) {0.0, 0.0, private->roll * DEG_TO_RAD});
|
|
|
|
if (! private->invert)
|
|
pika_tool_gyroscope_rotate_vector (&basis[i], &real_axis);
|
|
}
|
|
|
|
roll = atan2 (basis[1].x, basis[1].y);
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
pika_tool_gyroscope_rotate_vector (
|
|
&basis[i], &(PikaVector3) {0.0, 0.0, -roll});
|
|
}
|
|
|
|
pitch = atan2 (-basis[1].z, basis[1].y);
|
|
|
|
for (i = 0; i < 1; i++)
|
|
{
|
|
pika_tool_gyroscope_rotate_vector (
|
|
&basis[i], &(PikaVector3) {-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
|
|
pika_tool_gyroscope_rotate_vector (PikaVector3 *vector,
|
|
const PikaVector3 *axis)
|
|
{
|
|
PikaVector3 normalized_axis;
|
|
PikaVector3 projection;
|
|
PikaVector3 u;
|
|
PikaVector3 v;
|
|
gdouble angle;
|
|
|
|
angle = pika_vector3_length (axis);
|
|
|
|
if (angle < EPSILON)
|
|
return;
|
|
|
|
normalized_axis = pika_vector3_mul_val (*axis, 1.0 / angle);
|
|
|
|
projection = pika_vector3_mul_val (
|
|
normalized_axis,
|
|
pika_vector3_inner_product (vector, &normalized_axis));
|
|
|
|
u = pika_vector3_sub_val (*vector, projection);
|
|
v = pika_vector3_cross_product (&u, &normalized_axis);
|
|
|
|
pika_vector3_mul (&u, cos (angle));
|
|
pika_vector3_mul (&v, sin (angle));
|
|
|
|
pika_vector3_add (vector, &u, &v);
|
|
pika_vector3_add (vector, vector, &projection);
|
|
}
|
|
|
|
|
|
/* public functions */
|
|
|
|
|
|
PikaToolWidget *
|
|
pika_tool_gyroscope_new (PikaDisplayShell *shell)
|
|
{
|
|
g_return_val_if_fail (PIKA_IS_DISPLAY_SHELL (shell), NULL);
|
|
|
|
return g_object_new (PIKA_TYPE_TOOL_GYROSCOPE,
|
|
"shell", shell,
|
|
NULL);
|
|
}
|