diff options
Diffstat (limited to 'app/display/gimptoolgyroscope.c')
-rw-r--r-- | app/display/gimptoolgyroscope.c | 879 |
1 files changed, 879 insertions, 0 deletions
diff --git a/app/display/gimptoolgyroscope.c b/app/display/gimptoolgyroscope.c new file mode 100644 index 0000000..4d6b175 --- /dev/null +++ b/app/display/gimptoolgyroscope.c @@ -0,0 +1,879 @@ +/* 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 <https://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 GimpHit gimp_tool_gyroscope_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity); +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_WITH_PRIVATE (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->hit = gimp_tool_gyroscope_hit; + 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)); +} + +static void +gimp_tool_gyroscope_init (GimpToolGyroscope *gyroscope) +{ + gyroscope->private = gimp_tool_gyroscope_get_instance_private (gyroscope); +} + +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; + + if (private->invert) + zoom = -zoom; + + 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 GimpHit +gimp_tool_gyroscope_hit (GimpToolWidget *widget, + const GimpCoords *coords, + GdkModifierType state, + gboolean proximity) +{ + return GIMP_HIT_INDIRECT; +} + +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 (private->invert) + zoom = -zoom; + + 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; + 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; + } + + 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_vector3_mul (&axis, factor); + } + + 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->orig_yaw, + "pitch", private->orig_pitch, + "roll", private->orig_roll, + "zoom", private->orig_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); +} |