1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimptoolgyroscope.c
5 * Copyright (C) 2018 Ell
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <gegl.h>
24 #include <gtk/gtk.h>
25 #include <gdk/gdkkeysyms.h>
26
27 #include "libgimpmath/gimpmath.h"
28
29 #include "display-types.h"
30
31 #include "widgets/gimpwidgets-utils.h"
32
33 #include "gimpdisplayshell.h"
34 #include "gimpdisplayshell-transform.h"
35 #include "gimptoolgyroscope.h"
36
37 #include "gimp-intl.h"
38
39
40 #define EPSILON 1e-6
41 #define DEG_TO_RAD (G_PI / 180.0)
42
43
44 typedef enum
45 {
46 MODE_NONE,
47 MODE_PAN,
48 MODE_ROTATE,
49 MODE_ZOOM
50 } Mode;
51
52 typedef enum
53 {
54 CONSTRAINT_NONE,
55 CONSTRAINT_UNKNOWN,
56 CONSTRAINT_HORIZONTAL,
57 CONSTRAINT_VERTICAL
58 } Constraint;
59
60 enum
61 {
62 PROP_0,
63 PROP_YAW,
64 PROP_PITCH,
65 PROP_ROLL,
66 PROP_ZOOM,
67 PROP_INVERT,
68 PROP_SPEED,
69 PROP_PIVOT_X,
70 PROP_PIVOT_Y
71 };
72
73 struct _GimpToolGyroscopePrivate
74 {
75 gdouble yaw;
76 gdouble pitch;
77 gdouble roll;
78 gdouble zoom;
79
80 gdouble orig_yaw;
81 gdouble orig_pitch;
82 gdouble orig_roll;
83 gdouble orig_zoom;
84
85 gboolean invert;
86
87 gdouble speed;
88
89 gdouble pivot_x;
90 gdouble pivot_y;
91
92 Mode mode;
93 Constraint constraint;
94
95 gdouble last_x;
96 gdouble last_y;
97
98 gdouble last_angle;
99 gdouble curr_angle;
100
101 gdouble last_zoom;
102 };
103
104
105 /* local function prototypes */
106
107 static void gimp_tool_gyroscope_set_property (GObject *object,
108 guint property_id,
109 const GValue *value,
110 GParamSpec *pspec);
111 static void gimp_tool_gyroscope_get_property (GObject *object,
112 guint property_id,
113 GValue *value,
114 GParamSpec *pspec);
115
116 static gint gimp_tool_gyroscope_button_press (GimpToolWidget *widget,
117 const GimpCoords *coords,
118 guint32 time,
119 GdkModifierType state,
120 GimpButtonPressType press_type);
121 static void gimp_tool_gyroscope_button_release (GimpToolWidget *widget,
122 const GimpCoords *coords,
123 guint32 time,
124 GdkModifierType state,
125 GimpButtonReleaseType release_type);
126 static void gimp_tool_gyroscope_motion (GimpToolWidget *widget,
127 const GimpCoords *coords,
128 guint32 time,
129 GdkModifierType state);
130 static GimpHit gimp_tool_gyroscope_hit (GimpToolWidget *widget,
131 const GimpCoords *coords,
132 GdkModifierType state,
133 gboolean proximity);
134 static void gimp_tool_gyroscope_hover (GimpToolWidget *widget,
135 const GimpCoords *coords,
136 GdkModifierType state,
137 gboolean proximity);
138 static gboolean gimp_tool_gyroscope_key_press (GimpToolWidget *widget,
139 GdkEventKey *kevent);
140 static void gimp_tool_gyroscope_motion_modifier (GimpToolWidget *widget,
141 GdkModifierType key,
142 gboolean press,
143 GdkModifierType state);
144 static gboolean gimp_tool_gyroscope_get_cursor (GimpToolWidget *widget,
145 const GimpCoords *coords,
146 GdkModifierType state,
147 GimpCursorType *cursor,
148 GimpToolCursorType *tool_cursor,
149 GimpCursorModifier *modifier);
150
151 static void gimp_tool_gyroscope_update_status (GimpToolGyroscope *gyroscope,
152 GdkModifierType state);
153
154 static void gimp_tool_gyroscope_save (GimpToolGyroscope *gyroscope);
155 static void gimp_tool_gyroscope_restore (GimpToolGyroscope *gyroscope);
156
157 static void gimp_tool_gyroscope_rotate (GimpToolGyroscope *gyroscope,
158 const GimpVector3 *axis);
159 static void gimp_tool_gyroscope_rotate_vector (GimpVector3 *vector,
160 const GimpVector3 *axis);
161
162
G_DEFINE_TYPE_WITH_PRIVATE(GimpToolGyroscope,gimp_tool_gyroscope,GIMP_TYPE_TOOL_WIDGET)163 G_DEFINE_TYPE_WITH_PRIVATE (GimpToolGyroscope, gimp_tool_gyroscope,
164 GIMP_TYPE_TOOL_WIDGET)
165
166 #define parent_class gimp_tool_gyroscope_parent_class
167
168
169 static void
170 gimp_tool_gyroscope_class_init (GimpToolGyroscopeClass *klass)
171 {
172 GObjectClass *object_class = G_OBJECT_CLASS (klass);
173 GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);
174
175 object_class->set_property = gimp_tool_gyroscope_set_property;
176 object_class->get_property = gimp_tool_gyroscope_get_property;
177
178 widget_class->button_press = gimp_tool_gyroscope_button_press;
179 widget_class->button_release = gimp_tool_gyroscope_button_release;
180 widget_class->motion = gimp_tool_gyroscope_motion;
181 widget_class->hit = gimp_tool_gyroscope_hit;
182 widget_class->hover = gimp_tool_gyroscope_hover;
183 widget_class->key_press = gimp_tool_gyroscope_key_press;
184 widget_class->motion_modifier = gimp_tool_gyroscope_motion_modifier;
185 widget_class->get_cursor = gimp_tool_gyroscope_get_cursor;
186
187 g_object_class_install_property (object_class, PROP_YAW,
188 g_param_spec_double ("yaw", NULL, NULL,
189 -G_MAXDOUBLE,
190 +G_MAXDOUBLE,
191 0.0,
192 GIMP_PARAM_READWRITE |
193 G_PARAM_CONSTRUCT));
194
195 g_object_class_install_property (object_class, PROP_PITCH,
196 g_param_spec_double ("pitch", NULL, NULL,
197 -G_MAXDOUBLE,
198 +G_MAXDOUBLE,
199 0.0,
200 GIMP_PARAM_READWRITE |
201 G_PARAM_CONSTRUCT));
202
203 g_object_class_install_property (object_class, PROP_ROLL,
204 g_param_spec_double ("roll", NULL, NULL,
205 -G_MAXDOUBLE,
206 +G_MAXDOUBLE,
207 0.0,
208 GIMP_PARAM_READWRITE |
209 G_PARAM_CONSTRUCT));
210
211 g_object_class_install_property (object_class, PROP_ZOOM,
212 g_param_spec_double ("zoom", NULL, NULL,
213 0.0,
214 +G_MAXDOUBLE,
215 1.0,
216 GIMP_PARAM_READWRITE |
217 G_PARAM_CONSTRUCT));
218
219 g_object_class_install_property (object_class, PROP_INVERT,
220 g_param_spec_boolean ("invert", NULL, NULL,
221 FALSE,
222 GIMP_PARAM_READWRITE |
223 G_PARAM_CONSTRUCT));
224
225 g_object_class_install_property (object_class, PROP_SPEED,
226 g_param_spec_double ("speed", NULL, NULL,
227 -G_MAXDOUBLE,
228 +G_MAXDOUBLE,
229 1.0,
230 GIMP_PARAM_READWRITE |
231 G_PARAM_CONSTRUCT));
232
233 g_object_class_install_property (object_class, PROP_PIVOT_X,
234 g_param_spec_double ("pivot-x", NULL, NULL,
235 -G_MAXDOUBLE,
236 +G_MAXDOUBLE,
237 0.0,
238 GIMP_PARAM_READWRITE |
239 G_PARAM_CONSTRUCT));
240
241 g_object_class_install_property (object_class, PROP_PIVOT_Y,
242 g_param_spec_double ("pivot-y", NULL, NULL,
243 -G_MAXDOUBLE,
244 +G_MAXDOUBLE,
245 0.0,
246 GIMP_PARAM_READWRITE |
247 G_PARAM_CONSTRUCT));
248 }
249
250 static void
gimp_tool_gyroscope_init(GimpToolGyroscope * gyroscope)251 gimp_tool_gyroscope_init (GimpToolGyroscope *gyroscope)
252 {
253 gyroscope->private = gimp_tool_gyroscope_get_instance_private (gyroscope);
254 }
255
256 static void
gimp_tool_gyroscope_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)257 gimp_tool_gyroscope_set_property (GObject *object,
258 guint property_id,
259 const GValue *value,
260 GParamSpec *pspec)
261 {
262 GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (object);
263 GimpToolGyroscopePrivate *private = gyroscope->private;
264
265 switch (property_id)
266 {
267 case PROP_YAW:
268 private->yaw = g_value_get_double (value);
269 break;
270 case PROP_PITCH:
271 private->pitch = g_value_get_double (value);
272 break;
273 case PROP_ROLL:
274 private->roll = g_value_get_double (value);
275 break;
276 case PROP_ZOOM:
277 private->zoom = g_value_get_double (value);
278 break;
279 case PROP_INVERT:
280 private->invert = g_value_get_boolean (value);
281 break;
282 case PROP_SPEED:
283 private->speed = g_value_get_double (value);
284 break;
285 case PROP_PIVOT_X:
286 private->pivot_x = g_value_get_double (value);
287 break;
288 case PROP_PIVOT_Y:
289 private->pivot_y = g_value_get_double (value);
290 break;
291
292 default:
293 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
294 break;
295 }
296 }
297
298 static void
gimp_tool_gyroscope_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)299 gimp_tool_gyroscope_get_property (GObject *object,
300 guint property_id,
301 GValue *value,
302 GParamSpec *pspec)
303 {
304 GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (object);
305 GimpToolGyroscopePrivate *private = gyroscope->private;
306
307 switch (property_id)
308 {
309 case PROP_YAW:
310 g_value_set_double (value, private->yaw);
311 break;
312 case PROP_PITCH:
313 g_value_set_double (value, private->pitch);
314 break;
315 case PROP_ROLL:
316 g_value_set_double (value, private->roll);
317 break;
318 case PROP_ZOOM:
319 g_value_set_double (value, private->zoom);
320 break;
321 case PROP_INVERT:
322 g_value_set_boolean (value, private->invert);
323 break;
324 case PROP_SPEED:
325 g_value_set_double (value, private->speed);
326 break;
327 case PROP_PIVOT_X:
328 g_value_set_double (value, private->pivot_x);
329 break;
330 case PROP_PIVOT_Y:
331 g_value_set_double (value, private->pivot_y);
332 break;
333
334 default:
335 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
336 break;
337 }
338 }
339
340 static gint
gimp_tool_gyroscope_button_press(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type)341 gimp_tool_gyroscope_button_press (GimpToolWidget *widget,
342 const GimpCoords *coords,
343 guint32 time,
344 GdkModifierType state,
345 GimpButtonPressType press_type)
346 {
347 GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
348 GimpToolGyroscopePrivate *private = gyroscope->private;
349
350 gimp_tool_gyroscope_save (gyroscope);
351
352 if (state & GDK_MOD1_MASK)
353 {
354 private->mode = MODE_ZOOM;
355
356 private->last_zoom = private->zoom;
357 }
358 else if (state & gimp_get_extend_selection_mask ())
359 {
360 private->mode = MODE_ROTATE;
361
362 private->last_angle = atan2 (coords->y - private->pivot_y,
363 coords->x - private->pivot_x);
364 private->curr_angle = private->last_angle;
365 }
366 else
367 {
368 private->mode = MODE_PAN;
369
370 if (state & gimp_get_constrain_behavior_mask ())
371 private->constraint = CONSTRAINT_UNKNOWN;
372 else
373 private->constraint = CONSTRAINT_NONE;
374 }
375
376 private->last_x = coords->x;
377 private->last_y = coords->y;
378
379 gimp_tool_gyroscope_update_status (gyroscope, state);
380
381 return 1;
382 }
383
384 static void
gimp_tool_gyroscope_button_release(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type)385 gimp_tool_gyroscope_button_release (GimpToolWidget *widget,
386 const GimpCoords *coords,
387 guint32 time,
388 GdkModifierType state,
389 GimpButtonReleaseType release_type)
390 {
391 GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
392 GimpToolGyroscopePrivate *private = gyroscope->private;
393
394 if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
395 gimp_tool_gyroscope_restore (gyroscope);
396
397 private->mode = MODE_NONE;
398
399 gimp_tool_gyroscope_update_status (gyroscope, state);
400 }
401
402 static void
gimp_tool_gyroscope_motion(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state)403 gimp_tool_gyroscope_motion (GimpToolWidget *widget,
404 const GimpCoords *coords,
405 guint32 time,
406 GdkModifierType state)
407 {
408 GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
409 GimpToolGyroscopePrivate *private = gyroscope->private;
410 GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget);
411 GimpVector3 axis = {};
412
413 switch (private->mode)
414 {
415 case MODE_PAN:
416 {
417 gdouble x1 = private->last_x;
418 gdouble y1 = private->last_y;
419 gdouble x2 = coords->x;
420 gdouble y2 = coords->y;
421 gdouble factor = 1.0 / private->zoom;
422
423 if (private->constraint != CONSTRAINT_NONE)
424 {
425 gimp_display_shell_rotate_xy_f (shell, x1, y1, &x1, &y1);
426 gimp_display_shell_rotate_xy_f (shell, x2, y2, &x2, &y2);
427
428 if (private->constraint == CONSTRAINT_UNKNOWN)
429 {
430 if (fabs (x2 - x1) > fabs (y2 - y1))
431 private->constraint = CONSTRAINT_HORIZONTAL;
432 else if (fabs (y2 - y1) > fabs (x2 - x1))
433 private->constraint = CONSTRAINT_VERTICAL;
434 }
435
436 if (private->constraint == CONSTRAINT_HORIZONTAL)
437 y2 = y1;
438 else if (private->constraint == CONSTRAINT_VERTICAL)
439 x2 = x1;
440
441 gimp_display_shell_unrotate_xy_f (shell, x1, y1, &x1, &y1);
442 gimp_display_shell_unrotate_xy_f (shell, x2, y2, &x2, &y2);
443 }
444
445 if (private->invert)
446 factor = 1.0 / factor;
447
448 gimp_vector3_set (&axis, y2 - y1, x2 - x1, 0.0);
449 gimp_vector3_mul (&axis, factor * private->speed);
450 }
451 break;
452
453 case MODE_ROTATE:
454 {
455 gdouble angle;
456
457 angle = atan2 (coords->y - private->pivot_y,
458 coords->x - private->pivot_x);
459
460 private->curr_angle = angle;
461
462 angle -= private->last_angle;
463
464 if (state & gimp_get_constrain_behavior_mask ())
465 angle = RINT (angle / (G_PI / 6.0)) * (G_PI / 6.0);
466
467 gimp_vector3_set (&axis, 0.0, 0.0, angle);
468
469 private->last_angle += angle;
470 }
471 break;
472
473 case MODE_ZOOM:
474 {
475 gdouble x1, y1;
476 gdouble x2, y2;
477 gdouble zoom;
478
479 gimp_display_shell_transform_xy_f (shell,
480 private->last_x, private->last_y,
481 &x1, &y1);
482 gimp_display_shell_transform_xy_f (shell,
483 coords->x, coords->y,
484 &x2, &y2);
485
486 zoom = (y1 - y2) * shell->scale_y / 128.0;
487
488 if (private->invert)
489 zoom = -zoom;
490
491 private->last_zoom *= pow (2.0, zoom);
492
493 zoom = log (private->last_zoom / private->zoom) / G_LN2;
494
495 if (state & gimp_get_constrain_behavior_mask ())
496 zoom = RINT (zoom * 2.0) / 2.0;
497
498 g_object_set (gyroscope,
499 "zoom", private->zoom * pow (2.0, zoom),
500 NULL);
501 }
502 break;
503
504 case MODE_NONE:
505 g_return_if_reached ();
506 }
507
508 private->last_x = coords->x;
509 private->last_y = coords->y;
510
511 gimp_tool_gyroscope_rotate (gyroscope, &axis);
512 }
513
514 static GimpHit
gimp_tool_gyroscope_hit(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,gboolean proximity)515 gimp_tool_gyroscope_hit (GimpToolWidget *widget,
516 const GimpCoords *coords,
517 GdkModifierType state,
518 gboolean proximity)
519 {
520 return GIMP_HIT_INDIRECT;
521 }
522
523 static void
gimp_tool_gyroscope_hover(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,gboolean proximity)524 gimp_tool_gyroscope_hover (GimpToolWidget *widget,
525 const GimpCoords *coords,
526 GdkModifierType state,
527 gboolean proximity)
528 {
529 gimp_tool_gyroscope_update_status (GIMP_TOOL_GYROSCOPE (widget), state);
530 }
531
532 static gboolean
gimp_tool_gyroscope_key_press(GimpToolWidget * widget,GdkEventKey * kevent)533 gimp_tool_gyroscope_key_press (GimpToolWidget *widget,
534 GdkEventKey *kevent)
535 {
536 GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
537 GimpToolGyroscopePrivate *private = gyroscope->private;
538 GimpDisplayShell *shell = gimp_tool_widget_get_shell (widget);
539 GimpVector3 axis = {};
540 gboolean fast;
541 gboolean result = FALSE;
542
543 fast = (kevent->state & gimp_get_constrain_behavior_mask ());
544
545 if (kevent->state & GDK_MOD1_MASK)
546 {
547 /* zoom */
548 gdouble zoom = 0.0;
549
550 switch (kevent->keyval)
551 {
552 case GDK_KEY_Up:
553 zoom = fast ? +1.0 : +1.0 / 8.0;
554 result = TRUE;
555 break;
556
557 case GDK_KEY_Down:
558 zoom = fast ? -1.0 : -1.0 / 8.0;
559 result = TRUE;
560 break;
561 }
562
563 if (private->invert)
564 zoom = -zoom;
565
566 if (zoom)
567 {
568 g_object_set (gyroscope,
569 "zoom", private->zoom * pow (2.0, zoom),
570 NULL);
571 }
572 }
573 else if (kevent->state & gimp_get_extend_selection_mask ())
574 {
575 /* rotate */
576 gdouble angle = 0.0;
577
578 switch (kevent->keyval)
579 {
580 case GDK_KEY_Left:
581 angle = fast ? +15.0 : +1.0;
582 result = TRUE;
583 break;
584
585 case GDK_KEY_Right:
586 angle = fast ? -15.0 : -1.0;
587 result = TRUE;
588 break;
589 }
590
591 if (shell->flip_horizontally ^ shell->flip_vertically)
592 angle = -angle;
593
594 gimp_vector3_set (&axis, 0.0, 0.0, angle * DEG_TO_RAD);
595 }
596 else
597 {
598 /* pan */
599 gdouble x0 = 0.0;
600 gdouble y0 = 0.0;
601 gdouble x = 0.0;
602 gdouble y = 0.0;
603 gdouble factor = 1.0 / private->zoom;
604
605 if (private->invert)
606 factor = 1.0 / factor;
607
608 switch (kevent->keyval)
609 {
610 case GDK_KEY_Left:
611 x = fast ? +15.0 : +1.0;
612 result = TRUE;
613 break;
614
615 case GDK_KEY_Right:
616 x = fast ? -15.0 : -1.0;
617 result = TRUE;
618 break;
619
620 case GDK_KEY_Up:
621 y = fast ? +15.0 : +1.0;
622 result = TRUE;
623 break;
624
625 case GDK_KEY_Down:
626 y = fast ? -15.0 : -1.0;
627 result = TRUE;
628 break;
629 }
630
631 gimp_display_shell_unrotate_xy_f (shell, x0, y0, &x0, &y0);
632 gimp_display_shell_unrotate_xy_f (shell, x, y, &x, &y);
633
634 gimp_vector3_set (&axis,
635 (y - y0) * DEG_TO_RAD,
636 (x - x0) * DEG_TO_RAD,
637 0.0);
638 gimp_vector3_mul (&axis, factor);
639 }
640
641 gimp_tool_gyroscope_rotate (gyroscope, &axis);
642
643 return result;
644 }
645
646 static void
gimp_tool_gyroscope_motion_modifier(GimpToolWidget * widget,GdkModifierType key,gboolean press,GdkModifierType state)647 gimp_tool_gyroscope_motion_modifier (GimpToolWidget *widget,
648 GdkModifierType key,
649 gboolean press,
650 GdkModifierType state)
651 {
652 GimpToolGyroscope *gyroscope = GIMP_TOOL_GYROSCOPE (widget);
653 GimpToolGyroscopePrivate *private = gyroscope->private;
654
655 gimp_tool_gyroscope_update_status (gyroscope, state);
656
657 if (key == gimp_get_constrain_behavior_mask ())
658 {
659 switch (private->mode)
660 {
661 case MODE_PAN:
662 if (state & gimp_get_constrain_behavior_mask ())
663 private->constraint = CONSTRAINT_UNKNOWN;
664 else
665 private->constraint = CONSTRAINT_NONE;
666 break;
667
668 case MODE_ROTATE:
669 if (! (state & gimp_get_constrain_behavior_mask ()))
670 private->last_angle = private->curr_angle;
671 break;
672
673 case MODE_ZOOM:
674 if (! (state & gimp_get_constrain_behavior_mask ()))
675 private->last_zoom = private->zoom;
676 break;
677
678 case MODE_NONE:
679 break;
680 }
681 }
682 }
683
684 static gboolean
gimp_tool_gyroscope_get_cursor(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,GimpCursorType * cursor,GimpToolCursorType * tool_cursor,GimpCursorModifier * modifier)685 gimp_tool_gyroscope_get_cursor (GimpToolWidget *widget,
686 const GimpCoords *coords,
687 GdkModifierType state,
688 GimpCursorType *cursor,
689 GimpToolCursorType *tool_cursor,
690 GimpCursorModifier *modifier)
691 {
692 if (state & GDK_MOD1_MASK)
693 *modifier = GIMP_CURSOR_MODIFIER_ZOOM;
694 else if (state & gimp_get_extend_selection_mask ())
695 *modifier = GIMP_CURSOR_MODIFIER_ROTATE;
696 else
697 *modifier = GIMP_CURSOR_MODIFIER_MOVE;
698
699 return TRUE;
700 }
701
702 static void
gimp_tool_gyroscope_update_status(GimpToolGyroscope * gyroscope,GdkModifierType state)703 gimp_tool_gyroscope_update_status (GimpToolGyroscope *gyroscope,
704 GdkModifierType state)
705 {
706 GimpToolGyroscopePrivate *private = gyroscope->private;
707 gchar *status;
708
709 if (private->mode == MODE_ZOOM ||
710 (private->mode == MODE_NONE &&
711 state & GDK_MOD1_MASK))
712 {
713 status = gimp_suggest_modifiers (_("Click-Drag to zoom"),
714 gimp_get_toggle_behavior_mask () &
715 ~state,
716 NULL,
717 _("%s for constrained steps"),
718 NULL);
719 }
720 else if (private->mode == MODE_ROTATE ||
721 (private->mode == MODE_NONE &&
722 state & gimp_get_extend_selection_mask ()))
723 {
724 status = gimp_suggest_modifiers (_("Click-Drag to rotate"),
725 gimp_get_toggle_behavior_mask () &
726 ~state,
727 NULL,
728 _("%s for constrained angles"),
729 NULL);
730 }
731 else
732 {
733 status = gimp_suggest_modifiers (_("Click-Drag to pan"),
734 (((gimp_get_extend_selection_mask () |
735 GDK_MOD1_MASK) *
736 (private->mode == MODE_NONE)) |
737 gimp_get_toggle_behavior_mask ()) &
738 ~state,
739 _("%s to rotate"),
740 _("%s for a constrained axis"),
741 _("%s to zoom"));
742 }
743
744 gimp_tool_widget_set_status (GIMP_TOOL_WIDGET (gyroscope), status);
745
746 g_free (status);
747 }
748
749 static void
gimp_tool_gyroscope_save(GimpToolGyroscope * gyroscope)750 gimp_tool_gyroscope_save (GimpToolGyroscope *gyroscope)
751 {
752 GimpToolGyroscopePrivate *private = gyroscope->private;
753
754 private->orig_yaw = private->yaw;
755 private->orig_pitch = private->pitch;
756 private->orig_roll = private->roll;
757 private->orig_zoom = private->zoom;
758 }
759
760 static void
gimp_tool_gyroscope_restore(GimpToolGyroscope * gyroscope)761 gimp_tool_gyroscope_restore (GimpToolGyroscope *gyroscope)
762 {
763 GimpToolGyroscopePrivate *private = gyroscope->private;
764
765 g_object_set (gyroscope,
766 "yaw", private->orig_yaw,
767 "pitch", private->orig_pitch,
768 "roll", private->orig_roll,
769 "zoom", private->orig_zoom,
770 NULL);
771 }
772
773 static void
gimp_tool_gyroscope_rotate(GimpToolGyroscope * gyroscope,const GimpVector3 * axis)774 gimp_tool_gyroscope_rotate (GimpToolGyroscope *gyroscope,
775 const GimpVector3 *axis)
776 {
777 GimpToolGyroscopePrivate *private = gyroscope->private;
778 GimpVector3 real_axis;
779 GimpVector3 basis[2];
780 gdouble yaw;
781 gdouble pitch;
782 gdouble roll;
783 gint i;
784
785 if (gimp_vector3_length (axis) < EPSILON)
786 return;
787
788 real_axis = *axis;
789
790 if (private->invert)
791 gimp_vector3_neg (&real_axis);
792
793 for (i = 0; i < 2; i++)
794 {
795 gimp_vector3_set (&basis[i], i == 0, i == 1, 0.0);
796
797 if (private->invert)
798 gimp_tool_gyroscope_rotate_vector (&basis[i], &real_axis);
799
800 gimp_tool_gyroscope_rotate_vector (
801 &basis[i], &(GimpVector3) {0.0, private->yaw * DEG_TO_RAD, 0.0});
802 gimp_tool_gyroscope_rotate_vector (
803 &basis[i], &(GimpVector3) {private->pitch * DEG_TO_RAD, 0.0, 0.0});
804 gimp_tool_gyroscope_rotate_vector (
805 &basis[i], &(GimpVector3) {0.0, 0.0, private->roll * DEG_TO_RAD});
806
807 if (! private->invert)
808 gimp_tool_gyroscope_rotate_vector (&basis[i], &real_axis);
809 }
810
811 roll = atan2 (basis[1].x, basis[1].y);
812
813 for (i = 0; i < 2; i++)
814 {
815 gimp_tool_gyroscope_rotate_vector (
816 &basis[i], &(GimpVector3) {0.0, 0.0, -roll});
817 }
818
819 pitch = atan2 (-basis[1].z, basis[1].y);
820
821 for (i = 0; i < 1; i++)
822 {
823 gimp_tool_gyroscope_rotate_vector (
824 &basis[i], &(GimpVector3) {-pitch, 0.0, 0.0});
825 }
826
827 yaw = atan2 (basis[0].z, basis[0].x);
828
829 g_object_set (gyroscope,
830 "yaw", yaw / DEG_TO_RAD,
831 "pitch", pitch / DEG_TO_RAD,
832 "roll", roll / DEG_TO_RAD,
833 NULL);
834 }
835
836 static void
gimp_tool_gyroscope_rotate_vector(GimpVector3 * vector,const GimpVector3 * axis)837 gimp_tool_gyroscope_rotate_vector (GimpVector3 *vector,
838 const GimpVector3 *axis)
839 {
840 GimpVector3 normalized_axis;
841 GimpVector3 projection;
842 GimpVector3 u;
843 GimpVector3 v;
844 gdouble angle;
845
846 angle = gimp_vector3_length (axis);
847
848 if (angle < EPSILON)
849 return;
850
851 normalized_axis = gimp_vector3_mul_val (*axis, 1.0 / angle);
852
853 projection = gimp_vector3_mul_val (
854 normalized_axis,
855 gimp_vector3_inner_product (vector, &normalized_axis));
856
857 u = gimp_vector3_sub_val (*vector, projection);
858 v = gimp_vector3_cross_product (&u, &normalized_axis);
859
860 gimp_vector3_mul (&u, cos (angle));
861 gimp_vector3_mul (&v, sin (angle));
862
863 gimp_vector3_add (vector, &u, &v);
864 gimp_vector3_add (vector, vector, &projection);
865 }
866
867
868 /* public functions */
869
870
871 GimpToolWidget *
gimp_tool_gyroscope_new(GimpDisplayShell * shell)872 gimp_tool_gyroscope_new (GimpDisplayShell *shell)
873 {
874 g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
875
876 return g_object_new (GIMP_TYPE_TOOL_GYROSCOPE,
877 "shell", shell,
878 NULL);
879 }
880