1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimptooltransformgrid.c
5  * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
6  *
7  * Based on GimpUnifiedTransformTool
8  * Copyright (C) 2011 Mikael Magnusson <mikachu@src.gnome.org>
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <gegl.h>
27 #include <gtk/gtk.h>
28 
29 #include "libgimpbase/gimpbase.h"
30 #include "libgimpmath/gimpmath.h"
31 
32 #include "display-types.h"
33 
34 #include "core/gimp-transform-utils.h"
35 #include "core/gimp-utils.h"
36 
37 #include "widgets/gimpwidgets-utils.h"
38 
39 #include "gimpcanvashandle.h"
40 #include "gimpcanvastransformguides.h"
41 #include "gimpdisplayshell.h"
42 #include "gimptooltransformgrid.h"
43 
44 #include "gimp-intl.h"
45 
46 
47 #define MIN_HANDLE_SIZE 6
48 
49 
50 enum
51 {
52   PROP_0,
53   PROP_TRANSFORM,
54   PROP_X1,
55   PROP_Y1,
56   PROP_X2,
57   PROP_Y2,
58   PROP_PIVOT_X,
59   PROP_PIVOT_Y,
60   PROP_GUIDE_TYPE,
61   PROP_N_GUIDES,
62   PROP_CLIP_GUIDES,
63   PROP_SHOW_GUIDES,
64   PROP_INSIDE_FUNCTION,
65   PROP_OUTSIDE_FUNCTION,
66   PROP_USE_CORNER_HANDLES,
67   PROP_USE_PERSPECTIVE_HANDLES,
68   PROP_USE_SIDE_HANDLES,
69   PROP_USE_SHEAR_HANDLES,
70   PROP_USE_CENTER_HANDLE,
71   PROP_USE_PIVOT_HANDLE,
72   PROP_DYNAMIC_HANDLE_SIZE,
73   PROP_CONSTRAIN_MOVE,
74   PROP_CONSTRAIN_SCALE,
75   PROP_CONSTRAIN_ROTATE,
76   PROP_CONSTRAIN_SHEAR,
77   PROP_CONSTRAIN_PERSPECTIVE,
78   PROP_FROMPIVOT_SCALE,
79   PROP_FROMPIVOT_SHEAR,
80   PROP_FROMPIVOT_PERSPECTIVE,
81   PROP_CORNERSNAP,
82   PROP_FIXEDPIVOT
83 };
84 
85 
86 struct _GimpToolTransformGridPrivate
87 {
88   GimpMatrix3            transform;
89   gdouble                x1, y1;
90   gdouble                x2, y2;
91   gdouble                pivot_x;
92   gdouble                pivot_y;
93   GimpGuidesType         guide_type;
94   gint                   n_guides;
95   gboolean               clip_guides;
96   gboolean               show_guides;
97   GimpTransformFunction  inside_function;
98   GimpTransformFunction  outside_function;
99   gboolean               use_corner_handles;
100   gboolean               use_perspective_handles;
101   gboolean               use_side_handles;
102   gboolean               use_shear_handles;
103   gboolean               use_center_handle;
104   gboolean               use_pivot_handle;
105   gboolean               dynamic_handle_size;
106   gboolean               constrain_move;
107   gboolean               constrain_scale;
108   gboolean               constrain_rotate;
109   gboolean               constrain_shear;
110   gboolean               constrain_perspective;
111   gboolean               frompivot_scale;
112   gboolean               frompivot_shear;
113   gboolean               frompivot_perspective;
114   gboolean               cornersnap;
115   gboolean               fixedpivot;
116 
117   gdouble                curx;         /*  current x coord                    */
118   gdouble                cury;         /*  current y coord                    */
119 
120   gdouble                button_down;  /*  is the mouse button pressed        */
121   gdouble                mousex;       /*  x coord where mouse was clicked    */
122   gdouble                mousey;       /*  y coord where mouse was clicked    */
123 
124   gdouble                cx, cy;       /*  center point (for moving)          */
125 
126   /*  transformed handle coords */
127   gdouble                tx1, ty1;
128   gdouble                tx2, ty2;
129   gdouble                tx3, ty3;
130   gdouble                tx4, ty4;
131   gdouble                tcx, tcy;
132   gdouble                tpx, tpy;
133 
134   /*  previous transformed handle coords */
135   gdouble                prev_tx1, prev_ty1;
136   gdouble                prev_tx2, prev_ty2;
137   gdouble                prev_tx3, prev_ty3;
138   gdouble                prev_tx4, prev_ty4;
139   gdouble                prev_tcx, prev_tcy;
140   gdouble                prev_tpx, prev_tpy;
141 
142   GimpTransformHandle    handle;        /*  current tool activity              */
143 
144   GimpCanvasItem        *guides;
145   GimpCanvasItem        *handles[GIMP_N_TRANSFORM_HANDLES];
146   GimpCanvasItem        *center_items[2];
147   GimpCanvasItem        *pivot_items[2];
148 };
149 
150 
151 /*  local function prototypes  */
152 
153 static void     gimp_tool_transform_grid_constructed    (GObject               *object);
154 static void     gimp_tool_transform_grid_set_property   (GObject               *object,
155                                                          guint                  property_id,
156                                                          const GValue          *value,
157                                                          GParamSpec            *pspec);
158 static void     gimp_tool_transform_grid_get_property   (GObject               *object,
159                                                          guint                  property_id,
160                                                          GValue                *value,
161                                                          GParamSpec            *pspec);
162 
163 static void     gimp_tool_transform_grid_changed        (GimpToolWidget        *widget);
164 static gint     gimp_tool_transform_grid_button_press   (GimpToolWidget        *widget,
165                                                          const GimpCoords      *coords,
166                                                          guint32                time,
167                                                          GdkModifierType        state,
168                                                          GimpButtonPressType    press_type);
169 static void     gimp_tool_transform_grid_button_release (GimpToolWidget        *widget,
170                                                          const GimpCoords      *coords,
171                                                          guint32                time,
172                                                          GdkModifierType        state,
173                                                          GimpButtonReleaseType  release_type);
174 static void     gimp_tool_transform_grid_motion         (GimpToolWidget        *widget,
175                                                          const GimpCoords      *coords,
176                                                          guint32                time,
177                                                          GdkModifierType        state);
178 static GimpHit  gimp_tool_transform_grid_hit            (GimpToolWidget        *widget,
179                                                          const GimpCoords      *coords,
180                                                          GdkModifierType        state,
181                                                          gboolean               proximity);
182 static void     gimp_tool_transform_grid_hover          (GimpToolWidget        *widget,
183                                                          const GimpCoords      *coords,
184                                                          GdkModifierType        state,
185                                                          gboolean               proximity);
186 static void     gimp_tool_transform_grid_leave_notify   (GimpToolWidget        *widget);
187 static void     gimp_tool_transform_grid_hover_modifier (GimpToolWidget        *widget,
188                                                          GdkModifierType        key,
189                                                          gboolean               press,
190                                                          GdkModifierType        state);
191 static gboolean gimp_tool_transform_grid_get_cursor     (GimpToolWidget        *widget,
192                                                          const GimpCoords      *coords,
193                                                          GdkModifierType        state,
194                                                          GimpCursorType        *cursor,
195                                                          GimpToolCursorType    *tool_cursor,
196                                                          GimpCursorModifier    *modifier);
197 
198 static GimpTransformHandle
199                 gimp_tool_transform_grid_get_handle_for_coords
200                                                         (GimpToolTransformGrid *grid,
201                                                          const GimpCoords      *coords);
202 static void     gimp_tool_transform_grid_update_hilight (GimpToolTransformGrid *grid);
203 static void     gimp_tool_transform_grid_update_box     (GimpToolTransformGrid *grid);
204 static void     gimp_tool_transform_grid_update_matrix  (GimpToolTransformGrid *grid);
205 static void     gimp_tool_transform_grid_calc_handles   (GimpToolTransformGrid *grid,
206                                                          gint                  *handle_w,
207                                                          gint                  *handle_h);
208 
209 
G_DEFINE_TYPE_WITH_PRIVATE(GimpToolTransformGrid,gimp_tool_transform_grid,GIMP_TYPE_TOOL_WIDGET)210 G_DEFINE_TYPE_WITH_PRIVATE (GimpToolTransformGrid, gimp_tool_transform_grid,
211                             GIMP_TYPE_TOOL_WIDGET)
212 
213 #define parent_class gimp_tool_transform_grid_parent_class
214 
215 
216 static void
217 gimp_tool_transform_grid_class_init (GimpToolTransformGridClass *klass)
218 {
219   GObjectClass        *object_class = G_OBJECT_CLASS (klass);
220   GimpToolWidgetClass *widget_class = GIMP_TOOL_WIDGET_CLASS (klass);
221 
222   object_class->constructed     = gimp_tool_transform_grid_constructed;
223   object_class->set_property    = gimp_tool_transform_grid_set_property;
224   object_class->get_property    = gimp_tool_transform_grid_get_property;
225 
226   widget_class->changed         = gimp_tool_transform_grid_changed;
227   widget_class->button_press    = gimp_tool_transform_grid_button_press;
228   widget_class->button_release  = gimp_tool_transform_grid_button_release;
229   widget_class->motion          = gimp_tool_transform_grid_motion;
230   widget_class->hit             = gimp_tool_transform_grid_hit;
231   widget_class->hover           = gimp_tool_transform_grid_hover;
232   widget_class->leave_notify    = gimp_tool_transform_grid_leave_notify;
233   widget_class->hover_modifier  = gimp_tool_transform_grid_hover_modifier;
234   widget_class->get_cursor      = gimp_tool_transform_grid_get_cursor;
235   widget_class->update_on_scale = TRUE;
236 
237   g_object_class_install_property (object_class, PROP_TRANSFORM,
238                                    gimp_param_spec_matrix3 ("transform",
239                                                             NULL, NULL,
240                                                             NULL,
241                                                             GIMP_PARAM_READWRITE |
242                                                             G_PARAM_CONSTRUCT));
243 
244   g_object_class_install_property (object_class, PROP_X1,
245                                    g_param_spec_double ("x1",
246                                                         NULL, NULL,
247                                                         -GIMP_MAX_IMAGE_SIZE,
248                                                         GIMP_MAX_IMAGE_SIZE,
249                                                         0.0,
250                                                         GIMP_PARAM_READWRITE |
251                                                         G_PARAM_CONSTRUCT));
252 
253   g_object_class_install_property (object_class, PROP_Y1,
254                                    g_param_spec_double ("y1",
255                                                         NULL, NULL,
256                                                         -GIMP_MAX_IMAGE_SIZE,
257                                                         GIMP_MAX_IMAGE_SIZE,
258                                                         0.0,
259                                                         GIMP_PARAM_READWRITE |
260                                                         G_PARAM_CONSTRUCT));
261 
262   g_object_class_install_property (object_class, PROP_X2,
263                                    g_param_spec_double ("x2",
264                                                         NULL, NULL,
265                                                         -GIMP_MAX_IMAGE_SIZE,
266                                                         GIMP_MAX_IMAGE_SIZE,
267                                                         0.0,
268                                                         GIMP_PARAM_READWRITE |
269                                                         G_PARAM_CONSTRUCT));
270 
271   g_object_class_install_property (object_class, PROP_Y2,
272                                    g_param_spec_double ("y2",
273                                                         NULL, NULL,
274                                                         -GIMP_MAX_IMAGE_SIZE,
275                                                         GIMP_MAX_IMAGE_SIZE,
276                                                         0.0,
277                                                         GIMP_PARAM_READWRITE |
278                                                         G_PARAM_CONSTRUCT));
279 
280   g_object_class_install_property (object_class, PROP_PIVOT_X,
281                                    g_param_spec_double ("pivot-x",
282                                                         NULL, NULL,
283                                                         -GIMP_MAX_IMAGE_SIZE,
284                                                         GIMP_MAX_IMAGE_SIZE,
285                                                         0.0,
286                                                         GIMP_PARAM_READWRITE |
287                                                         G_PARAM_CONSTRUCT));
288 
289   g_object_class_install_property (object_class, PROP_PIVOT_Y,
290                                    g_param_spec_double ("pivot-y",
291                                                         NULL, NULL,
292                                                         -GIMP_MAX_IMAGE_SIZE,
293                                                         GIMP_MAX_IMAGE_SIZE,
294                                                         0.0,
295                                                         GIMP_PARAM_READWRITE |
296                                                         G_PARAM_CONSTRUCT));
297 
298   g_object_class_install_property (object_class, PROP_GUIDE_TYPE,
299                                    g_param_spec_enum ("guide-type", NULL, NULL,
300                                                       GIMP_TYPE_GUIDES_TYPE,
301                                                       GIMP_GUIDES_NONE,
302                                                       GIMP_PARAM_READWRITE |
303                                                       G_PARAM_CONSTRUCT));
304 
305   g_object_class_install_property (object_class, PROP_N_GUIDES,
306                                    g_param_spec_int ("n-guides", NULL, NULL,
307                                                      1, 128, 4,
308                                                      GIMP_PARAM_READWRITE |
309                                                      G_PARAM_CONSTRUCT));
310 
311   g_object_class_install_property (object_class, PROP_CLIP_GUIDES,
312                                    g_param_spec_boolean ("clip-guides", NULL, NULL,
313                                                          FALSE,
314                                                          GIMP_PARAM_READWRITE |
315                                                          G_PARAM_CONSTRUCT));
316 
317   g_object_class_install_property (object_class, PROP_SHOW_GUIDES,
318                                    g_param_spec_boolean ("show-guides", NULL, NULL,
319                                                          TRUE,
320                                                          GIMP_PARAM_READWRITE |
321                                                          G_PARAM_CONSTRUCT));
322 
323   g_object_class_install_property (object_class, PROP_INSIDE_FUNCTION,
324                                    g_param_spec_enum ("inside-function",
325                                                       NULL, NULL,
326                                                       GIMP_TYPE_TRANSFORM_FUNCTION,
327                                                       GIMP_TRANSFORM_FUNCTION_MOVE,
328                                                       GIMP_PARAM_READWRITE |
329                                                       G_PARAM_CONSTRUCT));
330 
331   g_object_class_install_property (object_class, PROP_OUTSIDE_FUNCTION,
332                                    g_param_spec_enum ("outside-function",
333                                                       NULL, NULL,
334                                                       GIMP_TYPE_TRANSFORM_FUNCTION,
335                                                       GIMP_TRANSFORM_FUNCTION_ROTATE,
336                                                       GIMP_PARAM_READWRITE |
337                                                       G_PARAM_CONSTRUCT));
338 
339   g_object_class_install_property (object_class, PROP_USE_CORNER_HANDLES,
340                                    g_param_spec_boolean ("use-corner-handles",
341                                                          NULL, NULL,
342                                                          FALSE,
343                                                          GIMP_PARAM_READWRITE |
344                                                          G_PARAM_CONSTRUCT));
345 
346   g_object_class_install_property (object_class, PROP_USE_PERSPECTIVE_HANDLES,
347                                    g_param_spec_boolean ("use-perspective-handles",
348                                                          NULL, NULL,
349                                                          FALSE,
350                                                          GIMP_PARAM_READWRITE |
351                                                          G_PARAM_CONSTRUCT));
352 
353   g_object_class_install_property (object_class, PROP_USE_SIDE_HANDLES,
354                                    g_param_spec_boolean ("use-side-handles",
355                                                          NULL, NULL,
356                                                          FALSE,
357                                                          GIMP_PARAM_READWRITE |
358                                                          G_PARAM_CONSTRUCT));
359 
360   g_object_class_install_property (object_class, PROP_USE_SHEAR_HANDLES,
361                                    g_param_spec_boolean ("use-shear-handles",
362                                                          NULL, NULL,
363                                                          FALSE,
364                                                          GIMP_PARAM_READWRITE |
365                                                          G_PARAM_CONSTRUCT));
366 
367   g_object_class_install_property (object_class, PROP_USE_CENTER_HANDLE,
368                                    g_param_spec_boolean ("use-center-handle",
369                                                          NULL, NULL,
370                                                          FALSE,
371                                                          GIMP_PARAM_READWRITE |
372                                                          G_PARAM_CONSTRUCT));
373 
374   g_object_class_install_property (object_class, PROP_USE_PIVOT_HANDLE,
375                                    g_param_spec_boolean ("use-pivot-handle",
376                                                          NULL, NULL,
377                                                          FALSE,
378                                                          GIMP_PARAM_READWRITE |
379                                                          G_PARAM_CONSTRUCT));
380 
381   g_object_class_install_property (object_class, PROP_DYNAMIC_HANDLE_SIZE,
382                                    g_param_spec_boolean ("dynamic-handle-size",
383                                                          NULL, NULL,
384                                                          TRUE,
385                                                          GIMP_PARAM_READWRITE |
386                                                          G_PARAM_CONSTRUCT));
387 
388   g_object_class_install_property (object_class, PROP_CONSTRAIN_MOVE,
389                                    g_param_spec_boolean ("constrain-move",
390                                                          NULL, NULL,
391                                                          FALSE,
392                                                          GIMP_PARAM_READWRITE |
393                                                          G_PARAM_CONSTRUCT));
394 
395   g_object_class_install_property (object_class, PROP_CONSTRAIN_SCALE,
396                                    g_param_spec_boolean ("constrain-scale",
397                                                          NULL, NULL,
398                                                          FALSE,
399                                                          GIMP_PARAM_READWRITE |
400                                                          G_PARAM_CONSTRUCT));
401 
402   g_object_class_install_property (object_class, PROP_CONSTRAIN_ROTATE,
403                                    g_param_spec_boolean ("constrain-rotate",
404                                                          NULL, NULL,
405                                                          FALSE,
406                                                          GIMP_PARAM_READWRITE |
407                                                          G_PARAM_CONSTRUCT));
408 
409   g_object_class_install_property (object_class, PROP_CONSTRAIN_SHEAR,
410                                    g_param_spec_boolean ("constrain-shear",
411                                                          NULL, NULL,
412                                                          FALSE,
413                                                          GIMP_PARAM_READWRITE |
414                                                          G_PARAM_CONSTRUCT));
415 
416   g_object_class_install_property (object_class, PROP_CONSTRAIN_PERSPECTIVE,
417                                    g_param_spec_boolean ("constrain-perspective",
418                                                          NULL, NULL,
419                                                          FALSE,
420                                                          GIMP_PARAM_READWRITE |
421                                                          G_PARAM_CONSTRUCT));
422 
423   g_object_class_install_property (object_class, PROP_FROMPIVOT_SCALE,
424                                    g_param_spec_boolean ("frompivot-scale",
425                                                          NULL, NULL,
426                                                          FALSE,
427                                                          GIMP_PARAM_READWRITE |
428                                                          G_PARAM_CONSTRUCT));
429 
430   g_object_class_install_property (object_class, PROP_FROMPIVOT_SHEAR,
431                                    g_param_spec_boolean ("frompivot-shear",
432                                                          NULL, NULL,
433                                                          FALSE,
434                                                          GIMP_PARAM_READWRITE |
435                                                          G_PARAM_CONSTRUCT));
436 
437   g_object_class_install_property (object_class, PROP_FROMPIVOT_PERSPECTIVE,
438                                    g_param_spec_boolean ("frompivot-perspective",
439                                                          NULL, NULL,
440                                                          FALSE,
441                                                          GIMP_PARAM_READWRITE |
442                                                          G_PARAM_CONSTRUCT));
443 
444   g_object_class_install_property (object_class, PROP_CORNERSNAP,
445                                    g_param_spec_boolean ("cornersnap",
446                                                          NULL, NULL,
447                                                          FALSE,
448                                                          GIMP_PARAM_READWRITE |
449                                                          G_PARAM_CONSTRUCT));
450 
451   g_object_class_install_property (object_class, PROP_FIXEDPIVOT,
452                                    g_param_spec_boolean ("fixedpivot",
453                                                          NULL, NULL,
454                                                          FALSE,
455                                                          GIMP_PARAM_READWRITE |
456                                                          G_PARAM_CONSTRUCT));
457 }
458 
459 static void
gimp_tool_transform_grid_init(GimpToolTransformGrid * grid)460 gimp_tool_transform_grid_init (GimpToolTransformGrid *grid)
461 {
462   grid->private = gimp_tool_transform_grid_get_instance_private (grid);
463 }
464 
465 static void
gimp_tool_transform_grid_constructed(GObject * object)466 gimp_tool_transform_grid_constructed (GObject *object)
467 {
468   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (object);
469   GimpToolWidget               *widget  = GIMP_TOOL_WIDGET (object);
470   GimpToolTransformGridPrivate *private = grid->private;
471   GimpCanvasGroup              *stroke_group;
472   gint                          i;
473 
474   G_OBJECT_CLASS (parent_class)->constructed (object);
475 
476   private->guides = gimp_tool_widget_add_transform_guides (widget,
477                                                            &private->transform,
478                                                            private->x1,
479                                                            private->y1,
480                                                            private->x2,
481                                                            private->y2,
482                                                            private->guide_type,
483                                                            private->n_guides,
484                                                            private->clip_guides);
485 
486   for (i = 0; i < 4; i++)
487     {
488       /*  draw the scale handles  */
489       private->handles[GIMP_TRANSFORM_HANDLE_NW + i] =
490         gimp_tool_widget_add_handle (widget,
491                                      GIMP_HANDLE_SQUARE,
492                                      0, 0, 10, 10,
493                                      GIMP_HANDLE_ANCHOR_CENTER);
494 
495       /*  draw the perspective handles  */
496       private->handles[GIMP_TRANSFORM_HANDLE_NW_P + i] =
497         gimp_tool_widget_add_handle (widget,
498                                      GIMP_HANDLE_DIAMOND,
499                                      0, 0, 10, 10,
500                                      GIMP_HANDLE_ANCHOR_CENTER);
501 
502       /*  draw the side handles  */
503       private->handles[GIMP_TRANSFORM_HANDLE_N + i] =
504         gimp_tool_widget_add_handle (widget,
505                                      GIMP_HANDLE_SQUARE,
506                                      0, 0, 10, 10,
507                                      GIMP_HANDLE_ANCHOR_CENTER);
508 
509       /*  draw the shear handles  */
510       private->handles[GIMP_TRANSFORM_HANDLE_N_S + i] =
511         gimp_tool_widget_add_handle (widget,
512                                      GIMP_HANDLE_FILLED_DIAMOND,
513                                      0, 0, 10, 10,
514                                      GIMP_HANDLE_ANCHOR_CENTER);
515     }
516 
517   /*  draw the rotation center axis handle  */
518   stroke_group = gimp_tool_widget_add_stroke_group (widget);
519 
520   private->handles[GIMP_TRANSFORM_HANDLE_PIVOT] =
521     GIMP_CANVAS_ITEM (stroke_group);
522 
523   gimp_tool_widget_push_group (widget, stroke_group);
524 
525   private->pivot_items[0] =
526     gimp_tool_widget_add_handle (widget,
527                                  GIMP_HANDLE_CIRCLE,
528                                  0, 0, 10, 10,
529                                  GIMP_HANDLE_ANCHOR_CENTER);
530   private->pivot_items[1] =
531     gimp_tool_widget_add_handle (widget,
532                                  GIMP_HANDLE_CROSS,
533                                  0, 0, 10, 10,
534                                  GIMP_HANDLE_ANCHOR_CENTER);
535 
536   gimp_tool_widget_pop_group (widget);
537 
538   /*  draw the center handle  */
539   stroke_group = gimp_tool_widget_add_stroke_group (widget);
540 
541   private->handles[GIMP_TRANSFORM_HANDLE_CENTER] =
542     GIMP_CANVAS_ITEM (stroke_group);
543 
544   gimp_tool_widget_push_group (widget, stroke_group);
545 
546   private->center_items[0] =
547     gimp_tool_widget_add_handle (widget,
548                                  GIMP_HANDLE_SQUARE,
549                                  0, 0, 10, 10,
550                                  GIMP_HANDLE_ANCHOR_CENTER);
551   private->center_items[1] =
552     gimp_tool_widget_add_handle (widget,
553                                  GIMP_HANDLE_CROSS,
554                                  0, 0, 10, 10,
555                                  GIMP_HANDLE_ANCHOR_CENTER);
556 
557   gimp_tool_widget_pop_group (widget);
558 
559   gimp_tool_transform_grid_changed (widget);
560 }
561 
562 static void
gimp_tool_transform_grid_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)563 gimp_tool_transform_grid_set_property (GObject      *object,
564                                        guint         property_id,
565                                        const GValue *value,
566                                        GParamSpec   *pspec)
567 {
568   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (object);
569   GimpToolTransformGridPrivate *private = grid->private;
570   gboolean                      box     = FALSE;
571 
572   switch (property_id)
573     {
574     case PROP_TRANSFORM:
575       {
576         GimpMatrix3 *transform = g_value_get_boxed (value);
577 
578         if (transform)
579           private->transform = *transform;
580         else
581           gimp_matrix3_identity (&private->transform);
582       }
583       break;
584 
585     case PROP_X1:
586       private->x1 = g_value_get_double (value);
587       box = TRUE;
588       break;
589     case PROP_Y1:
590       private->y1 = g_value_get_double (value);
591       box = TRUE;
592       break;
593     case PROP_X2:
594       private->x2 = g_value_get_double (value);
595       box = TRUE;
596       break;
597     case PROP_Y2:
598       private->y2 = g_value_get_double (value);
599       box = TRUE;
600       break;
601 
602     case PROP_PIVOT_X:
603       private->pivot_x = g_value_get_double (value);
604       break;
605     case PROP_PIVOT_Y:
606       private->pivot_y = g_value_get_double (value);
607       break;
608 
609     case PROP_GUIDE_TYPE:
610       private->guide_type = g_value_get_enum (value);
611       break;
612     case PROP_N_GUIDES:
613       private->n_guides = g_value_get_int (value);
614       break;
615     case PROP_CLIP_GUIDES:
616       private->clip_guides = g_value_get_boolean (value);
617       break;
618     case PROP_SHOW_GUIDES:
619       private->show_guides = g_value_get_boolean (value);
620       break;
621 
622     case PROP_INSIDE_FUNCTION:
623       private->inside_function = g_value_get_enum (value);
624       break;
625     case PROP_OUTSIDE_FUNCTION:
626       private->outside_function = g_value_get_enum (value);
627       break;
628 
629     case PROP_USE_CORNER_HANDLES:
630       private->use_corner_handles = g_value_get_boolean (value);
631       break;
632     case PROP_USE_PERSPECTIVE_HANDLES:
633       private->use_perspective_handles = g_value_get_boolean (value);
634       break;
635     case PROP_USE_SIDE_HANDLES:
636       private->use_side_handles = g_value_get_boolean (value);
637       break;
638     case PROP_USE_SHEAR_HANDLES:
639       private->use_shear_handles = g_value_get_boolean (value);
640       break;
641     case PROP_USE_CENTER_HANDLE:
642       private->use_center_handle = g_value_get_boolean (value);
643       break;
644     case PROP_USE_PIVOT_HANDLE:
645       private->use_pivot_handle = g_value_get_boolean (value);
646       break;
647 
648     case PROP_DYNAMIC_HANDLE_SIZE:
649       private->dynamic_handle_size = g_value_get_boolean (value);
650       break;
651 
652     case PROP_CONSTRAIN_MOVE:
653       private->constrain_move = g_value_get_boolean (value);
654       break;
655     case PROP_CONSTRAIN_SCALE:
656       private->constrain_scale = g_value_get_boolean (value);
657       break;
658     case PROP_CONSTRAIN_ROTATE:
659       private->constrain_rotate = g_value_get_boolean (value);
660       break;
661     case PROP_CONSTRAIN_SHEAR:
662       private->constrain_shear = g_value_get_boolean (value);
663       break;
664     case PROP_CONSTRAIN_PERSPECTIVE:
665       private->constrain_perspective = g_value_get_boolean (value);
666       break;
667 
668     case PROP_FROMPIVOT_SCALE:
669       private->frompivot_scale = g_value_get_boolean (value);
670       break;
671     case PROP_FROMPIVOT_SHEAR:
672       private->frompivot_shear = g_value_get_boolean (value);
673       break;
674     case PROP_FROMPIVOT_PERSPECTIVE:
675       private->frompivot_perspective = g_value_get_boolean (value);
676       break;
677 
678     case PROP_CORNERSNAP:
679       private->cornersnap = g_value_get_boolean (value);
680       break;
681     case PROP_FIXEDPIVOT:
682       private->fixedpivot = g_value_get_boolean (value);
683       break;
684 
685     default:
686       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
687       break;
688     }
689 
690   if (box)
691     {
692       private->cx = (private->x1 + private->x2) / 2.0;
693       private->cy = (private->y1 + private->y2) / 2.0;
694     }
695 }
696 
697 static void
gimp_tool_transform_grid_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)698 gimp_tool_transform_grid_get_property (GObject    *object,
699                                        guint       property_id,
700                                        GValue     *value,
701                                        GParamSpec *pspec)
702 {
703   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (object);
704   GimpToolTransformGridPrivate *private = grid->private;
705 
706   switch (property_id)
707     {
708     case PROP_TRANSFORM:
709       g_value_set_boxed (value, &private->transform);
710       break;
711 
712     case PROP_X1:
713       g_value_set_double (value, private->x1);
714       break;
715     case PROP_Y1:
716       g_value_set_double (value, private->y1);
717       break;
718     case PROP_X2:
719       g_value_set_double (value, private->x2);
720       break;
721     case PROP_Y2:
722       g_value_set_double (value, private->y2);
723       break;
724 
725     case PROP_PIVOT_X:
726       g_value_set_double (value, private->pivot_x);
727       break;
728     case PROP_PIVOT_Y:
729       g_value_set_double (value, private->pivot_y);
730       break;
731 
732     case PROP_GUIDE_TYPE:
733       g_value_set_enum (value, private->guide_type);
734       break;
735     case PROP_N_GUIDES:
736       g_value_set_int (value, private->n_guides);
737       break;
738     case PROP_CLIP_GUIDES:
739       g_value_set_boolean (value, private->clip_guides);
740       break;
741     case PROP_SHOW_GUIDES:
742       g_value_set_boolean (value, private->show_guides);
743       break;
744 
745     case PROP_INSIDE_FUNCTION:
746       g_value_set_enum (value, private->inside_function);
747       break;
748     case PROP_OUTSIDE_FUNCTION:
749       g_value_set_enum (value, private->outside_function);
750       break;
751 
752     case PROP_USE_CORNER_HANDLES:
753       g_value_set_boolean (value, private->use_corner_handles);
754       break;
755     case PROP_USE_PERSPECTIVE_HANDLES:
756       g_value_set_boolean (value, private->use_perspective_handles);
757       break;
758     case PROP_USE_SIDE_HANDLES:
759       g_value_set_boolean (value, private->use_side_handles);
760       break;
761     case PROP_USE_SHEAR_HANDLES:
762       g_value_set_boolean (value, private->use_shear_handles);
763       break;
764     case PROP_USE_CENTER_HANDLE:
765       g_value_set_boolean (value, private->use_center_handle);
766       break;
767     case PROP_USE_PIVOT_HANDLE:
768       g_value_set_boolean (value, private->use_pivot_handle);
769       break;
770 
771     case PROP_DYNAMIC_HANDLE_SIZE:
772       g_value_set_boolean (value, private->dynamic_handle_size);
773       break;
774 
775     case PROP_CONSTRAIN_MOVE:
776       g_value_set_boolean (value, private->constrain_move);
777       break;
778     case PROP_CONSTRAIN_SCALE:
779       g_value_set_boolean (value, private->constrain_scale);
780       break;
781     case PROP_CONSTRAIN_ROTATE:
782       g_value_set_boolean (value, private->constrain_rotate);
783       break;
784     case PROP_CONSTRAIN_SHEAR:
785       g_value_set_boolean (value, private->constrain_shear);
786       break;
787     case PROP_CONSTRAIN_PERSPECTIVE:
788       g_value_set_boolean (value, private->constrain_perspective);
789       break;
790 
791     case PROP_FROMPIVOT_SCALE:
792       g_value_set_boolean (value, private->frompivot_scale);
793       break;
794     case PROP_FROMPIVOT_SHEAR:
795       g_value_set_boolean (value, private->frompivot_shear);
796       break;
797     case PROP_FROMPIVOT_PERSPECTIVE:
798       g_value_set_boolean (value, private->frompivot_perspective);
799       break;
800 
801     case PROP_CORNERSNAP:
802       g_value_set_boolean (value, private->cornersnap);
803       break;
804     case PROP_FIXEDPIVOT:
805       g_value_set_boolean (value, private->fixedpivot);
806       break;
807 
808     default:
809       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
810       break;
811     }
812 }
813 
814 static gboolean
transform_is_convex(GimpVector2 * pos)815 transform_is_convex (GimpVector2 *pos)
816 {
817   return gimp_transform_polygon_is_convex (pos[0].x, pos[0].y,
818                                            pos[1].x, pos[1].y,
819                                            pos[2].x, pos[2].y,
820                                            pos[3].x, pos[3].y);
821 }
822 
823 static gboolean
transform_grid_is_convex(GimpToolTransformGrid * grid)824 transform_grid_is_convex (GimpToolTransformGrid *grid)
825 {
826   GimpToolTransformGridPrivate *private = grid->private;
827 
828   return gimp_transform_polygon_is_convex (private->tx1, private->ty1,
829                                            private->tx2, private->ty2,
830                                            private->tx3, private->ty3,
831                                            private->tx4, private->ty4);
832 }
833 
834 static inline gboolean
vectorisnull(GimpVector2 v)835 vectorisnull (GimpVector2 v)
836 {
837   return ((v.x == 0.0) && (v.y == 0.0));
838 }
839 
840 static inline gdouble
dotprod(GimpVector2 a,GimpVector2 b)841 dotprod (GimpVector2 a,
842          GimpVector2 b)
843 {
844   return a.x * b.x + a.y * b.y;
845 }
846 
847 static inline gdouble
norm(GimpVector2 a)848 norm (GimpVector2 a)
849 {
850   return sqrt (dotprod (a, a));
851 }
852 
853 static inline GimpVector2
vectorsubtract(GimpVector2 a,GimpVector2 b)854 vectorsubtract (GimpVector2 a,
855                 GimpVector2 b)
856 {
857   GimpVector2 c;
858 
859   c.x = a.x - b.x;
860   c.y = a.y - b.y;
861 
862   return c;
863 }
864 
865 static inline GimpVector2
vectoradd(GimpVector2 a,GimpVector2 b)866 vectoradd (GimpVector2 a,
867            GimpVector2 b)
868 {
869   GimpVector2 c;
870 
871   c.x = a.x + b.x;
872   c.y = a.y + b.y;
873 
874   return c;
875 }
876 
877 static inline GimpVector2
scalemult(GimpVector2 a,gdouble b)878 scalemult (GimpVector2 a,
879            gdouble     b)
880 {
881   GimpVector2 c;
882 
883   c.x = a.x * b;
884   c.y = a.y * b;
885 
886   return c;
887 }
888 
889 static inline GimpVector2
vectorproject(GimpVector2 a,GimpVector2 b)890 vectorproject (GimpVector2 a,
891                GimpVector2 b)
892 {
893   return scalemult (b, dotprod (a, b) / dotprod (b, b));
894 }
895 
896 /* finds the clockwise angle between the vectors given, 0-2π */
897 static inline gdouble
calcangle(GimpVector2 a,GimpVector2 b)898 calcangle (GimpVector2 a,
899            GimpVector2 b)
900 {
901   gdouble angle, angle2;
902   gdouble length;
903 
904   if (vectorisnull (a) || vectorisnull (b))
905     return 0.0;
906 
907   length = norm (a) * norm (b);
908 
909   angle = acos (SAFE_CLAMP (dotprod (a, b) / length, -1.0, +1.0));
910   angle2 = b.y;
911   b.y = -b.x;
912   b.x = angle2;
913   angle2 = acos (SAFE_CLAMP (dotprod (a, b) / length, -1.0, +1.0));
914 
915   return ((angle2 > G_PI / 2.0) ? angle : 2.0 * G_PI - angle);
916 }
917 
918 static inline GimpVector2
rotate2d(GimpVector2 p,gdouble angle)919 rotate2d (GimpVector2 p,
920           gdouble     angle)
921 {
922   GimpVector2 ret;
923 
924   ret.x = cos (angle) * p.x-sin (angle) * p.y;
925   ret.y = sin (angle) * p.x+cos (angle) * p.y;
926 
927   return ret;
928 }
929 
930 static inline GimpVector2
lineintersect(GimpVector2 p1,GimpVector2 p2,GimpVector2 q1,GimpVector2 q2)931 lineintersect (GimpVector2 p1, GimpVector2 p2,
932                GimpVector2 q1, GimpVector2 q2)
933 {
934   gdouble     denom, u;
935   GimpVector2 p;
936 
937   denom = (q2.y - q1.y) * (p2.x - p1.x) - (q2.x - q1.x) * (p2.y - p1.y);
938   if (denom == 0.0)
939     {
940       p.x = (p1.x + p2.x + q1.x + q2.x) / 4;
941       p.y = (p1.y + p2.y + q1.y + q2.y) / 4;
942     }
943   else
944     {
945       u = (q2.x - q1.x) * (p1.y - q1.y) - (q2.y - q1.y) * (p1.x - q1.x);
946       u /= denom;
947 
948       p.x = p1.x + u * (p2.x - p1.x);
949       p.y = p1.y + u * (p2.y - p1.y);
950     }
951 
952   return p;
953 }
954 
955 static inline GimpVector2
get_pivot_delta(GimpToolTransformGrid * grid,GimpVector2 * oldpos,GimpVector2 * newpos,GimpVector2 pivot)956 get_pivot_delta (GimpToolTransformGrid *grid,
957                  GimpVector2           *oldpos,
958                  GimpVector2           *newpos,
959                  GimpVector2            pivot)
960 {
961   GimpToolTransformGridPrivate *private = grid->private;
962   GimpMatrix3                   transform_before;
963   GimpMatrix3                   transform_after;
964   GimpVector2                   delta;
965 
966   gimp_matrix3_identity (&transform_before);
967   gimp_matrix3_identity (&transform_after);
968 
969   gimp_transform_matrix_perspective (&transform_before,
970                                      private->x1,
971                                      private->y1,
972                                      private->x2 - private->x1,
973                                      private->y2 - private->y1,
974                                      oldpos[0].x, oldpos[0].y,
975                                      oldpos[1].x, oldpos[1].y,
976                                      oldpos[2].x, oldpos[2].y,
977                                      oldpos[3].x, oldpos[3].y);
978   gimp_transform_matrix_perspective (&transform_after,
979                                      private->x1,
980                                      private->y1,
981                                      private->x2 - private->x1,
982                                      private->y2 - private->y1,
983                                      newpos[0].x, newpos[0].y,
984                                      newpos[1].x, newpos[1].y,
985                                      newpos[2].x, newpos[2].y,
986                                      newpos[3].x, newpos[3].y);
987   gimp_matrix3_invert (&transform_before);
988   gimp_matrix3_mult (&transform_after, &transform_before);
989   gimp_matrix3_transform_point (&transform_before,
990                                 pivot.x, pivot.y, &delta.x, &delta.y);
991 
992   delta = vectorsubtract (delta, pivot);
993 
994   return delta;
995 }
996 
997 static gboolean
point_is_inside_polygon(gint n,gdouble * x,gdouble * y,gdouble px,gdouble py)998 point_is_inside_polygon (gint     n,
999                          gdouble *x,
1000                          gdouble *y,
1001                          gdouble  px,
1002                          gdouble  py)
1003 {
1004   gint     i, j;
1005   gboolean odd = FALSE;
1006 
1007   for (i = 0, j = n - 1; i < n; j = i++)
1008     {
1009       if ((y[i] < py && y[j] >= py) ||
1010           (y[j] < py && y[i] >= py))
1011         {
1012           if (x[i] + (py - y[i]) / (y[j] - y[i]) * (x[j] - x[i]) < px)
1013             odd = !odd;
1014         }
1015     }
1016 
1017   return odd;
1018 }
1019 
1020 static gboolean
point_is_inside_polygon_pos(GimpVector2 * pos,GimpVector2 point)1021 point_is_inside_polygon_pos (GimpVector2 *pos,
1022                              GimpVector2  point)
1023 {
1024   return point_is_inside_polygon (4,
1025                                   (gdouble[4]){ pos[0].x, pos[1].x,
1026                                                 pos[3].x, pos[2].x },
1027                                   (gdouble[4]){ pos[0].y, pos[1].y,
1028                                                 pos[3].y, pos[2].y },
1029                                   point.x, point.y);
1030 }
1031 
1032 static void
get_handle_geometry(GimpToolTransformGrid * grid,GimpVector2 * position,gdouble * angle)1033 get_handle_geometry (GimpToolTransformGrid *grid,
1034                      GimpVector2           *position,
1035                      gdouble               *angle)
1036 {
1037   GimpToolTransformGridPrivate *private = grid->private;
1038 
1039   GimpVector2 o[] = { { .x = private->tx1, .y = private->ty1 },
1040                       { .x = private->tx2, .y = private->ty2 },
1041                       { .x = private->tx3, .y = private->ty3 },
1042                       { .x = private->tx4, .y = private->ty4 } };
1043   GimpVector2 right = { .x = 1.0, .y = 0.0 };
1044   GimpVector2 up    = { .x = 0.0, .y = 1.0 };
1045 
1046   if (position)
1047     {
1048       position[0] = o[0];
1049       position[1] = o[1];
1050       position[2] = o[2];
1051       position[3] = o[3];
1052     }
1053 
1054   angle[0] = calcangle (vectorsubtract (o[1], o[0]), right);
1055   angle[1] = calcangle (vectorsubtract (o[3], o[2]), right);
1056   angle[2] = calcangle (vectorsubtract (o[3], o[1]), up);
1057   angle[3] = calcangle (vectorsubtract (o[2], o[0]), up);
1058 
1059   angle[4] = (angle[0] + angle[3]) / 2.0;
1060   angle[5] = (angle[0] + angle[2]) / 2.0;
1061   angle[6] = (angle[1] + angle[3]) / 2.0;
1062   angle[7] = (angle[1] + angle[2]) / 2.0;
1063 
1064   angle[8] = (angle[0] + angle[1] + angle[2] + angle[3]) / 4.0;
1065 }
1066 
1067 static void
gimp_tool_transform_grid_changed(GimpToolWidget * widget)1068 gimp_tool_transform_grid_changed (GimpToolWidget *widget)
1069 {
1070   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
1071   GimpToolTransformGridPrivate *private = grid->private;
1072   gdouble                       angle[9];
1073   GimpVector2                   o[4], t[4];
1074   gint                          handle_w;
1075   gint                          handle_h;
1076   gint                          d, i;
1077 
1078   gimp_tool_transform_grid_update_box (grid);
1079 
1080   gimp_canvas_transform_guides_set (private->guides,
1081                                     &private->transform,
1082                                     private->x1,
1083                                     private->y1,
1084                                     private->x2,
1085                                     private->y2,
1086                                     private->guide_type,
1087                                     private->n_guides,
1088                                     private->clip_guides);
1089   gimp_canvas_item_set_visible (private->guides, private->show_guides);
1090 
1091   get_handle_geometry (grid, o, angle);
1092   gimp_tool_transform_grid_calc_handles (grid, &handle_w, &handle_h);
1093 
1094   for (i = 0; i < 4; i++)
1095     {
1096       GimpCanvasItem *h;
1097       gdouble         factor;
1098 
1099       /*  the scale handles  */
1100       factor = 1.0;
1101       if (private->use_perspective_handles)
1102         factor = 1.5;
1103 
1104       h = private->handles[GIMP_TRANSFORM_HANDLE_NW + i];
1105       gimp_canvas_item_set_visible (h, private->use_corner_handles);
1106 
1107       if (private->use_corner_handles)
1108         {
1109           gimp_canvas_handle_set_position (h, o[i].x, o[i].y);
1110           gimp_canvas_handle_set_size (h, handle_w * factor, handle_h * factor);
1111           gimp_canvas_handle_set_angles (h, angle[i + 4], 0.0);
1112         }
1113 
1114       /*  the perspective handles  */
1115       factor = 1.0;
1116       if (private->use_corner_handles)
1117         factor = 0.8;
1118 
1119       h = private->handles[GIMP_TRANSFORM_HANDLE_NW_P + i];
1120       gimp_canvas_item_set_visible (h, private->use_perspective_handles);
1121 
1122       if (private->use_perspective_handles)
1123         {
1124           gimp_canvas_handle_set_position (h, o[i].x, o[i].y);
1125           gimp_canvas_handle_set_size (h, handle_w * factor, handle_h * factor);
1126           gimp_canvas_handle_set_angles (h, angle[i + 4], 0.0);
1127         }
1128     }
1129 
1130   /*  draw the side handles  */
1131   t[0] = scalemult (vectoradd (o[0], o[1]), 0.5);
1132   t[1] = scalemult (vectoradd (o[2], o[3]), 0.5);
1133   t[2] = scalemult (vectoradd (o[1], o[3]), 0.5);
1134   t[3] = scalemult (vectoradd (o[2], o[0]), 0.5);
1135 
1136   for (i = 0; i < 4; i++)
1137     {
1138       GimpCanvasItem *h;
1139 
1140       h = private->handles[GIMP_TRANSFORM_HANDLE_N + i];
1141       gimp_canvas_item_set_visible (h, private->use_side_handles);
1142 
1143       if (private->use_side_handles)
1144         {
1145           gimp_canvas_handle_set_position (h, t[i].x, t[i].y);
1146           gimp_canvas_handle_set_size (h, handle_w, handle_h);
1147           gimp_canvas_handle_set_angles (h, angle[i], 0.0);
1148         }
1149     }
1150 
1151   /*  draw the shear handles  */
1152   t[0] = scalemult (vectoradd (           o[0]      , scalemult (o[1], 3.0)),
1153                     0.25);
1154   t[1] = scalemult (vectoradd (scalemult (o[2], 3.0),            o[3]      ),
1155                     0.25);
1156   t[2] = scalemult (vectoradd (           o[1]      , scalemult (o[3], 3.0)),
1157                     0.25);
1158   t[3] = scalemult (vectoradd (scalemult (o[0], 3.0),            o[2]      ),
1159                     0.25);
1160 
1161   for (i = 0; i < 4; i++)
1162     {
1163       GimpCanvasItem *h;
1164 
1165       h = private->handles[GIMP_TRANSFORM_HANDLE_N_S + i];
1166       gimp_canvas_item_set_visible (h, private->use_shear_handles);
1167 
1168       if (private->use_shear_handles)
1169         {
1170           gimp_canvas_handle_set_position (h, t[i].x, t[i].y);
1171           gimp_canvas_handle_set_size (h, handle_w, handle_h);
1172           gimp_canvas_handle_set_angles (h, angle[i], 0.0);
1173         }
1174     }
1175 
1176   d = MIN (handle_w, handle_h);
1177   if (private->use_center_handle)
1178     d *= 2; /* so you can grab it from under the center handle */
1179 
1180   gimp_canvas_item_set_visible (private->handles[GIMP_TRANSFORM_HANDLE_PIVOT],
1181                                 private->use_pivot_handle);
1182 
1183   if (private->use_pivot_handle)
1184     {
1185       gimp_canvas_handle_set_position (private->pivot_items[0],
1186                                        private->tpx, private->tpy);
1187       gimp_canvas_handle_set_size (private->pivot_items[0], d, d);
1188 
1189       gimp_canvas_handle_set_position (private->pivot_items[1],
1190                                        private->tpx, private->tpy);
1191       gimp_canvas_handle_set_size (private->pivot_items[1], d, d);
1192     }
1193 
1194   d = MIN (handle_w, handle_h);
1195 
1196   gimp_canvas_item_set_visible (private->handles[GIMP_TRANSFORM_HANDLE_CENTER],
1197                                 private->use_center_handle);
1198 
1199   if (private->use_center_handle)
1200     {
1201       gimp_canvas_handle_set_position (private->center_items[0],
1202                                        private->tcx, private->tcy);
1203       gimp_canvas_handle_set_size (private->center_items[0], d, d);
1204       gimp_canvas_handle_set_angles (private->center_items[0], angle[8], 0.0);
1205 
1206       gimp_canvas_handle_set_position (private->center_items[1],
1207                                        private->tcx, private->tcy);
1208       gimp_canvas_handle_set_size (private->center_items[1], d, d);
1209       gimp_canvas_handle_set_angles (private->center_items[1], angle[8], 0.0);
1210     }
1211 
1212   gimp_tool_transform_grid_update_hilight (grid);
1213 }
1214 
1215 gint
gimp_tool_transform_grid_button_press(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type)1216 gimp_tool_transform_grid_button_press (GimpToolWidget      *widget,
1217                                        const GimpCoords    *coords,
1218                                        guint32              time,
1219                                        GdkModifierType      state,
1220                                        GimpButtonPressType  press_type)
1221 {
1222   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
1223   GimpToolTransformGridPrivate *private = grid->private;
1224 
1225   private->button_down = TRUE;
1226   private->mousex      = coords->x;
1227   private->mousey      = coords->y;
1228 
1229   if (private->handle != GIMP_TRANSFORM_HANDLE_NONE)
1230     {
1231       if (private->handles[private->handle])
1232         {
1233           GimpCanvasItem *handle;
1234           gdouble         x, y;
1235 
1236           switch (private->handle)
1237             {
1238             case GIMP_TRANSFORM_HANDLE_CENTER:
1239               handle = private->center_items[0];
1240               break;
1241 
1242             case GIMP_TRANSFORM_HANDLE_PIVOT:
1243               handle = private->pivot_items[0];
1244               break;
1245 
1246              default:
1247               handle = private->handles[private->handle];
1248               break;
1249             }
1250 
1251           gimp_canvas_handle_get_position (handle, &x, &y);
1252 
1253           gimp_tool_widget_set_snap_offsets (widget,
1254                                              SIGNED_ROUND (x - coords->x),
1255                                              SIGNED_ROUND (y - coords->y),
1256                                              0, 0);
1257         }
1258       else
1259         {
1260           gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0);
1261         }
1262 
1263       private->prev_tx1 = private->tx1;
1264       private->prev_ty1 = private->ty1;
1265       private->prev_tx2 = private->tx2;
1266       private->prev_ty2 = private->ty2;
1267       private->prev_tx3 = private->tx3;
1268       private->prev_ty3 = private->ty3;
1269       private->prev_tx4 = private->tx4;
1270       private->prev_ty4 = private->ty4;
1271       private->prev_tpx = private->tpx;
1272       private->prev_tpy = private->tpy;
1273       private->prev_tcx = private->tcx;
1274       private->prev_tcy = private->tcy;
1275 
1276       return private->handle;
1277     }
1278 
1279   gimp_tool_widget_set_snap_offsets (widget, 0, 0, 0, 0);
1280 
1281   return 0;
1282 }
1283 
1284 void
gimp_tool_transform_grid_button_release(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type)1285 gimp_tool_transform_grid_button_release (GimpToolWidget        *widget,
1286                                          const GimpCoords      *coords,
1287                                          guint32                time,
1288                                          GdkModifierType        state,
1289                                          GimpButtonReleaseType  release_type)
1290 {
1291   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
1292   GimpToolTransformGridPrivate *private = grid->private;
1293 
1294   private->button_down = FALSE;
1295 }
1296 
1297 void
gimp_tool_transform_grid_motion(GimpToolWidget * widget,const GimpCoords * coords,guint32 time,GdkModifierType state)1298 gimp_tool_transform_grid_motion (GimpToolWidget   *widget,
1299                                  const GimpCoords *coords,
1300                                  guint32           time,
1301                                  GdkModifierType   state)
1302 {
1303   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
1304   GimpToolTransformGridPrivate *private = grid->private;
1305   gdouble                      *x[4], *y[4];
1306   gdouble                      *newpivot_x, *newpivot_y;
1307 
1308   GimpVector2                  oldpos[5], newpos[4];
1309   GimpVector2                  cur   = { .x = coords->x,
1310                                          .y = coords->y };
1311   GimpVector2                  mouse = { .x = private->mousex,
1312                                          .y = private->mousey };
1313   GimpVector2                  d;
1314   GimpVector2                  pivot;
1315 
1316   gboolean                     fixedpivot = private->fixedpivot;
1317   GimpTransformHandle          handle     = private->handle;
1318   gint                         i;
1319 
1320   private->curx = coords->x;
1321   private->cury = coords->y;
1322 
1323   x[0] = &private->tx1;
1324   y[0] = &private->ty1;
1325   x[1] = &private->tx2;
1326   y[1] = &private->ty2;
1327   x[2] = &private->tx3;
1328   y[2] = &private->ty3;
1329   x[3] = &private->tx4;
1330   y[3] = &private->ty4;
1331 
1332   newpos[0].x = oldpos[0].x = private->prev_tx1;
1333   newpos[0].y = oldpos[0].y = private->prev_ty1;
1334   newpos[1].x = oldpos[1].x = private->prev_tx2;
1335   newpos[1].y = oldpos[1].y = private->prev_ty2;
1336   newpos[2].x = oldpos[2].x = private->prev_tx3;
1337   newpos[2].y = oldpos[2].y = private->prev_ty3;
1338   newpos[3].x = oldpos[3].x = private->prev_tx4;
1339   newpos[3].y = oldpos[3].y = private->prev_ty4;
1340 
1341   /* put center point in this array too */
1342   oldpos[4].x = private->prev_tcx;
1343   oldpos[4].y = private->prev_tcy;
1344 
1345   d = vectorsubtract (cur, mouse);
1346 
1347   newpivot_x = &private->tpx;
1348   newpivot_y = &private->tpy;
1349 
1350   if (private->use_pivot_handle)
1351     {
1352       pivot.x = private->prev_tpx;
1353       pivot.y = private->prev_tpy;
1354     }
1355   else
1356     {
1357       /* when the transform grid doesn't use a pivot handle, use the center
1358        * point as the pivot instead.
1359        */
1360       pivot.x = private->prev_tcx;
1361       pivot.y = private->prev_tcy;
1362 
1363       fixedpivot = TRUE;
1364     }
1365 
1366   /* move */
1367   if (handle == GIMP_TRANSFORM_HANDLE_CENTER)
1368     {
1369       if (private->constrain_move)
1370         {
1371           /* snap to 45 degree vectors from starting point */
1372           gdouble angle = 16.0 * calcangle ((GimpVector2) { 1.0, 0.0 },
1373                                             d) / (2.0 * G_PI);
1374           gdouble dist  = norm (d) / sqrt (2);
1375 
1376           if (angle < 1.0 || angle >= 15.0)
1377             d.y = 0;
1378           else if (angle < 3.0)
1379             d.y = -(d.x = dist);
1380           else if (angle < 5.0)
1381             d.x = 0;
1382           else if (angle < 7.0)
1383             d.x = d.y = -dist;
1384           else if (angle < 9.0)
1385             d.y = 0;
1386           else if (angle < 11.0)
1387             d.x = -(d.y = dist);
1388           else if (angle < 13.0)
1389             d.x = 0;
1390           else if (angle < 15.0)
1391             d.x = d.y = dist;
1392         }
1393 
1394       for (i = 0; i < 4; i++)
1395         newpos[i] = vectoradd (oldpos[i], d);
1396     }
1397 
1398   /* rotate */
1399   if (handle == GIMP_TRANSFORM_HANDLE_ROTATION)
1400     {
1401       gdouble angle = calcangle (vectorsubtract (cur, pivot),
1402                                  vectorsubtract (mouse, pivot));
1403 
1404       if (private->constrain_rotate)
1405         {
1406           /* round to 15 degree multiple */
1407           angle /= 2 * G_PI / 24.0;
1408           angle = round (angle);
1409           angle *= 2 * G_PI / 24.0;
1410         }
1411 
1412       for (i = 0; i < 4; i++)
1413         newpos[i] = vectoradd (pivot,
1414                                rotate2d (vectorsubtract (oldpos[i], pivot),
1415                                          angle));
1416 
1417       fixedpivot = TRUE;
1418     }
1419 
1420   /* move rotation axis */
1421   if (handle == GIMP_TRANSFORM_HANDLE_PIVOT)
1422     {
1423       pivot = vectoradd (pivot, d);
1424 
1425       if (private->cornersnap)
1426         {
1427           /* snap to corner points and center */
1428           gint    closest = 0;
1429           gdouble closest_dist = G_MAXDOUBLE, dist;
1430 
1431           for (i = 0; i < 5; i++)
1432             {
1433               dist = norm (vectorsubtract (pivot, oldpos[i]));
1434               if (dist < closest_dist)
1435                 {
1436                   closest_dist = dist;
1437                   closest = i;
1438                 }
1439             }
1440 
1441           if (closest_dist *
1442               gimp_tool_widget_get_shell (widget)->scale_x < 50)
1443             {
1444               pivot = oldpos[closest];
1445             }
1446         }
1447 
1448       fixedpivot = TRUE;
1449     }
1450 
1451   /* scaling via corner */
1452   if (handle == GIMP_TRANSFORM_HANDLE_NW ||
1453       handle == GIMP_TRANSFORM_HANDLE_NE ||
1454       handle == GIMP_TRANSFORM_HANDLE_SE ||
1455       handle == GIMP_TRANSFORM_HANDLE_SW)
1456     {
1457       /* Scaling through scale handles means translating one corner point,
1458        * with all sides at constant angles.
1459        */
1460 
1461       gint this, left, right, opposite;
1462 
1463       /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */
1464       if (handle == GIMP_TRANSFORM_HANDLE_NW)
1465         {
1466           this = 0; left = 1; right = 2; opposite = 3;
1467         }
1468       else if (handle == GIMP_TRANSFORM_HANDLE_NE)
1469         {
1470           this = 1; left = 3; right = 0; opposite = 2;
1471         }
1472       else if (handle == GIMP_TRANSFORM_HANDLE_SW)
1473         {
1474           this = 2; left = 0; right = 3; opposite = 1;
1475         }
1476       else if (handle == GIMP_TRANSFORM_HANDLE_SE)
1477         {
1478           this = 3; left = 2; right = 1; opposite = 0;
1479         }
1480       else
1481         gimp_assert_not_reached ();
1482 
1483       /* when the keep aspect transformation constraint is enabled,
1484        * the translation shall only be along the diagonal that runs
1485        * trough this corner point.
1486        */
1487       if (private->constrain_scale)
1488         {
1489           /* restrict to movement along the diagonal */
1490           GimpVector2 diag = vectorsubtract (oldpos[this], oldpos[opposite]);
1491 
1492           d = vectorproject (d, diag);
1493         }
1494 
1495       /* Move the corner being interacted with */
1496       /*    rp---------tp
1497        *   /           /\ <- d, the interaction vector
1498        *  /           /  tp
1499        * op----------/
1500        *
1501        */
1502       newpos[this] = vectoradd (oldpos[this], d);
1503 
1504       /* Where the corner to the right and left would go, need these to form
1505        * lines to intersect with the sides */
1506       /*    rp----------/
1507        *   /\          /\
1508        *  /  nr       /  nt
1509        * op----------lp
1510        *              \
1511        *               nl
1512        */
1513 
1514       newpos[right] = vectoradd (oldpos[right], d);
1515       newpos[left]  = vectoradd (oldpos[left], d);
1516 
1517       /* Now we just need to find the intersection of op-rp and nr-nt.
1518        *    rp----------/
1519        *   /           /
1520        *  /  nr==========nt
1521        * op----------/
1522        *
1523        */
1524       newpos[right] = lineintersect (newpos[right], newpos[this],
1525                                      oldpos[opposite], oldpos[right]);
1526       newpos[left]  = lineintersect (newpos[left], newpos[this],
1527                                      oldpos[opposite], oldpos[left]);
1528       /*    /-----------/
1529        *   /           /
1530        *  rp============nt
1531        * op----------/
1532        *
1533        */
1534 
1535       /*
1536        *
1537        *  /--------------/
1538        * /--------------/
1539        *
1540        */
1541 
1542       if (private->frompivot_scale &&
1543           transform_is_convex (newpos) &&
1544           transform_is_convex (oldpos))
1545         {
1546           /* transform the pivot point before the interaction and
1547            * after, and move everything by this difference
1548            */
1549           //TODO the handle doesn't actually end up where the mouse cursor is
1550           GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
1551           for (i = 0; i < 4; i++)
1552             newpos[i] = vectorsubtract (newpos[i], delta);
1553 
1554           fixedpivot = TRUE;
1555         }
1556     }
1557 
1558   /* scaling via sides */
1559   if (handle == GIMP_TRANSFORM_HANDLE_N ||
1560       handle == GIMP_TRANSFORM_HANDLE_E ||
1561       handle == GIMP_TRANSFORM_HANDLE_S ||
1562       handle == GIMP_TRANSFORM_HANDLE_W)
1563     {
1564       gint        this_l, this_r, opp_l, opp_r;
1565       GimpVector2 side_l, side_r, midline;
1566 
1567       /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */
1568       if (handle == GIMP_TRANSFORM_HANDLE_N)
1569         {
1570           this_l = 1; this_r = 0;
1571         }
1572       else if (handle == GIMP_TRANSFORM_HANDLE_E)
1573         {
1574           this_l = 3; this_r = 1;
1575         }
1576       else if (handle == GIMP_TRANSFORM_HANDLE_S)
1577         {
1578           this_l = 2; this_r = 3;
1579         }
1580       else if (handle == GIMP_TRANSFORM_HANDLE_W)
1581         {
1582           this_l = 0; this_r = 2;
1583         }
1584       else
1585         gimp_assert_not_reached ();
1586 
1587       opp_l = 3 - this_r; opp_r = 3 - this_l;
1588 
1589       side_l = vectorsubtract (oldpos[opp_l], oldpos[this_l]);
1590       side_r = vectorsubtract (oldpos[opp_r], oldpos[this_r]);
1591       midline = vectoradd (side_l, side_r);
1592 
1593       /* restrict to movement along the midline */
1594       d = vectorproject (d, midline);
1595 
1596       if (private->constrain_scale)
1597         {
1598           GimpVector2 before, after, effective_pivot = pivot;
1599           gdouble     distance;
1600 
1601           if (! private->frompivot_scale)
1602             {
1603               /* center of the opposite side is pivot */
1604               effective_pivot = scalemult (vectoradd (oldpos[opp_l],
1605                                                       oldpos[opp_r]), 0.5);
1606             }
1607 
1608           /* get the difference between the distance from the pivot to
1609            * where interaction started and the distance from the pivot
1610            * to where cursor is now, and scale all corners distance
1611            * from the pivot with this factor
1612            */
1613           before = vectorsubtract (effective_pivot, mouse);
1614           after = vectorsubtract (effective_pivot, cur);
1615           after = vectorproject (after, before);
1616 
1617           distance = 0.5 * (after.x / before.x + after.y / before.y);
1618 
1619           for (i = 0; i < 4; i++)
1620             newpos[i] = vectoradd (effective_pivot,
1621                                    scalemult (vectorsubtract (oldpos[i],
1622                                                               effective_pivot),
1623                                               distance));
1624         }
1625       else
1626         {
1627           /* just move the side */
1628           newpos[this_l] = vectoradd (oldpos[this_l], d);
1629           newpos[this_r] = vectoradd (oldpos[this_r], d);
1630         }
1631 
1632       if (! private->constrain_scale   &&
1633           private->frompivot_scale     &&
1634           transform_is_convex (newpos) &&
1635           transform_is_convex (oldpos))
1636         {
1637           GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
1638           for (i = 0; i < 4; i++)
1639             newpos[i] = vectorsubtract (newpos[i], delta);
1640 
1641           fixedpivot = TRUE;
1642         }
1643     }
1644 
1645   /* shear */
1646   if (handle == GIMP_TRANSFORM_HANDLE_N_S ||
1647       handle == GIMP_TRANSFORM_HANDLE_E_S ||
1648       handle == GIMP_TRANSFORM_HANDLE_S_S ||
1649       handle == GIMP_TRANSFORM_HANDLE_W_S)
1650     {
1651       gint this_l, this_r;
1652 
1653       /* set up indices for this edge and the opposite edge */
1654       if (handle == GIMP_TRANSFORM_HANDLE_N_S)
1655         {
1656           this_l = 1; this_r = 0;
1657         }
1658       else if (handle == GIMP_TRANSFORM_HANDLE_W_S)
1659         {
1660           this_l = 0; this_r = 2;
1661         }
1662       else if (handle == GIMP_TRANSFORM_HANDLE_S_S)
1663         {
1664           this_l = 2; this_r = 3;
1665         }
1666       else if (handle == GIMP_TRANSFORM_HANDLE_E_S)
1667         {
1668           this_l = 3; this_r = 1;
1669         }
1670       else
1671         gimp_assert_not_reached ();
1672 
1673       if (private->constrain_shear)
1674         {
1675           /* restrict to movement along the side */
1676           GimpVector2 side = vectorsubtract (oldpos[this_r], oldpos[this_l]);
1677 
1678           d = vectorproject (d, side);
1679         }
1680 
1681       newpos[this_l] = vectoradd (oldpos[this_l], d);
1682       newpos[this_r] = vectoradd (oldpos[this_r], d);
1683 
1684       if (private->frompivot_shear     &&
1685           transform_is_convex (newpos) &&
1686           transform_is_convex (oldpos))
1687         {
1688           GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
1689           for (i = 0; i < 4; i++)
1690             newpos[i] = vectorsubtract (newpos[i], delta);
1691 
1692           fixedpivot = TRUE;
1693         }
1694     }
1695 
1696   /* perspective transform */
1697   if (handle == GIMP_TRANSFORM_HANDLE_NW_P ||
1698       handle == GIMP_TRANSFORM_HANDLE_NE_P ||
1699       handle == GIMP_TRANSFORM_HANDLE_SE_P ||
1700       handle == GIMP_TRANSFORM_HANDLE_SW_P)
1701     {
1702       gint this, left, right, opposite;
1703 
1704       /* 0: northwest, 1: northeast, 2: southwest, 3: southeast */
1705       if (handle == GIMP_TRANSFORM_HANDLE_NW_P)
1706         {
1707           this = 0; left = 1; right = 2; opposite = 3;
1708         }
1709       else if (handle == GIMP_TRANSFORM_HANDLE_NE_P)
1710         {
1711           this = 1; left = 3; right = 0; opposite = 2;
1712         }
1713       else if (handle == GIMP_TRANSFORM_HANDLE_SW_P)
1714         {
1715           this = 2; left = 0; right = 3; opposite = 1;
1716         }
1717       else if (handle == GIMP_TRANSFORM_HANDLE_SE_P)
1718         {
1719           this = 3; left = 2; right = 1; opposite = 0;
1720         }
1721       else
1722         gimp_assert_not_reached ();
1723 
1724       if (private->constrain_perspective)
1725         {
1726           /* when the constrain transformation constraint is enabled,
1727            * the translation shall only be either along the side
1728            * angles of the two sides that run to this corner point, or
1729            * along the diagonal that runs trough this corner point.
1730            */
1731           GimpVector2 proj[4];
1732           gdouble     rej[4];
1733 
1734           for (i = 0; i < 4; i++)
1735             {
1736               if (i == this)
1737                 continue;
1738 
1739               /* get the vectors along the sides and the diagonal */
1740               proj[i] = vectorsubtract (oldpos[this], oldpos[i]);
1741 
1742               /* project d on each candidate vector and see which has
1743                * the shortest rejection
1744                */
1745               proj[i] = vectorproject (d, proj[i]);
1746               rej[i] = norm (vectorsubtract (d, proj[i]));
1747             }
1748 
1749           if (rej[left] < rej[right] && rej[left] < rej[opposite])
1750             d = proj[left];
1751           else if (rej[right] < rej[opposite])
1752             d = proj[right];
1753           else
1754             d = proj[opposite];
1755         }
1756 
1757       newpos[this] = vectoradd (oldpos[this], d);
1758 
1759       if (private->frompivot_perspective &&
1760           transform_is_convex (newpos)   &&
1761           transform_is_convex (oldpos))
1762         {
1763           GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
1764 
1765           for (i = 0; i < 4; i++)
1766             newpos[i] = vectorsubtract (newpos[i], delta);
1767 
1768           fixedpivot = TRUE;
1769         }
1770     }
1771 
1772   /* this will have been set to TRUE if an operation used the pivot in
1773    * addition to being a user option
1774    */
1775   if (! fixedpivot                 &&
1776       transform_is_convex (newpos) &&
1777       transform_is_convex (oldpos) &&
1778       point_is_inside_polygon_pos (oldpos, pivot))
1779     {
1780       GimpVector2 delta = get_pivot_delta (grid, oldpos, newpos, pivot);
1781       pivot = vectoradd (pivot, delta);
1782     }
1783 
1784   /* make sure the new coordinates are valid */
1785   for (i = 0; i < 4; i++)
1786     {
1787       if (! isfinite (newpos[i].x) || ! isfinite (newpos[i].y))
1788         return;
1789     }
1790 
1791   if (! isfinite (pivot.x) || ! isfinite (pivot.y))
1792     return;
1793 
1794   for (i = 0; i < 4; i++)
1795     {
1796       *x[i] = newpos[i].x;
1797       *y[i] = newpos[i].y;
1798     }
1799 
1800   /* set unconditionally: if options get toggled during operation, we
1801    * have to move pivot back
1802    */
1803   *newpivot_x = pivot.x;
1804   *newpivot_y = pivot.y;
1805 
1806   gimp_tool_transform_grid_update_matrix (grid);
1807 }
1808 
1809 static const gchar *
get_friendly_operation_name(GimpTransformHandle handle)1810 get_friendly_operation_name (GimpTransformHandle handle)
1811 {
1812   switch (handle)
1813     {
1814     case GIMP_TRANSFORM_HANDLE_NONE:
1815       return "";
1816     case GIMP_TRANSFORM_HANDLE_NW_P:
1817     case GIMP_TRANSFORM_HANDLE_NE_P:
1818     case GIMP_TRANSFORM_HANDLE_SW_P:
1819     case GIMP_TRANSFORM_HANDLE_SE_P:
1820       return _("Click-Drag to change perspective");
1821     case GIMP_TRANSFORM_HANDLE_NW:
1822     case GIMP_TRANSFORM_HANDLE_NE:
1823     case GIMP_TRANSFORM_HANDLE_SW:
1824     case GIMP_TRANSFORM_HANDLE_SE:
1825       return _("Click-Drag to scale");
1826     case GIMP_TRANSFORM_HANDLE_N:
1827     case GIMP_TRANSFORM_HANDLE_S:
1828     case GIMP_TRANSFORM_HANDLE_E:
1829     case GIMP_TRANSFORM_HANDLE_W:
1830       return _("Click-Drag to scale");
1831     case GIMP_TRANSFORM_HANDLE_CENTER:
1832       return _("Click-Drag to move");
1833     case GIMP_TRANSFORM_HANDLE_PIVOT:
1834       return _("Click-Drag to move the pivot point");
1835     case GIMP_TRANSFORM_HANDLE_N_S:
1836     case GIMP_TRANSFORM_HANDLE_S_S:
1837     case GIMP_TRANSFORM_HANDLE_E_S:
1838     case GIMP_TRANSFORM_HANDLE_W_S:
1839       return _("Click-Drag to shear");
1840     case GIMP_TRANSFORM_HANDLE_ROTATION:
1841       return _("Click-Drag to rotate");
1842     default:
1843       gimp_assert_not_reached ();
1844     }
1845 }
1846 
1847 static GimpTransformHandle
gimp_tool_transform_get_area_handle(GimpToolTransformGrid * grid,const GimpCoords * coords,GimpTransformFunction function)1848 gimp_tool_transform_get_area_handle (GimpToolTransformGrid *grid,
1849                                      const GimpCoords      *coords,
1850                                      GimpTransformFunction  function)
1851 {
1852   GimpToolTransformGridPrivate *private = grid->private;
1853   GimpTransformHandle           handle  = GIMP_TRANSFORM_HANDLE_NONE;
1854 
1855   switch (function)
1856     {
1857     case GIMP_TRANSFORM_FUNCTION_NONE:
1858       break;
1859 
1860     case GIMP_TRANSFORM_FUNCTION_MOVE:
1861       handle = GIMP_TRANSFORM_HANDLE_CENTER;
1862       break;
1863 
1864     case GIMP_TRANSFORM_FUNCTION_ROTATE:
1865       handle = GIMP_TRANSFORM_HANDLE_ROTATION;
1866       break;
1867 
1868     case GIMP_TRANSFORM_FUNCTION_SCALE:
1869     case GIMP_TRANSFORM_FUNCTION_PERSPECTIVE:
1870       {
1871         gdouble closest_dist;
1872         gdouble dist;
1873 
1874         dist = gimp_canvas_item_transform_distance_square (private->guides,
1875                                                            coords->x, coords->y,
1876                                                            private->tx1,
1877                                                            private->ty1);
1878         closest_dist = dist;
1879         if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE)
1880           handle = GIMP_TRANSFORM_HANDLE_NW_P;
1881         else
1882           handle = GIMP_TRANSFORM_HANDLE_NW;
1883 
1884         dist = gimp_canvas_item_transform_distance_square (private->guides,
1885                                                            coords->x, coords->y,
1886                                                            private->tx2,
1887                                                            private->ty2);
1888         if (dist < closest_dist)
1889           {
1890             closest_dist = dist;
1891             if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE)
1892               handle = GIMP_TRANSFORM_HANDLE_NE_P;
1893             else
1894               handle = GIMP_TRANSFORM_HANDLE_NE;
1895           }
1896 
1897         dist = gimp_canvas_item_transform_distance_square (private->guides,
1898                                                            coords->x, coords->y,
1899                                                            private->tx3,
1900                                                            private->ty3);
1901         if (dist < closest_dist)
1902           {
1903             closest_dist = dist;
1904             if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE)
1905               handle = GIMP_TRANSFORM_HANDLE_SW_P;
1906             else
1907               handle = GIMP_TRANSFORM_HANDLE_SW;
1908           }
1909 
1910         dist = gimp_canvas_item_transform_distance_square (private->guides,
1911                                                            coords->x, coords->y,
1912                                                            private->tx4,
1913                                                            private->ty4);
1914         if (dist < closest_dist)
1915           {
1916             closest_dist = dist;
1917             if (function == GIMP_TRANSFORM_FUNCTION_PERSPECTIVE)
1918               handle = GIMP_TRANSFORM_HANDLE_SE_P;
1919             else
1920               handle = GIMP_TRANSFORM_HANDLE_SE;
1921           }
1922       }
1923       break;
1924 
1925     case GIMP_TRANSFORM_FUNCTION_SHEAR:
1926       {
1927         gdouble handle_x;
1928         gdouble handle_y;
1929         gdouble closest_dist;
1930         gdouble dist;
1931 
1932         gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_N],
1933                                          &handle_x, &handle_y);
1934         dist = gimp_canvas_item_transform_distance_square (private->guides,
1935                                                            coords->x, coords->y,
1936                                                            handle_x, handle_y);
1937         closest_dist = dist;
1938         handle = GIMP_TRANSFORM_HANDLE_N_S;
1939 
1940         gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_W],
1941                                          &handle_x, &handle_y);
1942         dist = gimp_canvas_item_transform_distance_square (private->guides,
1943                                                            coords->x, coords->y,
1944                                                            handle_x, handle_y);
1945         if (dist < closest_dist)
1946           {
1947             closest_dist = dist;
1948             handle = GIMP_TRANSFORM_HANDLE_W_S;
1949           }
1950 
1951         gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_E],
1952                                          &handle_x, &handle_y);
1953         dist = gimp_canvas_item_transform_distance_square (private->guides,
1954                                                            coords->x, coords->y,
1955                                                            handle_x, handle_y);
1956         if (dist < closest_dist)
1957           {
1958             closest_dist = dist;
1959             handle = GIMP_TRANSFORM_HANDLE_E_S;
1960           }
1961 
1962         gimp_canvas_handle_get_position (private->handles[GIMP_TRANSFORM_HANDLE_S],
1963                                          &handle_x, &handle_y);
1964         dist = gimp_canvas_item_transform_distance_square (private->guides,
1965                                                            coords->x, coords->y,
1966                                                            handle_x, handle_y);
1967         if (dist < closest_dist)
1968           {
1969             closest_dist = dist;
1970             handle = GIMP_TRANSFORM_HANDLE_S_S;
1971           }
1972       }
1973       break;
1974     }
1975 
1976   return handle;
1977 }
1978 
1979 GimpHit
gimp_tool_transform_grid_hit(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,gboolean proximity)1980 gimp_tool_transform_grid_hit (GimpToolWidget   *widget,
1981                               const GimpCoords *coords,
1982                               GdkModifierType   state,
1983                               gboolean          proximity)
1984 {
1985   GimpToolTransformGrid *grid = GIMP_TOOL_TRANSFORM_GRID (widget);
1986   GimpTransformHandle    handle;
1987 
1988   handle = gimp_tool_transform_grid_get_handle_for_coords (grid, coords);
1989 
1990   if (handle != GIMP_TRANSFORM_HANDLE_NONE)
1991     return GIMP_HIT_DIRECT;
1992 
1993   return GIMP_HIT_INDIRECT;
1994 }
1995 
1996 void
gimp_tool_transform_grid_hover(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,gboolean proximity)1997 gimp_tool_transform_grid_hover (GimpToolWidget   *widget,
1998                                 const GimpCoords *coords,
1999                                 GdkModifierType   state,
2000                                 gboolean          proximity)
2001 {
2002   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
2003   GimpToolTransformGridPrivate *private = grid->private;
2004   GimpTransformHandle           handle;
2005 
2006   handle = gimp_tool_transform_grid_get_handle_for_coords (grid, coords);
2007 
2008   if (handle == GIMP_TRANSFORM_HANDLE_NONE)
2009     {
2010       /* points passed in clockwise order */
2011       if (point_is_inside_polygon (4,
2012                                    (gdouble[4]){ private->tx1, private->tx2,
2013                                                  private->tx4, private->tx3 },
2014                                    (gdouble[4]){ private->ty1, private->ty2,
2015                                                  private->ty4, private->ty3 },
2016                                    coords->x, coords->y))
2017         {
2018           handle = gimp_tool_transform_get_area_handle (grid, coords,
2019                                                         private->inside_function);
2020         }
2021       else
2022         {
2023           handle = gimp_tool_transform_get_area_handle (grid, coords,
2024                                                         private->outside_function);
2025         }
2026     }
2027 
2028   if (handle != GIMP_TRANSFORM_HANDLE_NONE && proximity)
2029     {
2030       gimp_tool_widget_set_status (widget,
2031                                    get_friendly_operation_name (handle));
2032     }
2033   else
2034     {
2035       gimp_tool_widget_set_status (widget, NULL);
2036     }
2037 
2038   private->handle = handle;
2039 
2040   gimp_tool_transform_grid_update_hilight (grid);
2041 }
2042 
2043 void
gimp_tool_transform_grid_leave_notify(GimpToolWidget * widget)2044 gimp_tool_transform_grid_leave_notify (GimpToolWidget *widget)
2045 {
2046   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
2047   GimpToolTransformGridPrivate *private = grid->private;
2048 
2049   private->handle = GIMP_TRANSFORM_HANDLE_NONE;
2050 
2051   gimp_tool_transform_grid_update_hilight (grid);
2052 
2053   GIMP_TOOL_WIDGET_CLASS (parent_class)->leave_notify (widget);
2054 }
2055 
2056 static void
gimp_tool_transform_grid_modifier(GimpToolWidget * widget,GdkModifierType key)2057 gimp_tool_transform_grid_modifier (GimpToolWidget  *widget,
2058                                    GdkModifierType  key)
2059 {
2060   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
2061   GimpToolTransformGridPrivate *private = grid->private;
2062 
2063   if (key == gimp_get_constrain_behavior_mask ())
2064     {
2065       g_object_set (widget,
2066                     "frompivot-scale",       ! private->frompivot_scale,
2067                     "frompivot-shear",       ! private->frompivot_shear,
2068                     "frompivot-perspective", ! private->frompivot_perspective,
2069                     NULL);
2070     }
2071   else if (key == gimp_get_extend_selection_mask ())
2072     {
2073       g_object_set (widget,
2074                     "cornersnap",            ! private->cornersnap,
2075                     "constrain-move",        ! private->constrain_move,
2076                     "constrain-scale",       ! private->constrain_scale,
2077                     "constrain-rotate",      ! private->constrain_rotate,
2078                     "constrain-shear",       ! private->constrain_shear,
2079                     "constrain-perspective", ! private->constrain_perspective,
2080                     NULL);
2081     }
2082 }
2083 
2084 static void
gimp_tool_transform_grid_hover_modifier(GimpToolWidget * widget,GdkModifierType key,gboolean press,GdkModifierType state)2085 gimp_tool_transform_grid_hover_modifier (GimpToolWidget  *widget,
2086                                          GdkModifierType  key,
2087                                          gboolean         press,
2088                                          GdkModifierType  state)
2089 {
2090   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
2091   GimpToolTransformGridPrivate *private = grid->private;
2092   GimpCoords                    coords  = { 0.0, };
2093 
2094   gimp_tool_transform_grid_modifier (widget, key);
2095 
2096   if (private->button_down)
2097     {
2098       /*  send a non-motion to update the grid with the new constraints  */
2099       coords.x = private->curx;
2100       coords.y = private->cury;
2101       gimp_tool_transform_grid_motion (widget, &coords, 0, state);
2102     }
2103 }
2104 
2105 static gboolean
gimp_tool_transform_grid_get_cursor(GimpToolWidget * widget,const GimpCoords * coords,GdkModifierType state,GimpCursorType * cursor,GimpToolCursorType * tool_cursor,GimpCursorModifier * modifier)2106 gimp_tool_transform_grid_get_cursor (GimpToolWidget     *widget,
2107                                      const GimpCoords   *coords,
2108                                      GdkModifierType     state,
2109                                      GimpCursorType     *cursor,
2110                                      GimpToolCursorType *tool_cursor,
2111                                      GimpCursorModifier *modifier)
2112 {
2113   GimpToolTransformGrid        *grid    = GIMP_TOOL_TRANSFORM_GRID (widget);
2114   GimpToolTransformGridPrivate *private = grid->private;
2115   gdouble                       angle[9];
2116   gint                          i;
2117   GimpCursorType                map[8];
2118   GimpVector2                   pos[4], this, that;
2119   gboolean                      flip       = FALSE;
2120   gboolean                      side       = FALSE;
2121   gboolean                      set_cursor = TRUE;
2122 
2123   map[0] = GIMP_CURSOR_CORNER_TOP_LEFT;
2124   map[1] = GIMP_CURSOR_CORNER_TOP;
2125   map[2] = GIMP_CURSOR_CORNER_TOP_RIGHT;
2126   map[3] = GIMP_CURSOR_CORNER_RIGHT;
2127   map[4] = GIMP_CURSOR_CORNER_BOTTOM_RIGHT;
2128   map[5] = GIMP_CURSOR_CORNER_BOTTOM;
2129   map[6] = GIMP_CURSOR_CORNER_BOTTOM_LEFT;
2130   map[7] = GIMP_CURSOR_CORNER_LEFT;
2131 
2132   get_handle_geometry (grid, pos, angle);
2133 
2134   for (i = 0; i < 8; i++)
2135     angle[i] = round (angle[i] * 180.0 / G_PI / 45.0);
2136 
2137   switch (private->handle)
2138     {
2139     case GIMP_TRANSFORM_HANDLE_NW_P:
2140     case GIMP_TRANSFORM_HANDLE_NW:
2141       i = (gint) angle[4] + 0;
2142       this = pos[0];
2143       that = pos[3];
2144       break;
2145 
2146     case GIMP_TRANSFORM_HANDLE_NE_P:
2147     case GIMP_TRANSFORM_HANDLE_NE:
2148       i = (gint) angle[5] + 2;
2149       this = pos[1];
2150       that = pos[2];
2151       break;
2152 
2153     case GIMP_TRANSFORM_HANDLE_SW_P:
2154     case GIMP_TRANSFORM_HANDLE_SW:
2155       i = (gint) angle[6] + 6;
2156       this = pos[2];
2157       that = pos[1];
2158       break;
2159 
2160     case GIMP_TRANSFORM_HANDLE_SE_P:
2161     case GIMP_TRANSFORM_HANDLE_SE:
2162       i = (gint) angle[7] + 4;
2163       this = pos[3];
2164       that = pos[0];
2165       break;
2166 
2167     case GIMP_TRANSFORM_HANDLE_N:
2168     case GIMP_TRANSFORM_HANDLE_N_S:
2169       i = (gint) angle[0] + 1;
2170       this = vectoradd (pos[0], pos[1]);
2171       that = vectoradd (pos[2], pos[3]);
2172       side = TRUE;
2173       break;
2174 
2175     case GIMP_TRANSFORM_HANDLE_S:
2176     case GIMP_TRANSFORM_HANDLE_S_S:
2177       i = (gint) angle[1] + 5;
2178       this = vectoradd (pos[2], pos[3]);
2179       that = vectoradd (pos[0], pos[1]);
2180       side = TRUE;
2181       break;
2182 
2183     case GIMP_TRANSFORM_HANDLE_E:
2184     case GIMP_TRANSFORM_HANDLE_E_S:
2185       i = (gint) angle[2] + 3;
2186       this = vectoradd (pos[1], pos[3]);
2187       that = vectoradd (pos[0], pos[2]);
2188       side = TRUE;
2189       break;
2190 
2191     case GIMP_TRANSFORM_HANDLE_W:
2192     case GIMP_TRANSFORM_HANDLE_W_S:
2193       i = (gint) angle[3] + 7;
2194       this = vectoradd (pos[0], pos[2]);
2195       that = vectoradd (pos[1], pos[3]);
2196       side = TRUE;
2197       break;
2198 
2199     default:
2200       set_cursor = FALSE;
2201       break;
2202     }
2203 
2204   if (set_cursor)
2205     {
2206       i %= 8;
2207 
2208       switch (map[i])
2209         {
2210         case GIMP_CURSOR_CORNER_TOP_LEFT:
2211           if (this.x + this.y > that.x + that.y)
2212             flip = TRUE;
2213           break;
2214         case GIMP_CURSOR_CORNER_TOP:
2215           if (this.y > that.y)
2216             flip = TRUE;
2217           break;
2218         case GIMP_CURSOR_CORNER_TOP_RIGHT:
2219           if (this.x - this.y < that.x - that.y)
2220             flip = TRUE;
2221           break;
2222         case GIMP_CURSOR_CORNER_RIGHT:
2223           if (this.x < that.x)
2224             flip = TRUE;
2225           break;
2226         case GIMP_CURSOR_CORNER_BOTTOM_RIGHT:
2227           if (this.x + this.y < that.x + that.y)
2228             flip = TRUE;
2229           break;
2230         case GIMP_CURSOR_CORNER_BOTTOM:
2231           if (this.y < that.y)
2232             flip = TRUE;
2233           break;
2234         case GIMP_CURSOR_CORNER_BOTTOM_LEFT:
2235           if (this.x - this.y > that.x - that.y)
2236             flip = TRUE;
2237           break;
2238         case GIMP_CURSOR_CORNER_LEFT:
2239           if (this.x > that.x)
2240             flip = TRUE;
2241           break;
2242         default:
2243           gimp_assert_not_reached ();
2244         }
2245 
2246       if (flip)
2247         *cursor = map[(i + 4) % 8];
2248       else
2249         *cursor = map[i];
2250 
2251       if (side)
2252         *cursor += 8;
2253     }
2254 
2255   /* parent class handles *cursor and *modifier for most handles */
2256   switch (private->handle)
2257     {
2258     case GIMP_TRANSFORM_HANDLE_NONE:
2259       *tool_cursor = GIMP_TOOL_CURSOR_NONE;
2260       break;
2261 
2262     case GIMP_TRANSFORM_HANDLE_NW_P:
2263     case GIMP_TRANSFORM_HANDLE_NE_P:
2264     case GIMP_TRANSFORM_HANDLE_SW_P:
2265     case GIMP_TRANSFORM_HANDLE_SE_P:
2266       *tool_cursor = GIMP_TOOL_CURSOR_PERSPECTIVE;
2267       break;
2268 
2269     case GIMP_TRANSFORM_HANDLE_NW:
2270     case GIMP_TRANSFORM_HANDLE_NE:
2271     case GIMP_TRANSFORM_HANDLE_SW:
2272     case GIMP_TRANSFORM_HANDLE_SE:
2273     case GIMP_TRANSFORM_HANDLE_N:
2274     case GIMP_TRANSFORM_HANDLE_S:
2275     case GIMP_TRANSFORM_HANDLE_E:
2276     case GIMP_TRANSFORM_HANDLE_W:
2277       *tool_cursor = GIMP_TOOL_CURSOR_RESIZE;
2278       break;
2279 
2280     case GIMP_TRANSFORM_HANDLE_CENTER:
2281       *tool_cursor = GIMP_TOOL_CURSOR_MOVE;
2282       break;
2283 
2284     case GIMP_TRANSFORM_HANDLE_PIVOT:
2285       *tool_cursor = GIMP_TOOL_CURSOR_ROTATE;
2286       *modifier    = GIMP_CURSOR_MODIFIER_MOVE;
2287       break;
2288 
2289     case GIMP_TRANSFORM_HANDLE_N_S:
2290     case GIMP_TRANSFORM_HANDLE_S_S:
2291     case GIMP_TRANSFORM_HANDLE_E_S:
2292     case GIMP_TRANSFORM_HANDLE_W_S:
2293       *tool_cursor = GIMP_TOOL_CURSOR_SHEAR;
2294       break;
2295 
2296     case GIMP_TRANSFORM_HANDLE_ROTATION:
2297       *tool_cursor = GIMP_TOOL_CURSOR_ROTATE;
2298       break;
2299 
2300     default:
2301       g_return_val_if_reached (FALSE);
2302     }
2303 
2304   return TRUE;
2305 }
2306 
2307 static GimpTransformHandle
gimp_tool_transform_grid_get_handle_for_coords(GimpToolTransformGrid * grid,const GimpCoords * coords)2308 gimp_tool_transform_grid_get_handle_for_coords (GimpToolTransformGrid *grid,
2309                                                 const GimpCoords      *coords)
2310 {
2311   GimpToolTransformGridPrivate *private = grid->private;
2312   GimpTransformHandle           i;
2313 
2314   for (i = GIMP_TRANSFORM_HANDLE_NONE + 1; i < GIMP_N_TRANSFORM_HANDLES; i++)
2315     {
2316       if (private->handles[i] &&
2317           gimp_canvas_item_hit (private->handles[i], coords->x, coords->y))
2318         {
2319           return i;
2320         }
2321     }
2322 
2323   return GIMP_TRANSFORM_HANDLE_NONE;
2324 }
2325 
2326 static void
gimp_tool_transform_grid_update_hilight(GimpToolTransformGrid * grid)2327 gimp_tool_transform_grid_update_hilight (GimpToolTransformGrid *grid)
2328 {
2329   GimpToolTransformGridPrivate *private = grid->private;
2330   GimpTransformHandle           handle;
2331 
2332   for (handle = GIMP_TRANSFORM_HANDLE_NONE;
2333        handle < GIMP_N_TRANSFORM_HANDLES;
2334        handle++)
2335     {
2336       if (private->handles[handle])
2337         {
2338           gimp_canvas_item_set_highlight (private->handles[handle],
2339                                           handle == private->handle);
2340         }
2341     }
2342 }
2343 
2344 static void
gimp_tool_transform_grid_update_box(GimpToolTransformGrid * grid)2345 gimp_tool_transform_grid_update_box (GimpToolTransformGrid  *grid)
2346 {
2347   GimpToolTransformGridPrivate *private = grid->private;
2348 
2349   gimp_matrix3_transform_point (&private->transform,
2350                                 private->x1, private->y1,
2351                                 &private->tx1, &private->ty1);
2352   gimp_matrix3_transform_point (&private->transform,
2353                                 private->x2, private->y1,
2354                                 &private->tx2, &private->ty2);
2355   gimp_matrix3_transform_point (&private->transform,
2356                                 private->x1, private->y2,
2357                                 &private->tx3, &private->ty3);
2358   gimp_matrix3_transform_point (&private->transform,
2359                                 private->x2, private->y2,
2360                                 &private->tx4, &private->ty4);
2361 
2362   /* don't transform pivot */
2363   private->tpx = private->pivot_x;
2364   private->tpy = private->pivot_y;
2365 
2366   if (transform_grid_is_convex (grid))
2367     {
2368       gimp_matrix3_transform_point (&private->transform,
2369                                     (private->x1 + private->x2) / 2.0,
2370                                     (private->y1 + private->y2) / 2.0,
2371                                     &private->tcx, &private->tcy);
2372     }
2373   else
2374     {
2375       private->tcx = (private->tx1 +
2376                       private->tx2 +
2377                       private->tx3 +
2378                       private->tx4) / 4.0;
2379       private->tcy = (private->ty1 +
2380                       private->ty2 +
2381                       private->ty3 +
2382                       private->ty4) / 4.0;
2383     }
2384 }
2385 
2386 static void
gimp_tool_transform_grid_update_matrix(GimpToolTransformGrid * grid)2387 gimp_tool_transform_grid_update_matrix (GimpToolTransformGrid *grid)
2388 {
2389   GimpToolTransformGridPrivate *private = grid->private;
2390 
2391   gimp_matrix3_identity (&private->transform);
2392   gimp_transform_matrix_perspective (&private->transform,
2393                                      private->x1,
2394                                      private->y1,
2395                                      private->x2 - private->x1,
2396                                      private->y2 - private->y1,
2397                                      private->tx1,
2398                                      private->ty1,
2399                                      private->tx2,
2400                                      private->ty2,
2401                                      private->tx3,
2402                                      private->ty3,
2403                                      private->tx4,
2404                                      private->ty4);
2405 
2406   private->pivot_x = private->tpx;
2407   private->pivot_y = private->tpy;
2408 
2409   g_object_freeze_notify (G_OBJECT (grid));
2410   g_object_notify (G_OBJECT (grid), "transform");
2411   g_object_notify (G_OBJECT (grid), "pivot-x");
2412   g_object_notify (G_OBJECT (grid), "pivot-x");
2413   g_object_thaw_notify (G_OBJECT (grid));
2414 }
2415 
2416 static void
gimp_tool_transform_grid_calc_handles(GimpToolTransformGrid * grid,gint * handle_w,gint * handle_h)2417 gimp_tool_transform_grid_calc_handles (GimpToolTransformGrid *grid,
2418                                        gint                  *handle_w,
2419                                        gint                  *handle_h)
2420 {
2421   GimpToolTransformGridPrivate *private = grid->private;
2422   gint                          dx1, dy1;
2423   gint                          dx2, dy2;
2424   gint                          dx3, dy3;
2425   gint                          dx4, dy4;
2426   gint                          x1, y1;
2427   gint                          x2, y2;
2428 
2429   if (! private->dynamic_handle_size)
2430     {
2431       *handle_w = GIMP_CANVAS_HANDLE_SIZE_LARGE;
2432       *handle_h = GIMP_CANVAS_HANDLE_SIZE_LARGE;
2433 
2434       return;
2435     }
2436 
2437   gimp_canvas_item_transform_xy (private->guides,
2438                                  private->tx1, private->ty1,
2439                                  &dx1, &dy1);
2440   gimp_canvas_item_transform_xy (private->guides,
2441                                  private->tx2, private->ty2,
2442                                  &dx2, &dy2);
2443   gimp_canvas_item_transform_xy (private->guides,
2444                                  private->tx3, private->ty3,
2445                                  &dx3, &dy3);
2446   gimp_canvas_item_transform_xy (private->guides,
2447                                  private->tx4, private->ty4,
2448                                  &dx4, &dy4);
2449 
2450   x1 = MIN4 (dx1, dx2, dx3, dx4);
2451   y1 = MIN4 (dy1, dy2, dy3, dy4);
2452   x2 = MAX4 (dx1, dx2, dx3, dx4);
2453   y2 = MAX4 (dy1, dy2, dy3, dy4);
2454 
2455   *handle_w = CLAMP ((x2 - x1) / 3,
2456                      MIN_HANDLE_SIZE, GIMP_CANVAS_HANDLE_SIZE_LARGE);
2457   *handle_h = CLAMP ((y2 - y1) / 3,
2458                      MIN_HANDLE_SIZE, GIMP_CANVAS_HANDLE_SIZE_LARGE);
2459 }
2460 
2461 
2462 /*  public functions  */
2463 
2464 GimpToolWidget *
gimp_tool_transform_grid_new(GimpDisplayShell * shell,const GimpMatrix3 * transform,gdouble x1,gdouble y1,gdouble x2,gdouble y2)2465 gimp_tool_transform_grid_new (GimpDisplayShell  *shell,
2466                               const GimpMatrix3 *transform,
2467                               gdouble            x1,
2468                               gdouble            y1,
2469                               gdouble            x2,
2470                               gdouble            y2)
2471 {
2472   g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
2473 
2474   return g_object_new (GIMP_TYPE_TOOL_TRANSFORM_GRID,
2475                        "shell",      shell,
2476                        "transform",  transform,
2477                        "x1",         x1,
2478                        "y1",         y1,
2479                        "x2",         x2,
2480                        "y2",         y2,
2481                        NULL);
2482 }
2483 
2484 
2485 /*  protected functions  */
2486 
2487 GimpTransformHandle
gimp_tool_transform_grid_get_handle(GimpToolTransformGrid * grid)2488 gimp_tool_transform_grid_get_handle (GimpToolTransformGrid *grid)
2489 {
2490   g_return_val_if_fail (GIMP_IS_TOOL_TRANSFORM_GRID (grid),
2491                         GIMP_TRANSFORM_HANDLE_NONE);
2492 
2493   return grid->private->handle;
2494 }
2495