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