UI: Changes to Graph Editor selection and transform
[blender.git] / source / blender / editors / transform / transform.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edtransform
22  */
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <math.h>
28 #include <float.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_anim_types.h"
33 #include "DNA_armature_types.h"
34 #include "DNA_constraint_types.h"
35 #include "DNA_mask_types.h"
36 #include "DNA_mesh_types.h"
37 #include "DNA_movieclip_types.h"
38 #include "DNA_scene_types.h" /* PET modes */
39 #include "DNA_workspace_types.h"
40 #include "DNA_gpencil_types.h"
41
42 #include "BLI_alloca.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_math.h"
45 #include "BLI_rect.h"
46 #include "BLI_listbase.h"
47 #include "BLI_string.h"
48 #include "BLI_ghash.h"
49 #include "BLI_utildefines_stack.h"
50 #include "BLI_memarena.h"
51
52 #include "BKE_nla.h"
53 #include "BKE_editmesh.h"
54 #include "BKE_editmesh_bvh.h"
55 #include "BKE_context.h"
56 #include "BKE_constraint.h"
57 #include "BKE_particle.h"
58 #include "BKE_unit.h"
59 #include "BKE_scene.h"
60 #include "BKE_mask.h"
61 #include "BKE_mesh.h"
62 #include "BKE_report.h"
63 #include "BKE_workspace.h"
64
65 #include "DEG_depsgraph.h"
66
67 #include "GPU_immediate.h"
68 #include "GPU_matrix.h"
69 #include "GPU_state.h"
70
71 #include "ED_image.h"
72 #include "ED_keyframing.h"
73 #include "ED_screen.h"
74 #include "ED_space_api.h"
75 #include "ED_view3d.h"
76 #include "ED_mesh.h"
77 #include "ED_clip.h"
78 #include "ED_node.h"
79 #include "ED_gpencil.h"
80 #include "ED_sculpt.h"
81
82 #include "WM_types.h"
83 #include "WM_api.h"
84
85 #include "UI_view2d.h"
86 #include "UI_interface.h"
87 #include "UI_interface_icons.h"
88 #include "UI_resources.h"
89
90 #include "RNA_access.h"
91 #include "RNA_define.h"
92
93 #include "BLF_api.h"
94 #include "BLT_translation.h"
95
96 #include "transform.h"
97 #include "transform_convert.h"
98 #include "transform_snap.h"
99
100 /* Disabling, since when you type you know what you are doing,
101  * and being able to set it to zero is handy. */
102 // #define USE_NUM_NO_ZERO
103
104 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
105 static void doEdgeSlide(TransInfo *t, float perc);
106 static void doVertSlide(TransInfo *t, float perc);
107
108 static void drawEdgeSlide(TransInfo *t);
109 static void drawVertSlide(TransInfo *t);
110 static void postInputRotation(TransInfo *t, float values[3]);
111
112 static void ElementRotation(
113     TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around);
114 static void initSnapSpatial(TransInfo *t, float r_snap[3]);
115
116 static void storeCustomLNorValue(TransDataContainer *t, BMesh *bm);
117
118 /* Transform Callbacks */
119 static void initBend(TransInfo *t);
120 static eRedrawFlag handleEventBend(TransInfo *t, const struct wmEvent *event);
121 static void Bend(TransInfo *t, const int mval[2]);
122
123 static void initShear(TransInfo *t);
124 static eRedrawFlag handleEventShear(TransInfo *t, const struct wmEvent *event);
125 static void applyShear(TransInfo *t, const int mval[2]);
126
127 static void initResize(TransInfo *t);
128 static void applyResize(TransInfo *t, const int mval[2]);
129
130 static void initSkinResize(TransInfo *t);
131 static void applySkinResize(TransInfo *t, const int mval[2]);
132
133 static void initTranslation(TransInfo *t);
134 static void applyTranslation(TransInfo *t, const int mval[2]);
135
136 static void initToSphere(TransInfo *t);
137 static void applyToSphere(TransInfo *t, const int mval[2]);
138
139 static void initRotation(TransInfo *t);
140 static void applyRotation(TransInfo *t, const int mval[2]);
141
142 static void initNormalRotation(TransInfo *t);
143 static void applyNormalRotation(TransInfo *t, const int mval[2]);
144
145 static void initShrinkFatten(TransInfo *t);
146 static void applyShrinkFatten(TransInfo *t, const int mval[2]);
147
148 static void initTilt(TransInfo *t);
149 static void applyTilt(TransInfo *t, const int mval[2]);
150
151 static void initCurveShrinkFatten(TransInfo *t);
152 static void applyCurveShrinkFatten(TransInfo *t, const int mval[2]);
153
154 static void initMaskShrinkFatten(TransInfo *t);
155 static void applyMaskShrinkFatten(TransInfo *t, const int mval[2]);
156
157 static void initGPShrinkFatten(TransInfo *t);
158 static void applyGPShrinkFatten(TransInfo *t, const int mval[2]);
159
160 static void initTrackball(TransInfo *t);
161 static void applyTrackball(TransInfo *t, const int mval[2]);
162
163 static void initPushPull(TransInfo *t);
164 static void applyPushPull(TransInfo *t, const int mval[2]);
165
166 static void initBevelWeight(TransInfo *t);
167 static void applyBevelWeight(TransInfo *t, const int mval[2]);
168
169 static void initCrease(TransInfo *t);
170 static void applyCrease(TransInfo *t, const int mval[2]);
171
172 static void initBoneSize(TransInfo *t);
173 static void applyBoneSize(TransInfo *t, const int mval[2]);
174
175 static void initBoneEnvelope(TransInfo *t);
176 static void applyBoneEnvelope(TransInfo *t, const int mval[2]);
177
178 static void initBoneRoll(TransInfo *t);
179 static void applyBoneRoll(TransInfo *t, const int mval[2]);
180
181 static void initEdgeSlide_ex(
182     TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp);
183 static void initEdgeSlide(TransInfo *t);
184 static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
185 static void applyEdgeSlide(TransInfo *t, const int mval[2]);
186
187 static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp);
188 static void initVertSlide(TransInfo *t);
189 static eRedrawFlag handleEventVertSlide(TransInfo *t, const struct wmEvent *event);
190 static void applyVertSlide(TransInfo *t, const int mval[2]);
191
192 static void initTimeTranslate(TransInfo *t);
193 static void applyTimeTranslate(TransInfo *t, const int mval[2]);
194
195 static void initTimeSlide(TransInfo *t);
196 static void applyTimeSlide(TransInfo *t, const int mval[2]);
197
198 static void initTimeScale(TransInfo *t);
199 static void applyTimeScale(TransInfo *t, const int mval[2]);
200
201 static void initBakeTime(TransInfo *t);
202 static void applyBakeTime(TransInfo *t, const int mval[2]);
203
204 static void initMirror(TransInfo *t);
205 static void applyMirror(TransInfo *t, const int mval[2]);
206
207 static void initAlign(TransInfo *t);
208 static void applyAlign(TransInfo *t, const int mval[2]);
209
210 static void initSeqSlide(TransInfo *t);
211 static void applySeqSlide(TransInfo *t, const int mval[2]);
212
213 static void initGPOpacity(TransInfo *t);
214 static void applyGPOpacity(TransInfo *t, const int mval[2]);
215 /* end transform callbacks */
216
217 static bool transdata_check_local_center(TransInfo *t, short around)
218 {
219   return ((around == V3D_AROUND_LOCAL_ORIGINS) &&
220           ((t->flag & (T_OBJECT | T_POSE)) ||
221            /* implicit: (t->flag & T_EDIT) */
222            (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) ||
223            (t->spacetype == SPACE_GRAPH) ||
224            (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE))));
225 }
226
227 bool transdata_check_local_islands(TransInfo *t, short around)
228 {
229   return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH))));
230 }
231
232 /* ************************** SPACE DEPENDENT CODE **************************** */
233
234 void setTransformViewMatrices(TransInfo *t)
235 {
236   if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
237     RegionView3D *rv3d = t->ar->regiondata;
238
239     copy_m4_m4(t->viewmat, rv3d->viewmat);
240     copy_m4_m4(t->viewinv, rv3d->viewinv);
241     copy_m4_m4(t->persmat, rv3d->persmat);
242     copy_m4_m4(t->persinv, rv3d->persinv);
243     t->persp = rv3d->persp;
244   }
245   else {
246     unit_m4(t->viewmat);
247     unit_m4(t->viewinv);
248     unit_m4(t->persmat);
249     unit_m4(t->persinv);
250     t->persp = RV3D_ORTHO;
251   }
252
253   calculateCenter2D(t);
254   calculateCenterLocal(t, t->center_global);
255 }
256
257 void setTransformViewAspect(TransInfo *t, float r_aspect[3])
258 {
259   copy_v3_fl(r_aspect, 1.0f);
260
261   if (t->spacetype == SPACE_IMAGE) {
262     SpaceImage *sima = t->sa->spacedata.first;
263
264     if (t->options & CTX_MASK) {
265       ED_space_image_get_aspect(sima, &r_aspect[0], &r_aspect[1]);
266     }
267     else if (t->options & CTX_PAINT_CURVE) {
268       /* pass */
269     }
270     else {
271       ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]);
272     }
273   }
274   else if (t->spacetype == SPACE_CLIP) {
275     SpaceClip *sclip = t->sa->spacedata.first;
276
277     if (t->options & CTX_MOVIECLIP) {
278       ED_space_clip_get_aspect_dimension_aware(sclip, &r_aspect[0], &r_aspect[1]);
279     }
280     else {
281       ED_space_clip_get_aspect(sclip, &r_aspect[0], &r_aspect[1]);
282     }
283   }
284   else if (t->spacetype == SPACE_GRAPH) {
285     /* depemds on context of usage */
286   }
287 }
288
289 static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
290 {
291   float divx = BLI_rcti_size_x(&v2d->mask);
292   float divy = BLI_rcti_size_y(&v2d->mask);
293
294   r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
295   r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
296   r_vec[2] = 0.0f;
297 }
298
299 static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
300 {
301   float divx = BLI_rcti_size_x(&v2d->mask);
302   float divy = BLI_rcti_size_y(&v2d->mask);
303
304   float mulx = BLI_rctf_size_x(&v2d->cur);
305   float muly = BLI_rctf_size_y(&v2d->cur);
306
307   /* difference with convertViewVec2D */
308   /* clamp w/h, mask only */
309   if (mulx / divx < muly / divy) {
310     divy = divx;
311     muly = mulx;
312   }
313   else {
314     divx = divy;
315     mulx = muly;
316   }
317   /* end difference */
318
319   r_vec[0] = mulx * dx / divx;
320   r_vec[1] = muly * dy / divy;
321   r_vec[2] = 0.0f;
322 }
323
324 void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
325 {
326   if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
327     if (t->options & CTX_PAINT_CURVE) {
328       r_vec[0] = dx;
329       r_vec[1] = dy;
330     }
331     else {
332       const float mval_f[2] = {(float)dx, (float)dy};
333       ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac);
334     }
335   }
336   else if (t->spacetype == SPACE_IMAGE) {
337     if (t->options & CTX_MASK) {
338       convertViewVec2D_mask(t->view, r_vec, dx, dy);
339     }
340     else if (t->options & CTX_PAINT_CURVE) {
341       r_vec[0] = dx;
342       r_vec[1] = dy;
343     }
344     else {
345       convertViewVec2D(t->view, r_vec, dx, dy);
346     }
347
348     r_vec[0] *= t->aspect[0];
349     r_vec[1] *= t->aspect[1];
350   }
351   else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
352     convertViewVec2D(t->view, r_vec, dx, dy);
353   }
354   else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
355     convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
356   }
357   else if (t->spacetype == SPACE_CLIP) {
358     if (t->options & CTX_MASK) {
359       convertViewVec2D_mask(t->view, r_vec, dx, dy);
360     }
361     else {
362       convertViewVec2D(t->view, r_vec, dx, dy);
363     }
364
365     r_vec[0] *= t->aspect[0];
366     r_vec[1] *= t->aspect[1];
367   }
368   else {
369     printf("%s: called in an invalid context\n", __func__);
370     zero_v3(r_vec);
371   }
372 }
373
374 void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
375 {
376   if (t->spacetype == SPACE_VIEW3D) {
377     if (t->ar->regiontype == RGN_TYPE_WINDOW) {
378       if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
379         /* this is what was done in 2.64, perhaps we can be smarter? */
380         adr[0] = (int)2140000000.0f;
381         adr[1] = (int)2140000000.0f;
382       }
383     }
384   }
385   else if (t->spacetype == SPACE_IMAGE) {
386     SpaceImage *sima = t->sa->spacedata.first;
387
388     if (t->options & CTX_MASK) {
389       float v[2];
390
391       v[0] = vec[0] / t->aspect[0];
392       v[1] = vec[1] / t->aspect[1];
393
394       BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
395
396       ED_image_point_pos__reverse(sima, t->ar, v, v);
397
398       adr[0] = v[0];
399       adr[1] = v[1];
400     }
401     else if (t->options & CTX_PAINT_CURVE) {
402       adr[0] = vec[0];
403       adr[1] = vec[1];
404     }
405     else {
406       float v[2];
407
408       v[0] = vec[0] / t->aspect[0];
409       v[1] = vec[1] / t->aspect[1];
410
411       UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
412     }
413   }
414   else if (t->spacetype == SPACE_ACTION) {
415     int out[2] = {0, 0};
416 #if 0
417     SpaceAction *sact = t->sa->spacedata.first;
418
419     if (sact->flag & SACTION_DRAWTIME) {
420       //vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
421       /* same as below */
422       UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
423     }
424     else
425 #endif
426     {
427       UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
428     }
429
430     adr[0] = out[0];
431     adr[1] = out[1];
432   }
433   else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
434     int out[2] = {0, 0};
435
436     UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
437     adr[0] = out[0];
438     adr[1] = out[1];
439   }
440   else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
441     int out[2] = {0, 0};
442
443     UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
444     adr[0] = out[0];
445     adr[1] = out[1];
446   }
447   else if (t->spacetype == SPACE_CLIP) {
448     SpaceClip *sc = t->sa->spacedata.first;
449
450     if (t->options & CTX_MASK) {
451       MovieClip *clip = ED_space_clip_get_clip(sc);
452
453       if (clip) {
454         float v[2];
455
456         v[0] = vec[0] / t->aspect[0];
457         v[1] = vec[1] / t->aspect[1];
458
459         BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v);
460
461         ED_clip_point_stable_pos__reverse(sc, t->ar, v, v);
462
463         adr[0] = v[0];
464         adr[1] = v[1];
465       }
466       else {
467         adr[0] = 0;
468         adr[1] = 0;
469       }
470     }
471     else if (t->options & CTX_MOVIECLIP) {
472       float v[2];
473
474       v[0] = vec[0] / t->aspect[0];
475       v[1] = vec[1] / t->aspect[1];
476
477       UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
478     }
479     else {
480       BLI_assert(0);
481     }
482   }
483   else if (t->spacetype == SPACE_NODE) {
484     UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &adr[0], &adr[1]);
485   }
486 }
487 void projectIntView(TransInfo *t, const float vec[3], int adr[2])
488 {
489   projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
490 }
491
492 void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
493 {
494   switch (t->spacetype) {
495     case SPACE_VIEW3D: {
496       if (t->options & CTX_PAINT_CURVE) {
497         adr[0] = vec[0];
498         adr[1] = vec[1];
499       }
500       else if (t->ar->regiontype == RGN_TYPE_WINDOW) {
501         /* allow points behind the view [#33643] */
502         if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
503           /* XXX, 2.64 and prior did this, weak! */
504           adr[0] = t->ar->winx / 2.0f;
505           adr[1] = t->ar->winy / 2.0f;
506         }
507         return;
508       }
509       break;
510     }
511     default: {
512       int a[2] = {0, 0};
513       projectIntView(t, vec, a);
514       adr[0] = a[0];
515       adr[1] = a[1];
516       break;
517     }
518   }
519 }
520 void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
521 {
522   projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
523 }
524
525 void applyAspectRatio(TransInfo *t, float vec[2])
526 {
527   if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) &&
528       !(t->options & CTX_PAINT_CURVE)) {
529     SpaceImage *sima = t->sa->spacedata.first;
530
531     if ((sima->flag & SI_COORDFLOATS) == 0) {
532       int width, height;
533       ED_space_image_get_size(sima, &width, &height);
534
535       vec[0] *= width;
536       vec[1] *= height;
537     }
538
539     vec[0] /= t->aspect[0];
540     vec[1] /= t->aspect[1];
541   }
542   else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
543     if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
544       vec[0] /= t->aspect[0];
545       vec[1] /= t->aspect[1];
546     }
547   }
548 }
549
550 void removeAspectRatio(TransInfo *t, float vec[2])
551 {
552   if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
553     SpaceImage *sima = t->sa->spacedata.first;
554
555     if ((sima->flag & SI_COORDFLOATS) == 0) {
556       int width, height;
557       ED_space_image_get_size(sima, &width, &height);
558
559       vec[0] /= width;
560       vec[1] /= height;
561     }
562
563     vec[0] *= t->aspect[0];
564     vec[1] *= t->aspect[1];
565   }
566   else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
567     if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
568       vec[0] *= t->aspect[0];
569       vec[1] *= t->aspect[1];
570     }
571   }
572 }
573
574 static void viewRedrawForce(const bContext *C, TransInfo *t)
575 {
576   if (t->options & CTX_GPENCIL_STROKES) {
577     bGPdata *gpd = ED_gpencil_data_get_active(C);
578     if (gpd) {
579       DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
580     }
581     WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
582   }
583   else if (t->spacetype == SPACE_VIEW3D) {
584     if (t->options & CTX_PAINT_CURVE) {
585       wmWindow *window = CTX_wm_window(C);
586       WM_paint_cursor_tag_redraw(window, t->ar);
587     }
588     else {
589       /* Do we need more refined tags? */
590       if (t->flag & T_POSE) {
591         WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
592       }
593       else {
594         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
595       }
596
597       /* For real-time animation record - send notifiers recognized by animation editors */
598       // XXX: is this notifier a lame duck?
599       if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) {
600         WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
601       }
602     }
603   }
604   else if (t->spacetype == SPACE_ACTION) {
605     // SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
606     WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
607   }
608   else if (t->spacetype == SPACE_GRAPH) {
609     // SpaceGraph *sipo = (SpaceGraph *)t->sa->spacedata.first;
610     WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
611   }
612   else if (t->spacetype == SPACE_NLA) {
613     WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
614   }
615   else if (t->spacetype == SPACE_NODE) {
616     // ED_area_tag_redraw(t->sa);
617     WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
618   }
619   else if (t->spacetype == SPACE_SEQ) {
620     WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
621     /* Keyframes on strips has been moved, so make sure related editos are informed. */
622     WM_event_add_notifier(C, NC_ANIMATION, NULL);
623   }
624   else if (t->spacetype == SPACE_IMAGE) {
625     if (t->options & CTX_MASK) {
626       Mask *mask = CTX_data_edit_mask(C);
627
628       WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
629     }
630     else if (t->options & CTX_PAINT_CURVE) {
631       wmWindow *window = CTX_wm_window(C);
632       WM_paint_cursor_tag_redraw(window, t->ar);
633     }
634     else if (t->flag & T_CURSOR) {
635       ED_area_tag_redraw(t->sa);
636     }
637     else {
638       // XXX how to deal with lock?
639       SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
640       if (sima->lock) {
641         WM_event_add_notifier(C, NC_GEOM | ND_DATA, OBEDIT_FROM_VIEW_LAYER(t->view_layer)->data);
642       }
643       else {
644         ED_area_tag_redraw(t->sa);
645       }
646     }
647   }
648   else if (t->spacetype == SPACE_CLIP) {
649     SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
650
651     if (ED_space_clip_check_show_trackedit(sc)) {
652       MovieClip *clip = ED_space_clip_get_clip(sc);
653
654       /* objects could be parented to tracking data, so send this for viewport refresh */
655       WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
656
657       WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
658     }
659     else if (ED_space_clip_check_show_maskedit(sc)) {
660       Mask *mask = CTX_data_edit_mask(C);
661
662       WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
663     }
664   }
665 }
666
667 static void viewRedrawPost(bContext *C, TransInfo *t)
668 {
669   ED_area_status_text(t->sa, NULL);
670
671   if (t->spacetype == SPACE_VIEW3D) {
672     /* if autokeying is enabled, send notifiers that keyframes were added */
673     if (IS_AUTOKEY_ON(t->scene)) {
674       WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
675     }
676
677     /* redraw UV editor */
678     if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) &&
679         (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) {
680       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
681     }
682
683     /* XXX temp, first hack to get auto-render in compositor work (ton) */
684     WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
685   }
686
687 #if 0  // TRANSFORM_FIX_ME
688   if (t->spacetype == SPACE_VIEW3D) {
689     allqueue(REDRAWBUTSOBJECT, 0);
690     allqueue(REDRAWVIEW3D, 0);
691   }
692   else if (t->spacetype == SPACE_IMAGE) {
693     allqueue(REDRAWIMAGE, 0);
694     allqueue(REDRAWVIEW3D, 0);
695   }
696   else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_GRAPH)) {
697     allqueue(REDRAWVIEW3D, 0);
698     allqueue(REDRAWACTION, 0);
699     allqueue(REDRAWNLA, 0);
700     allqueue(REDRAWIPO, 0);
701     allqueue(REDRAWTIME, 0);
702     allqueue(REDRAWBUTSOBJECT, 0);
703   }
704
705   scrarea_queue_headredraw(curarea);
706 #endif
707 }
708
709 /* ************************** TRANSFORMATIONS **************************** */
710
711 static void view_editmove(unsigned short UNUSED(event))
712 {
713 #if 0  // TRANSFORM_FIX_ME
714   int refresh = 0;
715   /* Regular:   Zoom in */
716   /* Shift:     Scroll up */
717   /* Ctrl:      Scroll right */
718   /* Alt-Shift: Rotate up */
719   /* Alt-Ctrl:  Rotate right */
720
721   /* only work in 3D window for now
722    * In the end, will have to send to event to a 2D window handler instead
723    */
724   if (Trans.flag & T_2D_EDIT) {
725     return;
726   }
727
728   switch (event) {
729     case WHEELUPMOUSE:
730       if (G.qual & LR_SHIFTKEY) {
731         if (G.qual & LR_ALTKEY) {
732           G.qual &= ~LR_SHIFTKEY;
733           persptoetsen(PAD2);
734           G.qual |= LR_SHIFTKEY;
735         }
736         else {
737           persptoetsen(PAD2);
738         }
739       }
740       else if (G.qual & LR_CTRLKEY) {
741         if (G.qual & LR_ALTKEY) {
742           G.qual &= ~LR_CTRLKEY;
743           persptoetsen(PAD4);
744           G.qual |= LR_CTRLKEY;
745         }
746         else {
747           persptoetsen(PAD4);
748         }
749       }
750       else if (U.uiflag & USER_WHEELZOOMDIR) {
751         persptoetsen(PADMINUS);
752       }
753       else {
754         persptoetsen(PADPLUSKEY);
755       }
756
757       refresh = 1;
758       break;
759     case WHEELDOWNMOUSE:
760       if (G.qual & LR_SHIFTKEY) {
761         if (G.qual & LR_ALTKEY) {
762           G.qual &= ~LR_SHIFTKEY;
763           persptoetsen(PAD8);
764           G.qual |= LR_SHIFTKEY;
765         }
766         else {
767           persptoetsen(PAD8);
768         }
769       }
770       else if (G.qual & LR_CTRLKEY) {
771         if (G.qual & LR_ALTKEY) {
772           G.qual &= ~LR_CTRLKEY;
773           persptoetsen(PAD6);
774           G.qual |= LR_CTRLKEY;
775         }
776         else {
777           persptoetsen(PAD6);
778         }
779       }
780       else if (U.uiflag & USER_WHEELZOOMDIR) {
781         persptoetsen(PADPLUSKEY);
782       }
783       else {
784         persptoetsen(PADMINUS);
785       }
786
787       refresh = 1;
788       break;
789   }
790
791   if (refresh) {
792     setTransformViewMatrices(&Trans);
793   }
794 #endif
795 }
796
797 /* ************************************************* */
798
799 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
800 enum {
801   TFM_MODAL_CANCEL = 1,
802   TFM_MODAL_CONFIRM = 2,
803   TFM_MODAL_TRANSLATE = 3,
804   TFM_MODAL_ROTATE = 4,
805   TFM_MODAL_RESIZE = 5,
806   TFM_MODAL_SNAP_INV_ON = 6,
807   TFM_MODAL_SNAP_INV_OFF = 7,
808   TFM_MODAL_SNAP_TOGGLE = 8,
809   TFM_MODAL_AXIS_X = 9,
810   TFM_MODAL_AXIS_Y = 10,
811   TFM_MODAL_AXIS_Z = 11,
812   TFM_MODAL_PLANE_X = 12,
813   TFM_MODAL_PLANE_Y = 13,
814   TFM_MODAL_PLANE_Z = 14,
815   TFM_MODAL_CONS_OFF = 15,
816   TFM_MODAL_ADD_SNAP = 16,
817   TFM_MODAL_REMOVE_SNAP = 17,
818
819   /* 18 and 19 used by numinput, defined in transform.h */
820
821   TFM_MODAL_PROPSIZE_UP = 20,
822   TFM_MODAL_PROPSIZE_DOWN = 21,
823   TFM_MODAL_AUTOIK_LEN_INC = 22,
824   TFM_MODAL_AUTOIK_LEN_DEC = 23,
825
826   TFM_MODAL_EDGESLIDE_UP = 24,
827   TFM_MODAL_EDGESLIDE_DOWN = 25,
828
829   /* for analog input, like trackpad */
830   TFM_MODAL_PROPSIZE = 26,
831   /* node editor insert offset (aka auto-offset) direction toggle */
832   TFM_MODAL_INSERTOFS_TOGGLE_DIR = 27,
833 };
834
835 static bool transform_modal_item_poll(const wmOperator *op, int value)
836 {
837   const TransInfo *t = op->customdata;
838   switch (value) {
839     case TFM_MODAL_CANCEL: {
840       if ((t->flag & T_RELEASE_CONFIRM) && ISMOUSE(t->launch_event)) {
841         return false;
842       }
843       break;
844     }
845     case TFM_MODAL_PROPSIZE:
846     case TFM_MODAL_PROPSIZE_UP:
847     case TFM_MODAL_PROPSIZE_DOWN: {
848       if ((t->flag & T_PROP_EDIT) == 0) {
849         return false;
850       }
851       break;
852     }
853     case TFM_MODAL_ADD_SNAP:
854     case TFM_MODAL_REMOVE_SNAP: {
855       if (t->spacetype != SPACE_VIEW3D) {
856         return false;
857       }
858       else if (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) {
859         return false;
860       }
861       else if (!validSnap(t)) {
862         return false;
863       }
864       break;
865     }
866     case TFM_MODAL_AXIS_X:
867     case TFM_MODAL_AXIS_Y:
868     case TFM_MODAL_AXIS_Z:
869     case TFM_MODAL_PLANE_X:
870     case TFM_MODAL_PLANE_Y:
871     case TFM_MODAL_PLANE_Z: {
872       if (t->flag & T_NO_CONSTRAINT) {
873         return false;
874       }
875       if (!ELEM(value, TFM_MODAL_AXIS_X, TFM_MODAL_AXIS_Y)) {
876         if (t->flag & T_2D_EDIT) {
877           return false;
878         }
879       }
880       break;
881     }
882     case TFM_MODAL_CONS_OFF: {
883       if ((t->con.mode & CON_APPLY) == 0) {
884         return false;
885       }
886       break;
887     }
888     case TFM_MODAL_EDGESLIDE_UP:
889     case TFM_MODAL_EDGESLIDE_DOWN: {
890       if (t->mode != TFM_EDGE_SLIDE) {
891         return false;
892       }
893       break;
894     }
895     case TFM_MODAL_INSERTOFS_TOGGLE_DIR: {
896       if (t->spacetype != SPACE_NODE) {
897         return false;
898       }
899       break;
900     }
901     case TFM_MODAL_AUTOIK_LEN_INC:
902     case TFM_MODAL_AUTOIK_LEN_DEC: {
903       if ((t->flag & T_AUTOIK) == 0) {
904         return false;
905       }
906       break;
907     }
908   }
909   return true;
910 }
911
912 /* called in transform_ops.c, on each regeneration of keymaps */
913 wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
914 {
915   static const EnumPropertyItem modal_items[] = {
916       {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
917       {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
918       {TFM_MODAL_AXIS_X, "AXIS_X", 0, "X axis", ""},
919       {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Y axis", ""},
920       {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Z axis", ""},
921       {TFM_MODAL_PLANE_X, "PLANE_X", 0, "X plane", ""},
922       {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Y plane", ""},
923       {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Z plane", ""},
924       {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Clear Constraints", ""},
925       {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Snap Invert", ""},
926       {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Snap Invert (Off)", ""},
927       {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
928       {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
929       {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
930       {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
931       {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
932       {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
933       {TFM_MODAL_PROPSIZE_DOWN,
934        "PROPORTIONAL_SIZE_DOWN",
935        0,
936        "Decrease Proportional Influence",
937        ""},
938       {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
939       {TFM_MODAL_AUTOIK_LEN_DEC,
940        "AUTOIK_CHAIN_LEN_DOWN",
941        0,
942        "Decrease Max AutoIK Chain Length",
943        ""},
944       {TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
945       {TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
946       {TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
947       {TFM_MODAL_INSERTOFS_TOGGLE_DIR,
948        "INSERTOFS_TOGGLE_DIR",
949        0,
950        "Toggle Direction for Node Auto-offset",
951        ""},
952       {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Move", ""},
953       {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
954       {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
955       {0, NULL, 0, NULL, NULL},
956   };
957
958   wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
959
960   keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
961   keymap->poll_modal_item = transform_modal_item_poll;
962
963   return keymap;
964 }
965
966 static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode, bool is_plane)
967 {
968   if (!(t->flag & T_NO_CONSTRAINT)) {
969     int constraint_axis, constraint_plane;
970     const bool edit_2d = (t->flag & T_2D_EDIT) != 0;
971     const char *msg1 = "", *msg2 = "", *msg3 = "";
972     char axis;
973
974     /* Initialize */
975     switch (key_type) {
976       case XKEY:
977         msg1 = TIP_("along X");
978         msg2 = TIP_("along %s X");
979         msg3 = TIP_("locking %s X");
980         axis = 'X';
981         constraint_axis = CON_AXIS0;
982         break;
983       case YKEY:
984         msg1 = TIP_("along Y");
985         msg2 = TIP_("along %s Y");
986         msg3 = TIP_("locking %s Y");
987         axis = 'Y';
988         constraint_axis = CON_AXIS1;
989         break;
990       case ZKEY:
991         msg1 = TIP_("along Z");
992         msg2 = TIP_("along %s Z");
993         msg3 = TIP_("locking %s Z");
994         axis = 'Z';
995         constraint_axis = CON_AXIS2;
996         break;
997       default:
998         /* Invalid key */
999         return;
1000     }
1001     constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
1002
1003     if (edit_2d && (key_type != ZKEY)) {
1004       if (cmode == axis) {
1005         stopConstraint(t);
1006       }
1007       else {
1008         setUserConstraint(t, V3D_ORIENT_GLOBAL, constraint_axis, msg1);
1009       }
1010     }
1011     else if (!edit_2d) {
1012       if (cmode != axis) {
1013         /* First press, constraint to an axis. */
1014         t->orientation.index = 0;
1015         const short *orientation_ptr = t->orientation.types[t->orientation.index];
1016         const short orientation = orientation_ptr ? *orientation_ptr : V3D_ORIENT_GLOBAL;
1017         if (is_plane == false) {
1018           setUserConstraint(t, orientation, constraint_axis, msg2);
1019         }
1020         else {
1021           setUserConstraint(t, orientation, constraint_plane, msg3);
1022         }
1023       }
1024       else {
1025         /* Successive presses on existing axis, cycle orientation modes. */
1026         t->orientation.index = (t->orientation.index + 1) % ARRAY_SIZE(t->orientation.types);
1027
1028         if (t->orientation.index == 0) {
1029           stopConstraint(t);
1030         }
1031         else {
1032           const short *orientation_ptr = t->orientation.types[t->orientation.index];
1033           const short orientation = orientation_ptr ? *orientation_ptr : V3D_ORIENT_GLOBAL;
1034           if (is_plane == false) {
1035             setUserConstraint(t, orientation, constraint_axis, msg2);
1036           }
1037           else {
1038             setUserConstraint(t, orientation, constraint_plane, msg3);
1039           }
1040         }
1041       }
1042     }
1043     t->redraw |= TREDRAW_HARD;
1044   }
1045 }
1046
1047 int transformEvent(TransInfo *t, const wmEvent *event)
1048 {
1049   char cmode = constraintModeToChar(t);
1050   bool handled = false;
1051   const int modifiers_prev = t->modifiers;
1052   const int mode_prev = t->mode;
1053
1054   t->redraw |= handleMouseInput(t, &t->mouse, event);
1055
1056   /* Handle modal numinput events first, if already activated. */
1057   if (((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) && hasNumInput(&t->num) &&
1058       handleNumInput(t->context, &(t->num), event)) {
1059     t->redraw |= TREDRAW_HARD;
1060     handled = true;
1061   }
1062   else if (event->type == MOUSEMOVE) {
1063     if (t->modifiers & MOD_CONSTRAINT_SELECT) {
1064       t->con.mode |= CON_SELECT;
1065     }
1066
1067     copy_v2_v2_int(t->mval, event->mval);
1068
1069     /* Use this for soft redraw. Might cause flicker in object mode */
1070     // t->redraw |= TREDRAW_SOFT;
1071     t->redraw |= TREDRAW_HARD;
1072
1073     if (t->state == TRANS_STARTING) {
1074       t->state = TRANS_RUNNING;
1075     }
1076
1077     applyMouseInput(t, &t->mouse, t->mval, t->values);
1078
1079     // Snapping mouse move events
1080     t->redraw |= handleSnapping(t, event);
1081     handled = true;
1082   }
1083   /* handle modal keymap first */
1084   else if (event->type == EVT_MODAL_MAP) {
1085     switch (event->val) {
1086       case TFM_MODAL_CANCEL:
1087         t->state = TRANS_CANCEL;
1088         handled = true;
1089         break;
1090       case TFM_MODAL_CONFIRM:
1091         t->state = TRANS_CONFIRM;
1092         handled = true;
1093         break;
1094       case TFM_MODAL_TRANSLATE:
1095         /* only switch when... */
1096         if (ELEM(t->mode,
1097                  TFM_ROTATION,
1098                  TFM_RESIZE,
1099                  TFM_TRACKBALL,
1100                  TFM_EDGE_SLIDE,
1101                  TFM_VERT_SLIDE)) {
1102           restoreTransObjects(t);
1103           resetTransModal(t);
1104           resetTransRestrictions(t);
1105           initTranslation(t);
1106           initSnapping(t, NULL);  // need to reinit after mode change
1107           t->redraw |= TREDRAW_HARD;
1108           handled = true;
1109         }
1110         else if (t->mode == TFM_SEQ_SLIDE) {
1111           t->flag ^= T_ALT_TRANSFORM;
1112           t->redraw |= TREDRAW_HARD;
1113           handled = true;
1114         }
1115         else {
1116           if (t->obedit_type == OB_MESH) {
1117             if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) {
1118               restoreTransObjects(t);
1119               resetTransModal(t);
1120               resetTransRestrictions(t);
1121
1122               /* first try edge slide */
1123               initEdgeSlide(t);
1124               /* if that fails, do vertex slide */
1125               if (t->state == TRANS_CANCEL) {
1126                 resetTransModal(t);
1127                 t->state = TRANS_STARTING;
1128                 initVertSlide(t);
1129               }
1130               /* vert slide can fail on unconnected vertices (rare but possible) */
1131               if (t->state == TRANS_CANCEL) {
1132                 resetTransModal(t);
1133                 t->mode = TFM_TRANSLATION;
1134                 t->state = TRANS_STARTING;
1135                 restoreTransObjects(t);
1136                 resetTransRestrictions(t);
1137                 initTranslation(t);
1138               }
1139               initSnapping(t, NULL);  // need to reinit after mode change
1140               t->redraw |= TREDRAW_HARD;
1141               handled = true;
1142             }
1143           }
1144           else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
1145             if (t->mode == TFM_TRANSLATION) {
1146               restoreTransObjects(t);
1147
1148               t->flag ^= T_ALT_TRANSFORM;
1149               t->redraw |= TREDRAW_HARD;
1150               handled = true;
1151             }
1152           }
1153         }
1154         break;
1155       case TFM_MODAL_ROTATE:
1156         /* only switch when... */
1157         if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
1158           if (ELEM(t->mode,
1159                    TFM_ROTATION,
1160                    TFM_RESIZE,
1161                    TFM_TRACKBALL,
1162                    TFM_TRANSLATION,
1163                    TFM_EDGE_SLIDE,
1164                    TFM_VERT_SLIDE)) {
1165             restoreTransObjects(t);
1166             resetTransModal(t);
1167             resetTransRestrictions(t);
1168
1169             if (t->mode == TFM_ROTATION) {
1170               initTrackball(t);
1171             }
1172             else {
1173               initRotation(t);
1174             }
1175             initSnapping(t, NULL);  // need to reinit after mode change
1176             t->redraw |= TREDRAW_HARD;
1177             handled = true;
1178           }
1179         }
1180         break;
1181       case TFM_MODAL_RESIZE:
1182         /* only switch when... */
1183         if (ELEM(t->mode,
1184                  TFM_ROTATION,
1185                  TFM_TRANSLATION,
1186                  TFM_TRACKBALL,
1187                  TFM_EDGE_SLIDE,
1188                  TFM_VERT_SLIDE)) {
1189
1190           /* Scale isn't normally very useful after extrude along normals, see T39756 */
1191           if ((t->con.mode & CON_APPLY) && (t->con.orientation == V3D_ORIENT_NORMAL)) {
1192             stopConstraint(t);
1193           }
1194
1195           restoreTransObjects(t);
1196           resetTransModal(t);
1197           resetTransRestrictions(t);
1198           initResize(t);
1199           initSnapping(t, NULL);  // need to reinit after mode change
1200           t->redraw |= TREDRAW_HARD;
1201           handled = true;
1202         }
1203         else if (t->mode == TFM_SHRINKFATTEN) {
1204           t->flag ^= T_ALT_TRANSFORM;
1205           t->redraw |= TREDRAW_HARD;
1206           handled = true;
1207         }
1208         else if (t->mode == TFM_RESIZE) {
1209           if (t->options & CTX_MOVIECLIP) {
1210             restoreTransObjects(t);
1211
1212             t->flag ^= T_ALT_TRANSFORM;
1213             t->redraw |= TREDRAW_HARD;
1214             handled = true;
1215           }
1216         }
1217         break;
1218
1219       case TFM_MODAL_SNAP_INV_ON:
1220         t->modifiers |= MOD_SNAP_INVERT;
1221         t->redraw |= TREDRAW_HARD;
1222         handled = true;
1223         break;
1224       case TFM_MODAL_SNAP_INV_OFF:
1225         t->modifiers &= ~MOD_SNAP_INVERT;
1226         t->redraw |= TREDRAW_HARD;
1227         handled = true;
1228         break;
1229       case TFM_MODAL_SNAP_TOGGLE:
1230         t->modifiers ^= MOD_SNAP;
1231         t->redraw |= TREDRAW_HARD;
1232         handled = true;
1233         break;
1234       case TFM_MODAL_AXIS_X:
1235         if (!(t->flag & T_NO_CONSTRAINT)) {
1236           transform_event_xyz_constraint(t, XKEY, cmode, false);
1237           t->redraw |= TREDRAW_HARD;
1238           handled = true;
1239         }
1240         break;
1241       case TFM_MODAL_AXIS_Y:
1242         if ((t->flag & T_NO_CONSTRAINT) == 0) {
1243           transform_event_xyz_constraint(t, YKEY, cmode, false);
1244           t->redraw |= TREDRAW_HARD;
1245           handled = true;
1246         }
1247         break;
1248       case TFM_MODAL_AXIS_Z:
1249         if ((t->flag & (T_NO_CONSTRAINT)) == 0) {
1250           transform_event_xyz_constraint(t, ZKEY, cmode, false);
1251           t->redraw |= TREDRAW_HARD;
1252           handled = true;
1253         }
1254         break;
1255       case TFM_MODAL_PLANE_X:
1256         if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1257           transform_event_xyz_constraint(t, XKEY, cmode, true);
1258           t->redraw |= TREDRAW_HARD;
1259           handled = true;
1260         }
1261         break;
1262       case TFM_MODAL_PLANE_Y:
1263         if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1264           transform_event_xyz_constraint(t, YKEY, cmode, true);
1265           t->redraw |= TREDRAW_HARD;
1266           handled = true;
1267         }
1268         break;
1269       case TFM_MODAL_PLANE_Z:
1270         if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1271           transform_event_xyz_constraint(t, ZKEY, cmode, true);
1272           t->redraw |= TREDRAW_HARD;
1273           handled = true;
1274         }
1275         break;
1276       case TFM_MODAL_CONS_OFF:
1277         if ((t->flag & T_NO_CONSTRAINT) == 0) {
1278           stopConstraint(t);
1279           t->redraw |= TREDRAW_HARD;
1280           handled = true;
1281         }
1282         break;
1283       case TFM_MODAL_ADD_SNAP:
1284         addSnapPoint(t);
1285         t->redraw |= TREDRAW_HARD;
1286         handled = true;
1287         break;
1288       case TFM_MODAL_REMOVE_SNAP:
1289         removeSnapPoint(t);
1290         t->redraw |= TREDRAW_HARD;
1291         handled = true;
1292         break;
1293       case TFM_MODAL_PROPSIZE:
1294         /* MOUSEPAN usage... */
1295         if (t->flag & T_PROP_EDIT) {
1296           float fac = 1.0f + 0.005f * (event->y - event->prevy);
1297           t->prop_size *= fac;
1298           if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1299             t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->clip_end),
1300                                   T_PROP_SIZE_MIN);
1301           }
1302           else {
1303             t->prop_size = max_ff(min_ff(t->prop_size, T_PROP_SIZE_MAX), T_PROP_SIZE_MIN);
1304           }
1305           calculatePropRatio(t);
1306           t->redraw |= TREDRAW_HARD;
1307           handled = true;
1308         }
1309         break;
1310       case TFM_MODAL_PROPSIZE_UP:
1311         if (t->flag & T_PROP_EDIT) {
1312           t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1313           if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1314             t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end);
1315           }
1316           else {
1317             t->prop_size = min_ff(t->prop_size, T_PROP_SIZE_MAX);
1318           }
1319           calculatePropRatio(t);
1320           t->redraw |= TREDRAW_HARD;
1321           handled = true;
1322         }
1323         break;
1324       case TFM_MODAL_PROPSIZE_DOWN:
1325         if (t->flag & T_PROP_EDIT) {
1326           t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1327           t->prop_size = max_ff(t->prop_size, T_PROP_SIZE_MIN);
1328           calculatePropRatio(t);
1329           t->redraw |= TREDRAW_HARD;
1330           handled = true;
1331         }
1332         break;
1333       case TFM_MODAL_AUTOIK_LEN_INC:
1334         if (t->flag & T_AUTOIK) {
1335           transform_autoik_update(t, 1);
1336           t->redraw |= TREDRAW_HARD;
1337           handled = true;
1338         }
1339         break;
1340       case TFM_MODAL_AUTOIK_LEN_DEC:
1341         if (t->flag & T_AUTOIK) {
1342           transform_autoik_update(t, -1);
1343           t->redraw |= TREDRAW_HARD;
1344           handled = true;
1345         }
1346         break;
1347       case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
1348         if (t->spacetype == SPACE_NODE) {
1349           SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
1350
1351           BLI_assert(t->sa->spacetype == t->spacetype);
1352
1353           if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT) {
1354             snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_LEFT;
1355           }
1356           else if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_LEFT) {
1357             snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_RIGHT;
1358           }
1359           else {
1360             BLI_assert(0);
1361           }
1362
1363           t->redraw |= TREDRAW_SOFT;
1364         }
1365         break;
1366       /* Those two are only handled in transform's own handler, see T44634! */
1367       case TFM_MODAL_EDGESLIDE_UP:
1368       case TFM_MODAL_EDGESLIDE_DOWN:
1369       default:
1370         break;
1371     }
1372   }
1373   /* else do non-mapped events */
1374   else if (event->val == KM_PRESS) {
1375     switch (event->type) {
1376       case RIGHTMOUSE:
1377         t->state = TRANS_CANCEL;
1378         handled = true;
1379         break;
1380       /* enforce redraw of transform when modifiers are used */
1381       case LEFTSHIFTKEY:
1382       case RIGHTSHIFTKEY:
1383         t->modifiers |= MOD_CONSTRAINT_PLANE;
1384         t->redraw |= TREDRAW_HARD;
1385         handled = true;
1386         break;
1387
1388       case SPACEKEY:
1389         t->state = TRANS_CONFIRM;
1390         handled = true;
1391         break;
1392
1393       case MIDDLEMOUSE:
1394         if ((t->flag & T_NO_CONSTRAINT) == 0) {
1395           /* exception for switching to dolly, or trackball, in camera view */
1396           if (t->flag & T_CAMERA) {
1397             if (t->mode == TFM_TRANSLATION) {
1398               setLocalConstraint(t, (CON_AXIS2), TIP_("along local Z"));
1399             }
1400             else if (t->mode == TFM_ROTATION) {
1401               restoreTransObjects(t);
1402               initTrackball(t);
1403             }
1404           }
1405           else {
1406             t->modifiers |= MOD_CONSTRAINT_SELECT;
1407             if (t->con.mode & CON_APPLY) {
1408               stopConstraint(t);
1409             }
1410             else {
1411               if (event->shift) {
1412                 /* bit hackish... but it prevents mmb select to print the
1413                  * orientation from menu */
1414                 float mati[3][3];
1415                 strcpy(t->spacename, "global");
1416                 unit_m3(mati);
1417                 initSelectConstraint(t, mati);
1418               }
1419               else {
1420                 initSelectConstraint(t, t->spacemtx);
1421               }
1422               postSelectConstraint(t);
1423             }
1424           }
1425           t->redraw |= TREDRAW_HARD;
1426           handled = true;
1427         }
1428         break;
1429       case ESCKEY:
1430         t->state = TRANS_CANCEL;
1431         handled = true;
1432         break;
1433       case PADENTER:
1434       case RETKEY:
1435         t->state = TRANS_CONFIRM;
1436         handled = true;
1437         break;
1438       case GKEY:
1439         /* only switch when... */
1440         if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) {
1441           restoreTransObjects(t);
1442           resetTransModal(t);
1443           resetTransRestrictions(t);
1444           initTranslation(t);
1445           initSnapping(t, NULL);  // need to reinit after mode change
1446           t->redraw |= TREDRAW_HARD;
1447           handled = true;
1448         }
1449         break;
1450       case SKEY:
1451         /* only switch when... */
1452         if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL)) {
1453           restoreTransObjects(t);
1454           resetTransModal(t);
1455           resetTransRestrictions(t);
1456           initResize(t);
1457           initSnapping(t, NULL);  // need to reinit after mode change
1458           t->redraw |= TREDRAW_HARD;
1459           handled = true;
1460         }
1461         break;
1462       case RKEY:
1463         /* only switch when... */
1464         if (!(t->options & CTX_TEXTURE)) {
1465           if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION)) {
1466             restoreTransObjects(t);
1467             resetTransModal(t);
1468             resetTransRestrictions(t);
1469
1470             if (t->mode == TFM_ROTATION) {
1471               initTrackball(t);
1472             }
1473             else {
1474               initRotation(t);
1475             }
1476             initSnapping(t, NULL);  // need to reinit after mode change
1477             t->redraw |= TREDRAW_HARD;
1478             handled = true;
1479           }
1480         }
1481         break;
1482       case CKEY:
1483         if (event->alt) {
1484           if (!(t->options & CTX_NO_PET)) {
1485             t->flag ^= T_PROP_CONNECTED;
1486             sort_trans_data_dist(t);
1487             calculatePropRatio(t);
1488             t->redraw = TREDRAW_HARD;
1489             handled = true;
1490           }
1491         }
1492         break;
1493       case OKEY:
1494         if (t->flag & T_PROP_EDIT && event->shift) {
1495           t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1496           calculatePropRatio(t);
1497           t->redraw |= TREDRAW_HARD;
1498           handled = true;
1499         }
1500         break;
1501       case PADPLUSKEY:
1502         if (event->alt && t->flag & T_PROP_EDIT) {
1503           t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1504           if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1505             t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end);
1506           }
1507           calculatePropRatio(t);
1508           t->redraw = TREDRAW_HARD;
1509           handled = true;
1510         }
1511         break;
1512       case PAGEUPKEY:
1513       case WHEELDOWNMOUSE:
1514         if (t->flag & T_AUTOIK) {
1515           transform_autoik_update(t, 1);
1516         }
1517         else {
1518           view_editmove(event->type);
1519         }
1520         t->redraw = TREDRAW_HARD;
1521         handled = true;
1522         break;
1523       case PADMINUS:
1524         if (event->alt && t->flag & T_PROP_EDIT) {
1525           t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1526           calculatePropRatio(t);
1527           t->redraw = TREDRAW_HARD;
1528           handled = true;
1529         }
1530         break;
1531       case PAGEDOWNKEY:
1532       case WHEELUPMOUSE:
1533         if (t->flag & T_AUTOIK) {
1534           transform_autoik_update(t, -1);
1535         }
1536         else {
1537           view_editmove(event->type);
1538         }
1539         t->redraw = TREDRAW_HARD;
1540         handled = true;
1541         break;
1542       case LEFTALTKEY:
1543       case RIGHTALTKEY:
1544         if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1545           t->flag |= T_ALT_TRANSFORM;
1546           t->redraw |= TREDRAW_HARD;
1547           handled = true;
1548         }
1549         break;
1550       case NKEY:
1551         if (ELEM(t->mode, TFM_ROTATION)) {
1552           if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
1553             restoreTransObjects(t);
1554             resetTransModal(t);
1555             resetTransRestrictions(t);
1556             initNormalRotation(t);
1557             t->redraw = TREDRAW_HARD;
1558             handled = true;
1559           }
1560         }
1561         break;
1562       default:
1563         break;
1564     }
1565
1566     /* Snapping key events */
1567     t->redraw |= handleSnapping(t, event);
1568   }
1569   else if (event->val == KM_RELEASE) {
1570     switch (event->type) {
1571       case LEFTSHIFTKEY:
1572       case RIGHTSHIFTKEY:
1573         t->modifiers &= ~MOD_CONSTRAINT_PLANE;
1574         t->redraw |= TREDRAW_HARD;
1575         handled = true;
1576         break;
1577
1578       case MIDDLEMOUSE:
1579         if ((t->flag & T_NO_CONSTRAINT) == 0) {
1580           t->modifiers &= ~MOD_CONSTRAINT_SELECT;
1581           postSelectConstraint(t);
1582           t->redraw |= TREDRAW_HARD;
1583           handled = true;
1584         }
1585         break;
1586       case LEFTALTKEY:
1587       case RIGHTALTKEY:
1588         if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1589           t->flag &= ~T_ALT_TRANSFORM;
1590           t->redraw |= TREDRAW_HARD;
1591           handled = true;
1592         }
1593         break;
1594       default:
1595         break;
1596     }
1597
1598     /* confirm transform if launch key is released after mouse move */
1599     if ((t->flag & T_RELEASE_CONFIRM) && event->type == t->launch_event) {
1600       t->state = TRANS_CONFIRM;
1601     }
1602   }
1603
1604   /* if we change snap options, get the unsnapped values back */
1605   if ((mode_prev != t->mode) || ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) !=
1606                                  (modifiers_prev & (MOD_SNAP | MOD_SNAP_INVERT)))) {
1607     applyMouseInput(t, &t->mouse, t->mval, t->values);
1608   }
1609
1610   /* Per transform event, if present */
1611   if (t->handleEvent && (!handled ||
1612                          /* Needed for vertex slide, see [#38756] */
1613                          (event->type == MOUSEMOVE))) {
1614     t->redraw |= t->handleEvent(t, event);
1615   }
1616
1617   /* Try to init modal numinput now, if possible. */
1618   if (!(handled || t->redraw) && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1619       handleNumInput(t->context, &(t->num), event)) {
1620     t->redraw |= TREDRAW_HARD;
1621     handled = true;
1622   }
1623
1624   if (t->redraw && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
1625     WM_window_status_area_tag_redraw(CTX_wm_window(t->context));
1626   }
1627
1628   if (handled || t->redraw) {
1629     return 0;
1630   }
1631   else {
1632     return OPERATOR_PASS_THROUGH;
1633   }
1634 }
1635
1636 bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
1637 {
1638   TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
1639   bool success;
1640
1641   t->context = C;
1642
1643   t->state = TRANS_RUNNING;
1644
1645   /* avoid calculating PET */
1646   t->options = CTX_NO_PET;
1647
1648   t->mode = TFM_DUMMY;
1649
1650   initTransInfo(C, t, NULL, NULL);
1651
1652   /* avoid doing connectivity lookups (when V3D_AROUND_LOCAL_ORIGINS is set) */
1653   t->around = V3D_AROUND_CENTER_BOUNDS;
1654
1655   createTransData(C, t);  // make TransData structs from selection
1656
1657   t->around = centerMode;  // override userdefined mode
1658
1659   if (t->data_len_all == 0) {
1660     success = false;
1661   }
1662   else {
1663     success = true;
1664
1665     calculateCenter(t);
1666
1667     if (cent2d) {
1668       copy_v2_v2(cent2d, t->center2d);
1669     }
1670
1671     if (cent3d) {
1672       // Copy center from constraint center. Transform center can be local
1673       copy_v3_v3(cent3d, t->center_global);
1674     }
1675   }
1676
1677   /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
1678   special_aftertrans_update(C, t);
1679
1680   postTrans(C, t);
1681
1682   MEM_freeN(t);
1683
1684   return success;
1685 }
1686
1687 typedef enum {
1688   UP,
1689   DOWN,
1690   LEFT,
1691   RIGHT,
1692 } ArrowDirection;
1693
1694 #define POS_INDEX 0
1695 /* NOTE: this --^ is a bit hackish, but simplifies GPUVertFormat usage among functions
1696  * private to this file  - merwin
1697  */
1698
1699 static void drawArrow(ArrowDirection d, short offset, short length, short size)
1700 {
1701   immBegin(GPU_PRIM_LINES, 6);
1702
1703   switch (d) {
1704     case LEFT:
1705       offset = -offset;
1706       length = -length;
1707       size = -size;
1708       ATTR_FALLTHROUGH;
1709     case RIGHT:
1710       immVertex2f(POS_INDEX, offset, 0);
1711       immVertex2f(POS_INDEX, offset + length, 0);
1712       immVertex2f(POS_INDEX, offset + length, 0);
1713       immVertex2f(POS_INDEX, offset + length - size, -size);
1714       immVertex2f(POS_INDEX, offset + length, 0);
1715       immVertex2f(POS_INDEX, offset + length - size, size);
1716       break;
1717
1718     case DOWN:
1719       offset = -offset;
1720       length = -length;
1721       size = -size;
1722       ATTR_FALLTHROUGH;
1723     case UP:
1724       immVertex2f(POS_INDEX, 0, offset);
1725       immVertex2f(POS_INDEX, 0, offset + length);
1726       immVertex2f(POS_INDEX, 0, offset + length);
1727       immVertex2f(POS_INDEX, -size, offset + length - size);
1728       immVertex2f(POS_INDEX, 0, offset + length);
1729       immVertex2f(POS_INDEX, size, offset + length - size);
1730       break;
1731   }
1732
1733   immEnd();
1734 }
1735
1736 static void drawArrowHead(ArrowDirection d, short size)
1737 {
1738   immBegin(GPU_PRIM_LINES, 4);
1739
1740   switch (d) {
1741     case LEFT:
1742       size = -size;
1743       ATTR_FALLTHROUGH;
1744     case RIGHT:
1745       immVertex2f(POS_INDEX, 0, 0);
1746       immVertex2f(POS_INDEX, -size, -size);
1747       immVertex2f(POS_INDEX, 0, 0);
1748       immVertex2f(POS_INDEX, -size, size);
1749       break;
1750
1751     case DOWN:
1752       size = -size;
1753       ATTR_FALLTHROUGH;
1754     case UP:
1755       immVertex2f(POS_INDEX, 0, 0);
1756       immVertex2f(POS_INDEX, -size, -size);
1757       immVertex2f(POS_INDEX, 0, 0);
1758       immVertex2f(POS_INDEX, size, -size);
1759       break;
1760   }
1761
1762   immEnd();
1763 }
1764
1765 static void drawArc(float size, float angle_start, float angle_end, int segments)
1766 {
1767   float delta = (angle_end - angle_start) / segments;
1768   float angle;
1769   int a;
1770
1771   immBegin(GPU_PRIM_LINE_STRIP, segments + 1);
1772
1773   for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
1774     immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size);
1775   }
1776   immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size);
1777
1778   immEnd();
1779 }
1780
1781 static bool helpline_poll(bContext *C)
1782 {
1783   ARegion *ar = CTX_wm_region(C);
1784
1785   if (ar && ar->regiontype == RGN_TYPE_WINDOW) {
1786     return 1;
1787   }
1788   return 0;
1789 }
1790
1791 static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
1792 {
1793   TransInfo *t = (TransInfo *)customdata;
1794
1795   if (t->helpline != HLP_NONE) {
1796     float cent[2];
1797     const float mval[3] = {
1798         x,
1799         y,
1800         0.0f,
1801     };
1802     float tmval[2] = {
1803         (float)t->mval[0],
1804         (float)t->mval[1],
1805     };
1806
1807     projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO);
1808     /* Offset the values for the area region. */
1809     const float offset[2] = {
1810         t->ar->winrct.xmin,
1811         t->ar->winrct.ymin,
1812     };
1813
1814     for (int i = 0; i < 2; i++) {
1815       cent[i] += offset[i];
1816       tmval[i] += offset[i];
1817     }
1818
1819     GPU_matrix_push();
1820
1821     /* Dashed lines first. */
1822     if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) {
1823       const uint shdr_pos = GPU_vertformat_attr_add(
1824           immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1825
1826       UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */
1827       BLI_assert(shdr_pos == POS_INDEX);
1828
1829       GPU_line_width(1.0f);
1830
1831       immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
1832
1833       float viewport_size[4];
1834       GPU_viewport_size_get_f(viewport_size);
1835       immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
1836
1837       immUniform1i("colors_len", 0); /* "simple" mode */
1838       immUniformThemeColor(TH_VIEW_OVERLAY);
1839       immUniform1f("dash_width", 6.0f);
1840       immUniform1f("dash_factor", 0.5f);
1841
1842       immBegin(GPU_PRIM_LINES, 2);
1843       immVertex2fv(POS_INDEX, cent);
1844       immVertex2f(POS_INDEX, tmval[0], tmval[1]);
1845       immEnd();
1846
1847       immUnbindProgram();
1848     }
1849
1850     /* And now, solid lines. */
1851     uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1852     UNUSED_VARS_NDEBUG(pos); /* silence warning */
1853     BLI_assert(pos == POS_INDEX);
1854     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1855
1856     switch (t->helpline) {
1857       case HLP_SPRING:
1858         immUniformThemeColor(TH_VIEW_OVERLAY);
1859
1860         GPU_matrix_translate_3fv(mval);
1861         GPU_matrix_rotate_axis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z');
1862
1863         GPU_line_width(3.0f);
1864         drawArrow(UP, 5, 10, 5);
1865         drawArrow(DOWN, 5, 10, 5);
1866         break;
1867       case HLP_HARROW:
1868         immUniformThemeColor(TH_VIEW_OVERLAY);
1869         GPU_matrix_translate_3fv(mval);
1870
1871         GPU_line_width(3.0f);
1872         drawArrow(RIGHT, 5, 10, 5);
1873         drawArrow(LEFT, 5, 10, 5);
1874         break;
1875       case HLP_VARROW:
1876         immUniformThemeColor(TH_VIEW_OVERLAY);
1877
1878         GPU_matrix_translate_3fv(mval);
1879
1880         GPU_line_width(3.0f);
1881         drawArrow(UP, 5, 10, 5);
1882         drawArrow(DOWN, 5, 10, 5);
1883         break;
1884       case HLP_CARROW: {
1885         /* Draw arrow based on direction defined by custom-points. */
1886         immUniformThemeColor(TH_VIEW_OVERLAY);
1887
1888         GPU_matrix_translate_3fv(mval);
1889
1890         GPU_line_width(3.0f);
1891
1892         const int *data = t->mouse.data;
1893         const float dx = data[2] - data[0], dy = data[3] - data[1];
1894         const float angle = -atan2f(dx, dy);
1895
1896         GPU_matrix_push();
1897
1898         GPU_matrix_rotate_axis(RAD2DEGF(angle), 'Z');
1899
1900         drawArrow(UP, 5, 10, 5);
1901         drawArrow(DOWN, 5, 10, 5);
1902
1903         GPU_matrix_pop();
1904         break;
1905       }
1906       case HLP_ANGLE: {
1907         float dx = tmval[0] - cent[0], dy = tmval[1] - cent[1];
1908         float angle = atan2f(dy, dx);
1909         float dist = hypotf(dx, dy);
1910         float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
1911         float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
1912
1913         immUniformThemeColor(TH_VIEW_OVERLAY);
1914
1915         GPU_matrix_translate_3f(cent[0] - tmval[0] + mval[0], cent[1] - tmval[1] + mval[1], 0);
1916
1917         GPU_line_width(3.0f);
1918         drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
1919         drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
1920
1921         GPU_matrix_push();
1922
1923         GPU_matrix_translate_3f(
1924             cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
1925         GPU_matrix_rotate_axis(RAD2DEGF(angle - delta_angle), 'Z');
1926
1927         drawArrowHead(DOWN, 5);
1928
1929         GPU_matrix_pop();
1930
1931         GPU_matrix_translate_3f(
1932             cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
1933         GPU_matrix_rotate_axis(RAD2DEGF(angle + delta_angle), 'Z');
1934
1935         drawArrowHead(UP, 5);
1936         break;
1937       }
1938       case HLP_TRACKBALL: {
1939         unsigned char col[3], col2[3];
1940         UI_GetThemeColor3ubv(TH_GRID, col);
1941
1942         GPU_matrix_translate_3fv(mval);
1943
1944         GPU_line_width(3.0f);
1945
1946         UI_make_axis_color(col, col2, 'X');
1947         immUniformColor3ubv(col2);
1948
1949         drawArrow(RIGHT, 5, 10, 5);
1950         drawArrow(LEFT, 5, 10, 5);
1951
1952         UI_make_axis_color(col, col2, 'Y');
1953         immUniformColor3ubv(col2);
1954
1955         drawArrow(UP, 5, 10, 5);
1956         drawArrow(DOWN, 5, 10, 5);
1957         break;
1958       }
1959     }
1960
1961     immUnbindProgram();
1962     GPU_matrix_pop();
1963   }
1964 }
1965
1966 static bool transinfo_show_overlay(const struct bContext *C, TransInfo *t, ARegion *ar)
1967 {
1968   /* Don't show overlays when not the active view and when overlay is disabled: T57139 */
1969   bool ok = false;
1970   if (ar == t->ar) {
1971     ok = true;
1972   }
1973   else {
1974     ScrArea *sa = CTX_wm_area(C);
1975     if (sa->spacetype == SPACE_VIEW3D) {
1976       View3D *v3d = sa->spacedata.first;
1977       if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) {
1978         ok = true;
1979       }
1980     }
1981   }
1982   return ok;
1983 }
1984
1985 static void drawTransformView(const struct bContext *C, ARegion *ar, void *arg)
1986 {
1987   TransInfo *t = arg;
1988
1989   if (!transinfo_show_overlay(C, t, ar)) {
1990     return;
1991   }
1992
1993   GPU_line_width(1.0f);
1994
1995   drawConstraint(t);
1996   drawPropCircle(C, t);
1997   drawSnapping(C, t);
1998
1999   if (ar == t->ar) {
2000     /* edge slide, vert slide */
2001     drawEdgeSlide(t);
2002     drawVertSlide(t);
2003
2004     /* Rotation */
2005     drawDial3d(t);
2006   }
2007 }
2008
2009 /* just draw a little warning message in the top-right corner of the viewport
2010  * to warn that autokeying is enabled */
2011 static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar)
2012 {
2013   const char *printable = IFACE_("Auto Keying On");
2014   float printable_size[2];
2015   int xco, yco;
2016
2017   const rcti *rect = ED_region_visible_rect(ar);
2018
2019   const int font_id = BLF_default();
2020   BLF_width_and_height(
2021       font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
2022
2023   xco = (rect->xmax - U.widget_unit) - (int)printable_size[0];
2024   yco = (rect->ymax - U.widget_unit);
2025
2026   /* warning text (to clarify meaning of overlays)
2027    * - original color was red to match the icon, but that clashes badly with a less nasty border
2028    */
2029   unsigned char color[3];
2030   UI_GetThemeColorShade3ubv(TH_TEXT_HI, -50, color);
2031   BLF_color3ubv(font_id, color);
2032 #ifdef WITH_INTERNATIONAL
2033   BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
2034 #else
2035   BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
2036 #endif
2037
2038   /* autokey recording icon... */
2039   GPU_blend_set_func_separate(
2040       GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
2041   GPU_blend(true);
2042
2043   xco -= U.widget_unit;
2044   yco -= (int)printable_size[1] / 2;
2045
2046   UI_icon_draw(xco, yco, ICON_REC);
2047
2048   GPU_blend(false);
2049 }
2050
2051 static void drawTransformPixel(const struct bContext *C, ARegion *ar, void *arg)
2052 {
2053   TransInfo *t = arg;
2054
2055   if (!transinfo_show_overlay(C, t, ar)) {
2056     return;
2057   }
2058
2059   if (ar == t->ar) {
2060     Scene *scene = t->scene;
2061     ViewLayer *view_layer = t->view_layer;
2062     Object *ob = OBACT(view_layer);
2063
2064     /* draw auto-key-framing hint in the corner
2065      * - only draw if enabled (advanced users may be distracted/annoyed),
2066      *   for objects that will be autokeyframed (no point otherwise),
2067      *   AND only for the active region (as showing all is too overwhelming)
2068      */
2069     if ((U.autokey_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
2070       if (ar == t->ar) {
2071         if (t->flag & (T_OBJECT | T_POSE)) {
2072           if (ob && autokeyframe_cfra_can_key(scene, &ob->id)) {
2073             drawAutoKeyWarning(t, ar);
2074           }
2075         }
2076       }
2077     }
2078   }
2079 }
2080
2081 /**
2082  * \see #initTransform which reads values from the operator.
2083  */
2084 void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
2085 {
2086   ToolSettings *ts = CTX_data_tool_settings(C);
2087   int proportional = 0;
2088   PropertyRNA *prop;
2089
2090   // Save back mode in case we're in the generic operator
2091   if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
2092     RNA_property_enum_set(op->ptr, prop, t->mode);
2093   }
2094
2095   if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
2096     if (RNA_property_array_check(prop)) {
2097       RNA_property_float_set_array(op->ptr, prop, t->values_final);
2098     }
2099     else {
2100       RNA_property_float_set(op->ptr, prop, t->values_final[0]);
2101     }
2102   }
2103
2104   if (t->flag & T_PROP_EDIT_ALL) {
2105     if (t->flag & T_PROP_EDIT) {
2106       proportional |= PROP_EDIT_USE;
2107     }
2108     if (t->flag & T_PROP_CONNECTED) {
2109       proportional |= PROP_EDIT_CONNECTED;
2110     }
2111     if (t->flag & T_PROP_PROJECTED) {
2112       proportional |= PROP_EDIT_PROJECTED;
2113     }
2114   }
2115
2116   // If modal, save settings back in scene if not set as operator argument
2117   if ((t->flag & T_MODAL) || (op->flag & OP_IS_REPEAT)) {
2118     /* save settings if not set in operator */
2119
2120     /* skip saving proportional edit if it was not actually used */
2121     if (!(t->options & CTX_NO_PET)) {
2122       if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit")) &&
2123           !RNA_property_is_set(op->ptr, prop)) {
2124         if (t->spacetype == SPACE_GRAPH) {
2125           ts->proportional_fcurve = proportional;
2126         }
2127         else if (t->spacetype == SPACE_ACTION) {
2128           ts->proportional_action = proportional;
2129         }
2130         else if (t->obedit_type != -1) {
2131           ts->proportional_edit = proportional;
2132         }
2133         else if (t->options & CTX_MASK) {
2134           ts->proportional_mask = proportional != 0;
2135         }
2136         else if ((t->options & CTX_CURSOR) == 0) {
2137           ts->proportional_objects = proportional != 0;
2138         }
2139       }
2140
2141       if ((prop = RNA_struct_find_property(op->ptr, "proportional_size"))) {
2142         ts->proportional_size = RNA_property_is_set(op->ptr, prop) ?
2143                                     RNA_property_float_get(op->ptr, prop) :
2144                                     t->prop_size;
2145       }
2146
2147       if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
2148           !RNA_property_is_set(op->ptr, prop)) {
2149         ts->prop_mode = t->prop_mode;
2150       }
2151     }
2152
2153     if (t->spacetype == SPACE_VIEW3D) {
2154       if ((prop = RNA_struct_find_property(op->ptr, "orient_type")) &&
2155           !RNA_property_is_set(op->ptr, prop) &&
2156           (t->orientation.user != V3D_ORIENT_CUSTOM_MATRIX)) {
2157         TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT];
2158         orient_slot->type = t->orientation.user;
2159         BLI_assert(((orient_slot->index_custom == -1) && (t->orientation.custom == NULL)) ||
2160                    (BKE_scene_transform_orientation_get_index(t->scene, t->orientation.custom) ==
2161                     orient_slot->index_custom));
2162       }
2163     }
2164   }
2165
2166   if (t->flag & T_MODAL) {
2167     /* do we check for parameter? */
2168     if (transformModeUseSnap(t)) {
2169       if (t->modifiers & MOD_SNAP) {
2170         ts->snap_flag |= SCE_SNAP;
2171       }
2172       else {
2173         ts->snap_flag &= ~SCE_SNAP;
2174       }
2175     }
2176   }
2177
2178   if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit"))) {
2179     RNA_property_boolean_set(op->ptr, prop, proportional & PROP_EDIT_USE);
2180     RNA_boolean_set(op->ptr, "use_proportional_connected", proportional & PROP_EDIT_CONNECTED);
2181     RNA_boolean_set(op->ptr, "use_proportional_projected", proportional & PROP_EDIT_PROJECTED);
2182     RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
2183     RNA_float_set(op->ptr, "proportional_size", t->prop_size);
2184   }
2185
2186   if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
2187     RNA_property_boolean_set(op->ptr, prop, (t->flag & T_NO_MIRROR) == 0);
2188   }
2189
2190   /* Orientation used for redo. */
2191   const bool use_orient_axis = (t->orient_matrix_is_set &&
2192                                 (RNA_struct_find_property(op->ptr, "orient_axis") != NULL));
2193   short orientation;
2194   if (t->con.mode & CON_APPLY) {
2195     orientation = t->con.orientation;
2196     if (orientation == V3D_ORIENT_CUSTOM) {
2197       const int orientation_index_custom = BKE_scene_transform_orientation_get_index(
2198           t->scene, t->orientation.custom);
2199       /* Maybe we need a t->con.custom_orientation?
2200        * Seems like it would always match t->orientation.custom. */
2201       orientation = V3D_ORIENT_CUSTOM + orientation_index_custom;
2202       BLI_assert(orientation >= V3D_ORIENT_CUSTOM);
2203     }
2204   }
2205   else if ((t->orientation.user == V3D_ORIENT_CUSTOM_MATRIX) &&
2206            (prop = RNA_struct_find_property(op->ptr, "orient_matrix_type"))) {
2207     orientation = RNA_property_enum_get(op->ptr, prop);
2208   }
2209   else if (use_orient_axis) {
2210     /* We're not using an orientation, use the fallback. */
2211     orientation = t->orientation.unset;
2212   }
2213   else {
2214     orientation = V3D_ORIENT_GLOBAL;
2215   }
2216
2217   if ((prop = RNA_struct_find_property(op->ptr, "orient_axis"))) {
2218     if (t->flag & T_MODAL) {
2219       if (t->con.mode & CON_APPLY) {
2220         int orient_axis = constraintModeToIndex(t);
2221         if (orient_axis != -1) {
2222           RNA_property_enum_set(op->ptr, prop, orient_axis);
2223         }
2224       }
2225       else {
2226         RNA_property_enum_set(op->ptr, prop, t->orient_axis);
2227       }
2228     }
2229   }
2230   if ((prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) {
2231     if (t->flag & T_MODAL) {
2232       RNA_property_enum_set(op->ptr, prop, t->orient_axis_ortho);
2233     }
2234   }
2235
2236   if ((prop = RNA_struct_find_property(op->ptr, "orient_matrix"))) {
2237     if (t->flag & T_MODAL) {
2238       if (orientation != V3D_ORIENT_CUSTOM_MATRIX) {
2239         if (t->flag & T_MODAL) {
2240           RNA_enum_set(op->ptr, "orient_matrix_type", orientation);
2241         }
2242       }
2243       if (t->con.mode & CON_APPLY) {
2244         RNA_float_set_array(op->ptr, "orient_matrix", &t->con.mtx[0][0]);
2245       }
2246       else if (use_orient_axis) {
2247         RNA_float_set_array(op->ptr, "orient_matrix", &t->orient_matrix[0][0]);
2248       }
2249       else {
2250         RNA_float_set_array(op->ptr, "orient_matrix", &t->spacemtx[0][0]);
2251       }
2252     }
2253   }
2254
2255   if ((prop = RNA_struct_find_property(op->ptr, "orient_type"))) {
2256     /* constraint orientation can be global, even if user selects something else
2257      * so use the orientation in the constraint if set */
2258
2259     /* Use 'orient_matrix' instead. */
2260     if (t->flag & T_MODAL) {
2261       if (orientation != V3D_ORIENT_CUSTOM_MATRIX) {
2262         RNA_property_enum_set(op->ptr, prop, orientation);
2263       }
2264     }
2265   }
2266
2267   if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
2268     bool constraint_axis[3] = {false, false, false};
2269     if (t->flag & T_MODAL) {
2270       /* Only set if needed, so we can hide in the UI when nothing is set.
2271        * See 'transform_poll_property'. */
2272       if (t->con.mode & CON_APPLY) {
2273         if (t->con.mode & CON_AXIS0) {
2274           constraint_axis[0] = true;
2275         }
2276         if (t->con.mode & CON_AXIS1) {
2277           constraint_axis[1] = true;
2278         }
2279         if (t->con.mode & CON_AXIS2) {
2280           constraint_axis[2] = true;
2281         }
2282       }
2283       if (ELEM(true, UNPACK3(constraint_axis))) {
2284         RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
2285       }
2286     }
2287   }
2288
2289   {
2290     const char *prop_id = NULL;
2291     bool prop_state = true;
2292     if (t->mode == TFM_SHRINKFATTEN) {
2293       prop_id = "use_even_offset";
2294       prop_state = false;
2295     }
2296
2297     if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) {
2298       RNA_property_boolean_set(op->ptr, prop, ((t->flag & T_ALT_TRANSFORM) == 0) == prop_state);
2299     }
2300   }
2301
2302   if ((t->options & CTX_SCULPT) && !(t->options & CTX_PAINT_CURVE)) {
2303     ED_sculpt_end_transform(C);
2304   }
2305
2306   if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
2307     RNA_property_boolean_set(
2308         op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
2309   }
2310 }
2311
2312 /**
2313  * \note  caller needs to free 't' on a 0 return
2314  * \warning  \a event might be NULL (when tweaking from redo panel)
2315  * \see #saveTransform which writes these values back.
2316  */
2317 bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
2318 {
2319   int options = 0;
2320   PropertyRNA *prop;
2321
2322   t->context = C;
2323
2324   /* added initialize, for external calls to set stuff in TransInfo, like undo string */
2325
2326   t->state = TRANS_STARTING;
2327
2328   if ((prop = RNA_struct_find_property(op->ptr, "cursor_transform")) &&
2329       RNA_property_is_set(op->ptr, prop)) {
2330     if (RNA_property_boolean_get(op->ptr, prop)) {
2331       options |= CTX_CURSOR;
2332     }
2333   }
2334
2335   if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) &&
2336       RNA_property_is_set(op->ptr, prop)) {
2337     if (RNA_property_boolean_get(op->ptr, prop)) {
2338       options |= CTX_TEXTURE;
2339     }
2340   }
2341
2342   if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) &&
2343       RNA_property_is_set(op->ptr, prop)) {
2344     if (RNA_property_boolean_get(op->ptr, prop)) {
2345       options |= CTX_GPENCIL_STROKES;
2346     }
2347   }
2348
2349   if (CTX_wm_view3d(C) != NULL) {
2350     Object *ob = CTX_data_active_object(C);
2351     if (ob && ob->mode == OB_MODE_SCULPT && ob->sculpt) {
2352       options |= CTX_SCULPT;
2353     }
2354   }
2355
2356   t->options = options;
2357
2358   t->mode = mode;
2359
2360   /* Needed to translate tweak events to mouse buttons. */
2361   t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1;
2362   t->is_launch_event_tweak = event ? ISTWEAK(event->type) : false;
2363
2364   /* XXX Remove this when wm_operator_call_internal doesn't use window->eventstate
2365    * (which can have type = 0) */
2366   /* For gizmo only, so assume LEFTMOUSE. */
2367   if (t->launch_event == 0) {
2368     t->launch_event = LEFTMOUSE;
2369   }
2370
2371   unit_m3(t->spacemtx);
2372
2373   initTransInfo(C, t, op, event);
2374   initTransformOrientation(C, t);
2375
2376   if (t->spacetype == SPACE_VIEW3D) {
2377     t->draw_handle_apply = ED_region_draw_cb_activate(
2378         t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
2379     t->draw_handle_view = ED_region_draw_cb_activate(
2380         t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2381     t->draw_handle_pixel = ED_region_draw_cb_activate(
2382         t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2383     t->draw_handle_cursor = WM_paint_cursor_activate(
2384         CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
2385   }
2386   else if (t->spacetype == SPACE_IMAGE) {
2387     t->draw_handle_view = ED_region_draw_cb_activate(
2388         t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2389     t->draw_handle_cursor = WM_paint_cursor_activate(
2390         CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
2391   }
2392   else if (t->spacetype == SPACE_CLIP) {
2393     t->draw_handle_view = ED_region_draw_cb_activate(
2394         t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2395     t->draw_handle_cursor = WM_paint_cursor_activate(
2396         CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
2397   }
2398   else if (t->spacetype == SPACE_NODE) {
2399     t->draw_handle_view = ED_region_draw_cb_activate(
2400         t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2401     t->draw_handle_cursor = WM_paint_cursor_activate(
2402         CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
2403   }
2404   else if (t->spacetype == SPACE_GRAPH) {
2405     t->draw_handle_view = ED_region_draw_cb_activate(
2406         t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2407     t->draw_handle_cursor = WM_paint_cursor_activate(
2408         CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
2409   }
2410   else if (t->spacetype == SPACE_ACTION) {
2411     t->draw_handle_view = ED_region_draw_cb_activate(
2412         t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2413     t->draw_handle_cursor = WM_paint_cursor_activate(
2414         CTX_wm_manager(C), SPACE_TYPE_ANY, RGN_TYPE_ANY, helpline_poll, drawHelpline, t);
2415   }
2416
2417   createTransData(C, t);  // make TransData structs from selection
2418
2419   if ((t->options & CTX_SCULPT) && !(t->options & CTX_PAINT_CURVE)) {
2420     ED_sculpt_init_transform(C);
2421   }
2422
2423   if (t->data_len_all == 0) {
2424     postTrans(C, t);
2425     return 0;
2426   }
2427
2428   if (event) {
2429     /* keymap for shortcut header prints */
2430     t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
2431
2432     /* Stupid code to have Ctrl-Click on gizmo work ok.
2433      *
2434      * Do this only for translation/rotation/resize because only these
2435      * modes are available from gizmo and doing such check could
2436      * lead to keymap conflicts for other modes (see #31584)
2437      */
2438     if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
2439       wmKeyMapItem *kmi;
2440
2441       for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) {
2442         if (kmi->flag & KMI_INACTIVE) {
2443           continue;
2444         }
2445
2446         if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
2447           if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) && event->ctrl) ||
2448               (ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
2449               (ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) && event->alt) ||
2450               ((kmi->type == OSKEY) && event->oskey)) {
2451             t->modifiers |= MOD_SNAP_INVERT;
2452           }
2453           break;
2454         }
2455       }
2456     }
2457   }
2458
2459   initSnapping(t, op);  // Initialize snapping data AFTER mode flags
2460
2461   initSnapSpatial(t, t->snap_spatial);
2462
2463   /* EVIL! posemode code can switch translation to rotate when 1 bone is selected.
2464    * will be removed (ton) */
2465
2466   /* EVIL2: we gave as argument also texture space context bit... was cleared */
2467
2468   /* EVIL3: extend mode for animation editors also switches modes...
2469    * but is best way to avoid duplicate code */
2470   mode = t->mode;
2471
2472   calculatePropRatio(t);
2473   calculateCenter(t);
2474
2475   /* Overwrite initial values if operator supplied a non-null vector.
2476    *
2477    * Run before init functions so 'values_modal_offset' can be applied on mouse input.
2478    */
2479   BLI_assert(is_zero_v4(t->values_modal_offset));
2480   if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
2481     float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory  */
2482
2483     if (RNA_property_array_check(prop)) {
2484       RNA_float_get_array(op->ptr, "value", values);
2485     }
2486     else {
2487       values[0] = RNA_float_get(op->ptr, "value");
2488     }
2489
2490     copy_v4_v4(t->values, values);
2491
2492     if (t->flag & T_MODAL) {
2493       copy_v4_v4(t->values_modal_offset, values);
2494       t->redraw = TREDRAW_HARD;
2495     }
2496     else {
2497       copy_v4_v4(t->values, values);
2498       t->flag |= T_INPUT_IS_VALUES_FINAL;
2499     }
2500   }
2501
2502   if (event) {
2503     /* Initialize accurate transform to settings requested by keymap. */
2504     bool use_accurate = false;
2505     if ((prop = RNA_struct_find_property(op->ptr, "use_accurate")) &&
2506         RNA_property_is_set(op->ptr, prop)) {
2507       if (RNA_property_boolean_get(op->ptr, prop)) {
2508         use_accurate = true;
2509       }
2510     }
2511     initMouseInput(t, &t->mouse, t->center2d, event->mval, use_accurate);
2512   }
2513
2514   switch (mode) {
2515     case TFM_TRANSLATION:
2516       initTranslation(t);
2517       break;
2518     case TFM_ROTATION:
2519       initRotation(t);
2520       break;
2521     case TFM_RESIZE:
2522       initResize(t);
2523       break;
2524     case TFM_SKIN_RESIZE:
2525       initSkinResize(t);
2526       break;
2527     case TFM_TOSPHERE:
2528       initToSphere(t);
2529       break;
2530     case TFM_SHEAR:
2531       initShear(t);
2532       break;
2533     case TFM_BEND:
2534       initBend(t);
2535       break;
2536     case TFM_SHRINKFATTEN:
2537       initShrinkFatten(t);
2538       break;
2539     case TFM_TILT:
2540       initTilt(t);
2541       break;
2542     case TFM_CURVE_SHRINKFATTEN:
2543       initCurveShrinkFatten(t);
2544       break;
2545     case TFM_MASK_SHRINKFATTEN:
2546       initMaskShrinkFatten(t);
2547       break;
2548     case TFM_GPENCIL_SHRINKFATTEN:
2549       initGPShrinkFatten(t);
2550       break;
2551     case TFM_TRACKBALL:
2552       initTrackball(t);
2553       break;
2554     case TFM_PUSHPULL:
2555       initPushPull(t);
2556       break;
2557     case TFM_CREASE:
2558       initCrease(t);
2559       break;
2560     case TFM_BONESIZE: { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
2561       /* Note: we have to pick one, use the active object. */
2562       TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
2563       bArmature *arm = tc->poseobj->data;
2564       if (arm->drawtype == ARM_ENVELOPE) {
2565         initBoneEnvelope(t);
2566         t->mode = TFM_BONE_ENVELOPE_DIST;
2567       }
2568       else {
2569         initBoneSize(t);
2570       }
2571       break;
2572     }
2573     case TFM_BONE_ENVELOPE:
2574       initBoneEnvelope(t);
2575       break;
2576     case TFM_BONE_ENVELOPE_DIST:
2577       initBoneEnvelope(t);
2578       t->mode = TFM_BONE_ENVELOPE_DIST;
2579       break;
2580     case TFM_EDGE_SLIDE:
2581     case TFM_VERT_SLIDE: {
2582       const bool use_even = (op ? RNA_boolean_get(op->ptr, "use_even") : false);
2583       const bool flipped = (op ? RNA_boolean_get(op->ptr, "flipped") : false);
2584       const bool use_clamp = (op ? RNA_boolean_get(op->ptr, "use_clamp") : true);
2585       if (mode == TFM_EDGE_SLIDE) {
2586         const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true);
2587         initEdgeSlide_ex(t, use_double_side, use_even, flipped, use_clamp);
2588       }
2589       else {
2590         initVertSlide_ex(t, use_even, flipped, use_clamp);
2591       }
2592       break;
2593     }
2594     case TFM_BONE_ROLL:
2595       initBoneRoll(t);
2596       break;
2597     case TFM_TIME_TRANSLATE:
2598       initTimeTranslate(t);
2599       break;
2600     case TFM_TIME_SLIDE:
2601       initTimeSlide(t);
2602       break;
2603     case TFM_TIME_SCALE:
2604       initTimeScale(t);
2605       break;
2606     case TFM_TIME_DUPLICATE:
2607       /* same as TFM_TIME_EXTEND, but we need the mode info for later
2608        * so that duplicate-culling will work properly
2609        */
2610       if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
2611         initTranslation(t);
2612       }
2613       else {
2614         initTimeTranslate(t);
2615       }
2616       t->mode = mode;
2617       break;
2618     case TFM_TIME_EXTEND:
2619       /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
2620        * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
2621        * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
2622        * depending on which editor this was called from
2623        */
2624       if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
2625         initTranslation(t);
2626       }
2627       else {
2628         initTimeTranslate(t);
2629       }
2630       break;
2631     case TFM_BAKE_TIME:
2632       initBakeTime(t);
2633       break;
2634     case TFM_MIRROR:
2635       initMirror(t);
2636       break;
2637     case TFM_BWEIGHT:
2638       initBevelWeight(t);
2639       break;
2640     case TFM_ALIGN:
2641       initAlign(t);
2642       break;
2643     case TFM_SEQ_SLIDE:
2644       initSeqSlide(t);
2645       break;
2646     case TFM_NORMAL_ROTATION:
2647       initNormalRotation(t);
2648       break;
2649     case TFM_GPENCIL_OPACITY:
2650       initGPOpacity(t);
2651       break;
2652   }
2653
2654   if (t->state == TRANS_CANCEL) {
2655     postTrans(C, t);
2656     return 0;
2657   }
2658
2659   /* Transformation axis from operator */
2660   if ((prop = RNA_struct_find_property(op->ptr, "orient_axis")) &&
2661       RNA_property_is_set(op->ptr, prop)) {
2662     t->orient_axis = RNA_property_enum_get(op->ptr, prop);
2663   }
2664   if ((prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho")) &&
2665       RNA_property_is_set(op->ptr, prop)) {
2666     t->orient_axis_ortho = RNA_property_enum_get(op->ptr, prop);
2667   }
2668
2669   /* Constraint init from operator */
2670   if ((t->flag & T_MODAL) ||
2671       /* For mirror operator the constraint axes are effectively the values. */
2672       (RNA_struct_find_property(op->ptr, "value") == NULL)) {
2673     if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) &&
2674         RNA_property_is_set(op->ptr, prop)) {
2675       bool constraint_axis[3];
2676
2677       RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
2678
2679       if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
2680         t->con.mode |= CON_APPLY;
2681
2682         if (constraint_axis[0]) {
2683           t->con.mode |= CON_AXIS0;
2684         }
2685         if (constraint_axis[1]) {
2686           t->con.mode |= CON_AXIS1;
2687         }
2688         if (constraint_axis[2]) {
2689           t->con.mode |= CON_AXIS2;
2690         }
2691
2692         setUserConstraint(t, t->orientation.user, t->con.mode, "%s");
2693       }
2694     }
2695   }
2696   else {
2697     /* So we can adjust in non global orientation. */
2698     if (t->orientation.user != V3D_ORIENT_GLOBAL) {
2699       t->con.mode |= CON_APPLY | CON_AXIS0 | CON_AXIS1 | CON_AXIS2;
2700       setUserConstraint(t, t->orientation.user, t->con.mode, "%s");
2701     }
2702   }
2703
2704   /* Don't write into the values when non-modal because they are already set from operator redo
2705    * values. */
2706   if (t->flag & T_MODAL) {
2707     /* Setup the mouse input with initial values. */
2708     applyMouseInput(t, &t->mouse, t->mouse.imval, t->values);
2709   }
2710
2711   if ((prop = RNA_struct_find_property(op->ptr, "preserve_clnor"))) {
2712     if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
2713
2714       FOREACH_TRANS_DATA_CONTAINER (t, tc) {
2715         if ((((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH)) {
2716           BMEditMesh *em = NULL;  // BKE_editmesh_from_object(t->obedit);
2717           bool do_skip = false;
2718
2719           /* Currently only used for two of three most frequent transform ops,
2720            * can include more ops.
2721            * Note that scaling cannot be included here,
2722            * non-uniform scaling will affect normals. */
2723           if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION)) {
2724             if (em->bm->totvertsel == em->bm->totvert) {
2725               /* No need to invalidate if whole mesh is selected. */
2726               do_skip = true;
2727             }
2728           }
2729
2730           if (t->flag & T_MODAL) {
2731             RNA_property_boolean_set(op->ptr, prop, false);
2732           }
2733           else if (!do_skip) {
2734             const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop);
2735             if (preserve_clnor) {
2736               BKE_editmesh_lnorspace_update(em);
2737               t->flag |= T_CLNOR_REBUILD;
2738             }
2739             BM_lnorspace_invalidate(em->bm, true);
2740           }
2741         }
2742       }
2743     }
2744   }
2745
2746   t->context = NULL;
2747
2748   return 1;
2749 }
2750
2751 void transformApply(bContext *C, TransInfo *t)
2752 {
2753   t->context = C;
2754
2755   if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
2756     selectConstraint(t);
2757     if (t->transform) {
2758       t->transform(t, t->mval);  // calls recalcData()
2759       viewRedrawForce(C, t);
2760     }
2761     t->redraw = TREDRAW_NOTHING;
2762   }
2763   else if (t->redraw & TREDRAW_SOFT) {
2764     viewRedrawForce(C, t);
2765   }
2766
2767   /* If auto confirm is on, break after one pass */
2768   if (t->options & CTX_AUTOCONFIRM) {
2769     t->state = TRANS_CONFIRM;
2770   }
2771
2772   t->context = NULL;
2773 }
2774
2775 static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
2776 {
2777   TransInfo *t = arg;
2778
2779   if (t->redraw & TREDRAW_SOFT) {
2780     t->redraw |= TREDRAW_HARD;
2781     transformApply((bContext *)C, t);
2782   }
2783 }
2784
2785 int transformEnd(bContext *C, TransInfo *t)
2786 {
2787   int exit_code = OPERATOR_RUNNING_MODAL;
2788
2789   t->context = C;
2790
2791   if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
2792     /* handle restoring objects */
2793     if (t->state == TRANS_CANCEL) {
2794       /* exception, edge slide transformed UVs too */
2795       if (t->mode == TFM_EDGE_SLIDE) {
2796         doEdgeSlide(t, 0.0f);
2797       }
2798       else if (t->mode == TFM_VERT_SLIDE) {
2799         doVertSlide(t, 0.0f);
2800       }
2801
2802       exit_code = OPERATOR_CANCELLED;
2803       restoreTransObjects(t);  // calls recalcData()
2804     }
2805     else {
2806       if (t->flag & T_CLNOR_REBUILD) {
2807         FOREACH_TRANS_DATA_CONTAINER (t, tc) {
2808           BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
2809           BM_lnorspace_rebuild(em->bm, true);
2810         }
2811       }
2812       exit_code = OPERATOR_FINISHED;
2813     }
2814
2815     /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
2816     special_aftertrans_update(C, t);
2817
2818     /* free data */
2819     postTrans(C, t);
2820
2821     /* send events out for redraws */
2822     viewRedrawPost(C, t);
2823
2824     viewRedrawForce(C, t);
2825   }
2826
2827   t->context = NULL;
2828
2829   return exit_code;
2830 }
2831
2832 /* ************************** TRANSFORM LOCKS **************************** */
2833
2834 static void protectedTransBits(short protectflag, float vec[3])
2835 {
2836   if (protectflag & OB_LOCK_LOCX) {
2837     vec[0] = 0.0f;
2838   }
2839   if (protectflag & OB_LOCK_LOCY) {
2840     vec[1] = 0.0f;
2841   }
2842   if (protectflag & OB_LOCK_LOCZ) {
2843     vec[2] = 0.0f;
2844   }
2845 }
2846
2847 static void protectedSizeBits(short protectflag, float size[3])
2848 {
2849   if (protectflag & OB_LOCK_SCALEX) {
2850     size[0] = 1.0f;
2851   }
2852   if (protectflag & OB_LOCK_SCALEY) {
2853     size[1] = 1.0f;
2854   }
2855   if (protectflag & OB_LOCK_SCALEZ) {
2856     size[2] = 1.0f;
2857   }
2858 }
2859
2860 static void protectedRotateBits(short protectflag, float eul[3], const float oldeul[3])
2861 {
2862   if (protectflag & OB_LOCK_ROTX) {
2863     eul[0] = oldeul[0];
2864   }
2865   if (protectflag & OB_LOCK_ROTY) {
2866     eul[1] = oldeul[1];
2867   }
2868   if (protectflag & OB_LOCK_ROTZ) {
2869     eul[2] = oldeul[2];
2870   }
2871 }
2872
2873 /* this function only does the delta rotation */
2874 /* axis-angle is usually internally stored as quats... */
2875 static void protectedAxisAngleBits(
2876     short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle)
2877 {
2878   /* check that protection flags are set */
2879   if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0) {
2880     return;
2881   }
2882
2883   if (protectflag & OB_LOCK_ROT4D) {
2884     /* axis-angle getting limited as 4D entities that they are... */
2885     if (protectflag & OB_LOCK_ROTW) {
2886       *angle = oldAngle;
2887     }
2888     if (protectflag & OB_LOCK_ROTX) {
2889       axis[0] = oldAxis[0];
2890     }
2891     if (protectflag & OB_LOCK_ROTY) {
2892       axis[1] = oldAxis[1];
2893     }
2894     if (protectflag & OB_LOCK_ROTZ) {
2895       axis[2] = oldAxis[2];
2896     }
2897   }
2898   else {
2899     /* axis-angle get limited with euler... */
2900     float eul[3], oldeul[3];
2901
2902     axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle);
2903     axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle);
2904
2905     if (protectflag & OB_LOCK_ROTX) {
2906       eul[0] = oldeul[0];
2907     }
2908     if (protectflag & OB_LOCK_ROTY) {
2909       eul[1] = oldeul[1];
2910     }
2911     if (protectflag & OB_LOCK_ROTZ) {
2912       eul[2] = oldeul[2];
2913     }
2914
2915     eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT);
2916
2917     /* When converting to axis-angle,
2918      * we need a special exception for the case when there is no axis. */
2919     if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) {
2920       /* for now, rotate around y-axis then (so that it simply becomes the roll) */
2921       axis[1] = 1.0f;
2922     }
2923   }
2924 }
2925
2926 /* this function only does the delta rotation */
2927 static void protectedQuaternionBits(short protectflag, float quat[4], const float oldquat[4])
2928 {
2929   /* check that protection flags are set */
2930   if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0) {
2931     return;
2932   }
2933
2934   if (protectflag & OB_LOCK_ROT4D) {
2935     /* quaternions getting limited as 4D entities that they are... */
2936     if (protectflag & OB_LOCK_ROTW) {
2937       quat[0] = oldquat[0];
2938     }
2939     if (protectflag & OB_LOCK_ROTX) {
2940       quat[1] = oldquat[1];
2941     }
2942     if (protectflag & OB_LOCK_ROTY) {
2943       quat[2] = oldquat[2];
2944     }
2945     if (protectflag & OB_LOCK_ROTZ) {
2946       quat[3] = oldquat[3];
2947     }
2948   }
2949   else {
2950     /* quaternions get limited with euler... (compatibility mode) */
2951     float eul[3], oldeul[3], nquat[4], noldquat[4];
2952     float qlen;
2953
2954     qlen = normalize_qt_qt(nquat, quat);
2955     normalize_qt_qt(noldquat, oldquat);
2956
2957     quat_to_eul(eul, nquat);
2958     quat_to_eul(oldeul, noldquat);
2959
2960     if (protectflag & OB_LOCK_ROTX) {
2961       eul[0] = oldeul[0];
2962     }
2963     if (protectflag & OB_LOCK_ROTY) {
2964       eul[1] = oldeul[1];
2965     }
2966     if (protectflag & OB_LOCK_ROTZ) {
2967       eul[2] = oldeul[2];
2968     }
2969
2970     eul_to_quat(quat, eul);
2971
2972     /* restore original quat size */
2973     mul_qt_fl(quat, qlen);
2974
2975     /* quaternions flip w sign to accumulate rotations correctly */
2976     if ((nquat[0] < 0.0f && quat[0] > 0.0f) || (nquat[0] > 0.0f && quat[0] < 0.0f)) {
2977       mul_qt_fl(quat, -1.0f);
2978     }
2979   }
2980 }
2981
2982 /* ******************* TRANSFORM LIMITS ********************** */
2983
2984 static void constraintTransLim(TransInfo *t, TransData *td)
2985 {
2986   if (td->con) {
2987     const bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type(
2988         CONSTRAINT_TYPE_LOCLIMIT);
2989     const bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type(
2990         CONSTRAINT_TYPE_DISTLIMIT);
2991
2992     bConstraintOb cob = {NULL};
2993     bConstraint *con;
2994     float ctime = (float)(t->scene->r.cfra);
2995
2996     /* Make a temporary bConstraintOb for using these limit constraints
2997      * - they only care that cob->matrix is correctly set ;-)
2998      * - current space should be local
2999      */
3000     unit_m4(cob.matrix);
3001     copy_v3_v3(cob.matrix[3], td->loc);
3002
3003     /* Evaluate valid constraints */
3004     for (con = td->con; con; con = con->next) {
3005       const bConstraintTypeInfo *cti = NULL;
3006       ListBase targets = {NULL, NULL};
3007
3008       /* only consider constraint if enabled */
3009       if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) {
3010         continue;
3011       }
3012       if (con->enforce == 0.0f) {
3013         continue;
3014       }
3015
3016       /* only use it if it's tagged for this purpose (and the right type) */
3017       if (con->type == CONSTRAINT_TYPE_LOCLIMIT) {
3018         bLocLimitConstraint *data = con->data;
3019
3020         if ((data->flag2 & LIMIT_TRANSFORM) == 0) {
3021           continue;
3022         }
3023         cti = ctiLoc;
3024       }
3025       else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) {
3026         bDistLimitConstraint *data = con->data;
3027
3028         if ((data->flag & LIMITDIST_TRANSFORM) == 0) {
3029           continue;
3030         }
3031         cti = ctiDist;
3032       }
3033
3034       if (cti) {
3035         /* do space conversions */
3036         if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
3037           /* just multiply by td->mtx (this should be ok) */
3038           mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
3039         }
3040         else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
3041           /* skip... incompatible spacetype */
3042           continue;
3043         }
3044
3045         /* get constraint targets if needed */
3046         BKE_constraint_targets_for_solving_get(t->depsgraph, con, &cob, &targets, ctime);
3047
3048         /* do constraint */
3049         cti->evaluate_constraint(con, &cob, &targets);
3050
3051         /* convert spaces again */
3052         if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
3053           /* just multiply by td->smtx (this should be ok) */
3054           mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
3055         }
3056
3057         /* free targets list */
3058         BLI_freelistN(&targets);
3059       }
3060     }
3061
3062     /* copy results from cob->matrix */
3063     copy_v3_v3(td->loc, cob.matrix[3]);
3064   }
3065 }
3066
3067 static void constraintob_from_transdata(bConstraintOb *cob, TransData *td)
3068 {
3069   /* Make a temporary bConstraintOb for use by limit constraints
3070    * - they only care that cob->matrix is correctly set ;-)
3071    * - current space should be local
3072    */
3073   memset(cob, 0, sizeof(bConstraintOb));
3074   if (td->ext) {
3075     if (td->ext->rotOrder == ROT_MODE_QUAT) {
3076       /* quats */
3077       /* objects and bones do normalization first too, otherwise
3078        * we don't necessarily end up with a rotation matrix, and
3079        * then conversion back to quat gives a different result */
3080       float quat[4];
3081       normalize_qt_qt(quat, td->ext->quat);
3082       quat_to_mat4(cob->matrix, quat);
3083     }
3084     else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
3085       /* axis angle */
3086       axis_angle_to_mat4(cob->matrix, td->ext->rotAxis, *td->ext->rotAngle);
3087     }
3088     else {
3089       /* eulers */
3090       eulO_to_mat4(cob->matrix, td->ext->rot, td->ext->rotOrder);
3091     }
3092   }
3093 }
3094
3095 static void constraintRotLim(TransInfo *UNUSED(t), TransData *td)
3096 {
3097   if (td->con) {
3098     const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_ROTLIMIT);
3099     bConstraintOb cob;
3100     bConstraint *con;
3101     bool do_limit = false;
3102
3103     /* Evaluate valid constraints */
3104     for (con = td->con; con; con = con->next) {
3105       /* only consider constraint if enabled */
3106       if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) {
3107         continue;
3108       }
3109       if (con->enforce == 0.0f) {
3110         continue;
3111       }
3112
3113       /* we're only interested in Limit-Rotation constraints */
3114       if (con->type == CONSTRAINT_TYPE_ROTLIMIT) {
3115         bRotLimitConstraint *data = con->data;
3116
3117         /* only use it if it's tagged for this purpose */
3118         if ((data->flag2 & LIMIT_TRANSFORM) == 0) {
3119           continue;
3120         }
3121
3122         /* skip incompatible spacetypes */
3123         if (!ELEM(con->ownspace, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL)) {
3124           continue;
3125         }
3126
3127         /* only do conversion if necessary, to preserve quats and eulers */
3128         if (do_limit == false) {
3129           constraintob_from_transdata(&cob, td);
3130           do_limit = true;
3131         }
3132
3133         /* do space conversions */
3134         if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
3135           /* just multiply by td->mtx (this should be ok) */
3136           mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
3137         }
3138
3139         /* do constraint */
3140         cti->evaluate_constraint(con, &cob, NULL);
3141
3142         /* convert spaces again */
3143         if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
3144           /* just multiply by td->smtx (this should be ok) */
3145           mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
3146         }
3147       }
3148     }
3149
3150     if (do_limit) {
3151       /* copy results from cob->matrix */
3152       if (td->ext->rotOrder == ROT_MODE_QUAT) {
3153         /* quats */
3154         mat4_to_quat(td->ext->quat, cob.matrix);
3155       }
3156       else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
3157         /* axis angle */
3158         mat4_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, cob.matrix);
3159       }
3160       else {
3161         /* eulers */
3162         mat4_to_eulO(td->ext->rot, td->ext->rotOrder, cob.matrix);
3163       }
3164     }
3165   }
3166 }
3167
3168 static void constraintSizeLim(TransInfo *t, TransData *td)
3169 {
3170   if (td->con && td->ext) {
3171     const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_SIZELIMIT);
3172     bConstraintOb cob = {NULL};
3173     bConstraint *con;
3174     float size_sign[3], size_abs[3];
3175     int i;
3176
3177     /* Make a temporary bConstraintOb for using these limit constraints
3178      * - they only care that cob->matrix is correctly set ;-)
3179      * - current space should be local
3180      */
3181     if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
3182       /* scale val and reset size */
3183       return;  // TODO: fix this case
3184     }
3185     else {
3186       /* Reset val if SINGLESIZE but using a constraint */
3187       if (td->flag & TD_SINGLESIZE) {
3188         return;
3189       }
3190
3191       /* separate out sign to apply back later */
3192       for (i = 0; i < 3; i++) {
3193         size_sign[i] = signf(td->ext->size[i]);
3194         size_abs[i] = fabsf(td->ext->size[i]);
3195       }
3196
3197       size_to_mat4(cob.matrix, size_abs);
3198     }
3199
3200     /* Evaluate valid constraints */
3201     for (con = td->con; con; con = con->next) {
3202       /* only consider constraint if enabled */
3203       if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) {
3204         continue;
3205       }
3206       if (con->enforce == 0.0f) {
3207         continue;
3208       }
3209
3210       /* we're only interested in Limit-Scale constraints */
3211       if (con->type == CONSTRAINT_TYPE_SIZELIMIT) {
3212         bSizeLimitConstraint *data = con->data;
3213
3214         /* only use it if it's tagged for this purpose */
3215         if ((data->flag2 & LIMIT_TRANSFORM) == 0) {
3216           continue;
3217         }
3218
3219         /* do space conversions */
3220         if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
3221           /* just multiply by td->mtx (this should be ok) */
3222           mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
3223         }
3224         else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
3225           /* skip... incompatible spacetype */
3226           continue;
3227         }
3228
3229         /* do constraint */
3230         cti->evaluate_constraint(con, &cob, NULL);
3231
3232         /* convert spaces again */
3233         if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
3234           /* just multiply by td->smtx (this should be ok) */
3235           mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
3236         }
3237       }
3238     }
3239
3240     /* copy results from cob->matrix */
3241     if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
3242       /* scale val and reset size */
3243       return;  // TODO: fix this case
3244     }
3245     else {
3246       /* Reset val if SINGLESIZE but using a constraint */
3247       if (td->flag & TD_SINGLESIZE) {
3248         return;
3249       }
3250
3251       /* extrace scale from matrix and apply back sign */
3252       mat4_to_size(td->ext->size, cob.matrix);
3253       mul_v3_v3(td->ext->size, size_sign);
3254     }
3255   }
3256 }
3257
3258 /* -------------------------------------------------------------------- */
3259 /* Transform (Bend) */
3260
3261 /** \name Transform Bend
3262  * \{ */
3263
3264 struct BendCustomData {
3265   /* All values are in global space. */
3266   float warp_sta[3];
3267   float warp_end[3];
3268
3269   float warp_nor[3];
3270   float warp_tan[3];
3271
3272   /* for applying the mouse distance */
3273   float warp_init_dist;
3274 };
3275
3276 static void initBend(TransInfo *t)
3277 {
3278   const float mval_fl[2] = {UNPACK2(t->mval)};
3279   const float *curs;
3280   float tvec[3];
3281   struct BendCustomData *data;
3282
3283   t->mode = TFM_BEND;
3284   t->transform = Bend;
3285   t->handleEvent = handleEventBend;
3286
3287   setInputPostFct(&t->mouse, postInputRotation);
3288   initMouseInputMode(t, &t->mouse, INPUT_ANGLE_SPRING);
3289
3290   t->idx_max = 1;
3291   t->num.idx_max = 1;
3292   t->snap[0] = 0.0f;
3293   t->snap[1] = SNAP_INCREMENTAL_ANGLE;
3294   t->snap[2] = t->snap[1] * 0.2;
3295
3296   copy_v3_fl(t->num.val_inc, t->snap[1]);
3297   t->num.unit_sys = t->scene->unit.system;
3298   t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
3299   t->num.unit_type[0] = B_UNIT_ROTATION;
3300   t->num.unit_type[1] = B_UNIT_LENGTH;
3301
3302   t->flag |= T_NO_CONSTRAINT;
3303
3304   // copy_v3_v3(t->center, ED_view3d_cursor3d_get(t->scene, t->view));
3305   if ((t->flag & T_OVERRIDE_CENTER) == 0) {
3306     calculateCenterCursor(t, t->center_global);
3307   }
3308   calculateCenterLocal(t, t->center_global);
3309
3310   t->val = 0.0f;
3311
3312   data = MEM_callocN(sizeof(*data), __func__);
3313
3314   curs = t->scene->cursor.location;
3315   copy_v3_v3(data->warp_sta, curs);
3316   ED_view3d_win_to_3d(t->sa->spacedata.first, t->ar, curs, mval_fl, data->warp_end);
3317
3318   copy_v3_v3(data->warp_nor, t->viewinv[2]);
3319   normalize_v3(data->warp_nor);
3320
3321   /* tangent */
3322   sub_v3_v3v3(tvec, data->warp_end, data->warp_sta);
3323   cross_v3_v3v3(data->warp_tan, tvec, data->warp_nor);
3324   normalize_v3(data->warp_tan);
3325
3326   data->warp_init_dist = len_v3v3(data->warp_end, data->warp_sta);
3327
3328   t->custom.mode.data = data;
3329   t->custom.mode.use_free = true;
3330 }
3331
3332 static eRedrawFlag handleEventBend(TransInfo *UNUSED(t), const wmEvent *event)
3333 {
3334   eRedrawFlag status = TREDRAW_NOTHING;
3335
3336   if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
3337     status = TREDRAW_HARD;
3338   }
3339
3340   return status;
3341 }
3342
3343 static void Bend(TransInfo *t, const int UNUSED(mval[2]))
3344 {
3345   float vec[3];
3346   float pivot_global[3];
3347   float warp_end_radius_global[3];
3348   int i;
3349   char str[UI_MAX_DRAW_STR];
3350   const struct BendCustomData *data = t->custom.mode.data;
3351   const bool is_clamp = (t->flag & T_ALT_TRANSFORM) == 0;
3352
3353   union {
3354     struct {
3355       float angle, scale;
3356     };
3357     float vector[2];
3358   } values;
3359
3360   /* amount of radians for bend */
3361   copy_v2_v2(values.vector, t->values);
3362
3363 #if 0
3364   snapGrid(t, angle_rad);
3365 #else
3366   /* hrmf, snapping radius is using 'angle' steps, need to convert to something else
3367    * this isnt essential but nicer to give reasonable snapping values for radius */
3368   if (t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) {
3369     const float radius_snap = 0.1f;
3370     const float snap_hack = (t->snap[1] * data->warp_init_dist) / radius_snap;
3371     values.scale *= snap_hack;
3372     snapGridIncrement(t, values.vector);
3373     values.scale /= snap_hack;
3374   }
3375 #endif
3376
3377   if (applyNumInput(&t->num, values.vector)) {
3378     values.scale = values.scale / data->warp_init_dist;
3379   }
3380
3381   copy_v2_v2(t->values_final, values.vector);
3382
3383   /* header print for NumInput */
3384   if (hasNumInput(&t->num)) {
3385     char c[NUM_STR_REP_LEN * 2];
3386
3387     outputNumInput(&(t->num), c, &t->scene->unit);
3388
3389     BLI_snprintf(str,
3390                  sizeof(str),
3391                  TIP_("Bend Angle: %s Radius: %s Alt, Clamp %s"),
3392                  &c[0],
3393                  &c[NUM_STR_REP_LEN],
3394                  WM_bool_as_string(is_clamp));
3395   }
3396   else {
3397     /* default header print */
3398     BLI_snprintf(str,
3399                  sizeof(str),
3400                  TIP_("Bend Angle: %.3f Radius: %.4f, Alt, Clamp %s"),
3401                  RAD2DEGF(values.angle),
3402                  values.scale * data->warp_init_dist,
3403                  WM_bool_as_string(is_clamp));
3404   }
3405
3406   values.angle *= -1.0f;
3407   values.scale *= data->warp_init_dist;
3408
3409   /* calc 'data->warp_end' from 'data->warp_end_init' */
3410   copy_v3_v3(warp_end_radius_global, data->warp_end);
3411   dist_ensure_v3_v3fl(warp_end_radius_global, data->warp_sta, values.scale);
3412   /* done */
3413
3414   /* calculate pivot */
3415   copy_v3_v3(pivot_global, data->warp_sta);
3416   if (values.angle > 0.0f) {
3417     madd_v3_v3fl(pivot_global,
3418                  data->warp_tan,
3419                  -values.scale * shell_angle_to_dist((float)M_PI_2 - values.angle));
3420   }
3421   else {
3422     madd_v3_v3fl(pivot_global,
3423                  data->warp_tan,
3424                  +values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle));
3425   }
3426
3427   /* TODO(campbell): xform, compensate object center. */
3428   FOREACH_TRANS_DATA_CONTAINER (t, tc) {
3429     TransData *td = tc->data;
3430
3431     float warp_sta_local[3];
3432     float warp_end_local[3];
3433     float warp_end_radius_local[3];
3434     float pivot_local[3];
3435
3436     if (tc->use_local_mat) {
3437       sub_v3_v3v3(warp_sta_local, data->warp_sta, tc->mat[3]);
3438       sub_v3_v3v3(warp_end_local, data->warp_end, tc->mat[3]);
3439       sub_v3_v3v3(warp_end_radius_local, warp_end_radius_global, tc->mat[3]);
3440       sub_v3_v3v3(pivot_local, pivot_global, tc->mat[3]);
3441     }
3442     else {
3443       copy_v3_v3(warp_sta_local, data->warp_sta);
3444       copy_v3_v3(warp_end_local, data->warp_end);
3445       copy_v3_v3(warp_end_radius_local, warp_end_radius_global);
3446       copy_v3_v3(pivot_local, pivot_global);
3447     }
3448
3449     for (i = 0; i < tc->data_len; i++, td++) {
3450       float mat[3][3];
3451       float delta[3];
3452       float fac, fac_scaled;
3453
3454       if (td->flag & TD_NOACTION) {
3455         break;
3456       }
3457
3458       if (td->flag & TD_SKIP) {
3459         continue;
3460       }
3461
3462       if (UNLIKELY(values.angle == 0.0f)) {
3463         copy_v3_v3(td->loc, td->iloc);
3464         continue;
3465       }
3466
3467       copy_v3_v3(vec, td->iloc);
3468       mul_m3_v3(td->mtx, vec);
3469
3470       fac = line_point_factor_v3(vec, warp_sta_local, warp_end_radius_local);
3471       if (is_clamp) {
3472         CLAMP(fac, 0.0f, 1.0f);
3473       }
3474
3475       if (t->options & CTX_GPENCIL_STROKES) {
3476         /* grease pencil multiframe falloff */
3477         bGPDstroke *gps = (bGPDstroke *)td->extra;
3478         if (gps != NULL) {
3479           fac_scaled = fac * td->factor * gps->runtime.multi_frame_falloff;
3480         }
3481         else {
3482           fac_scaled = fac * td->factor;
3483         }
3484       }
3485       else {
3486         fac_scaled = fac * td->factor;
3487       }
3488
3489       axis_angle_normalized_to_mat3(mat, data->warp_nor, values.angle * fac_scaled);
3490       interp_v3_v3v3(delta, warp_sta_local, warp_end_radius_local, fac_scaled);
3491       sub_v3_v3(delta, warp_sta_local);
3492
3493       /* delta is subtracted, rotation adds back this offset */
3494       sub_v3_v3(vec, delta);
3495
3496       sub_v3_v3(vec, pivot_local);
3497       mul_m3_v3(mat, vec);
3498       add_v3_v3(vec, pivot_local);
3499
3500       mul_m3_v3(td->smtx, vec);
3501
3502       /* rotation */
3503       if ((t->flag & T_POINTS) == 0) {
3504         ElementRotation(t, tc, td, mat, V3D_AROUND_LOCAL_ORIGINS);
3505       }
3506
3507       /* location */
3508       copy_v3_v3(td->loc, vec);
3509     }
3510   }
3511
3512   recalcData(t);
3513
3514   ED_area_status_text(t->sa, str);
3515 }
3516 /** \} */
3517
3518 /* -------------------------------------------------------------------- */
3519 /* Transform (Shear) */
3520
3521 /** \name Transform Shear
3522  * \{ */
3523
3524 static void initShear_mouseInputMode(TransInfo *t)
3525 {
3526   float dir[3];
3527   copy_v3_v3(dir, t->orient_matrix[t->orient_axis_ortho]);
3528
3529   /* Without this, half the gizmo handles move in the opposite direction. */
3530   if ((t->orient_axis_ortho + 1) % 3 != t->orient_axis) {
3531     negate_v3(dir);
3532   }
3533
3534   mul_mat3_m4_v3(t->viewmat, dir);
3535   if (normalize_v2(dir) == 0.0f) {
3536     dir[0] = 1.0f;
3537   }
3538   setCustomPointsFromDirection(t, &t->mouse, dir);
3539
3540   initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO);
3541 }
3542
3543 static void initShear(TransInfo *t)
3544 {
3545   t->mode = TFM_SHEAR;
3546   t->transform = applyShear;
3547   t->handleEvent = handleEventShear;
3548
3549   if (t->orient_axis == t->orient_axis_ortho) {
3550     t->orient_axis = 2;
3551     t->orient_axis_ortho = 1;
3552   }
3553
3554   initShear_mouseInputMode(t);
3555
3556   t->idx_max = 0;
3557   t->num.idx_max = 0;
3558   t->snap[0] = 0.0f;
3559   t->snap[1] = 0.1f;
3560   t->snap[2] = t->snap[1] * 0.1f;
3561
3562   copy_v3_fl(t->num.val_inc, t->snap[1]);
3563   t->num.unit_sys = t->scene->unit.system;
3564   t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */
3565
3566   t->flag |= T_NO_CONSTRAINT;
3567 }
3568
3569 static eRedrawFlag handleEventShear(TransInfo *t, const wmEvent *event)
3570 {
3571   eRedrawFlag status = TREDRAW_NOTHING;
3572
3573   if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
3574     /* Use custom.mode.data pointer to signal Shear direction */
3575     do {
3576       t->orient_axis_ortho = (t->orient_axis_ortho + 1) % 3;
3577     } while (t->orient_axis_ortho == t->orient_axis);
3578
3579     initShear_mouseInputMode(t);
3580
3581     status = TREDRAW_HARD;
3582   }
3583   else if (event->type == XKEY && event->val == KM_PRESS) {
3584     t->orient_axis_ortho = (t->orient_axis + 1) % 3;
3585     initShear_mouseInputMode(t);
3586
3587     status = TREDRAW_HARD;
3588   }
3589   else if (event->type == YKEY && event->val == KM_PRESS) {
3590     t->orient_axis_ortho = (t->orient_axis + 2) % 3;
3591     initShear_mouseInputMode(t);
3592
3593     status = TREDRAW_HARD;
3594   }
3595
3596   return status;
3597 }
3598
3599 static void applyShear(TransInfo *t, const int UNUSED(mval[2]))
3600 {
3601   float vec[3];
3602   float smat[3][3], tmat[3][3], totmat[3][3], axismat[3][3], axismat_inv[3][3];
3603   float value;
3604   int i;
3605   char str[UI_MAX_DRAW_STR];
3606   const bool is_local_center = transdata_check_local_center(t, t->around);
3607
3608   value = t->values[0];
3609
3610   snapGridIncrement(t, &value);
3611
3612   applyNumInput(&t->num, &value);
3613
3614   t->values_final[0] = value;
3615
3616   /* header print for NumInput */
3617   if (hasNumInput(&t->num)) {
3618     char c[NUM_STR_REP_LEN];
3619
3620     outputNumInput(&(t->num), c, &t->scene->unit);
3621
3622     BLI_snprintf(str, sizeof(str), TIP_("Shear: %s %s"), c, t->proptext);
3623   }
3624   else {
3625     /* default header print */
3626     BLI_snprintf(str,
3627                  sizeof(str),
3628                  TIP_("Shear: %.3f %s (Press X or Y to set shear axis)"),
3629                  value,
3630                  t->proptext);
3631   }
3632
3633   unit_m3(smat);
3634   smat[1][0] = value;
3635
3636   copy_v3_v3(axismat_inv[0], t->orient_matrix[t->orient_axis_ortho]);
3637   copy_v3_v3(axismat_inv[2], t->orient_matrix[t->orient_axis]);
3638   cross_v3_v3v3(axismat_inv[1], axismat_inv[0], axismat_inv[2]);
3639   invert_m3_m3(axismat, axismat_inv);
3640
3641   mul_m3_series(totmat, axismat_inv, smat, axismat);
3642
3643   FOREACH_TRANS_DATA_CONTAINER (t, tc) {
3644     TransData *td = tc->data;
3645     for (i = 0; i < tc->data_len; i++, td++) {
3646       const float *center, *co;
3647
3648       if (td->flag & TD_NOACTION) {
3649         break;
3650       }
3651
3652       if (td->flag & TD_SKIP) {
3653         continue;
3654       }
3655
3656       if (t->flag & T_EDIT) {
3657         mul_m3_series(tmat, td->smtx, totmat, td->mtx);
3658       }
3659       else {
3660         copy_m3_m3(tmat, totmat);
3661       }
3662
3663       if (is_local_center) {
3664         center = td->center;
3665         co = td->loc;
3666       }
3667       else {
3668         center = tc->center_local;
3669         co = td->center;
3670       }
3671
3672       sub_v3_v3v3(vec, co, center);