Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / transform / transform.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/transform/transform.c
29  *  \ingroup edtransform
30  */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <math.h>
36 #include <float.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "DNA_anim_types.h"
41 #include "DNA_armature_types.h"
42 #include "DNA_constraint_types.h"
43 #include "DNA_mask_types.h"
44 #include "DNA_movieclip_types.h"
45 #include "DNA_scene_types.h"  /* PET modes */
46 #include "DNA_workspace_types.h"
47
48 #include "BLI_alloca.h"
49 #include "BLI_utildefines.h"
50 #include "BLI_math.h"
51 #include "BLI_rect.h"
52 #include "BLI_listbase.h"
53 #include "BLI_string.h"
54 #include "BLI_ghash.h"
55 #include "BLI_utildefines_stack.h"
56 #include "BLI_memarena.h"
57
58 #include "BKE_nla.h"
59 #include "BKE_editmesh.h"
60 #include "BKE_editmesh_bvh.h"
61 #include "BKE_context.h"
62 #include "BKE_constraint.h"
63 #include "BKE_particle.h"
64 #include "BKE_unit.h"
65 #include "BKE_scene.h"
66 #include "BKE_mask.h"
67 #include "BKE_report.h"
68 #include "BKE_workspace.h"
69
70 #include "DEG_depsgraph.h"
71
72 #include "BIF_glutil.h"
73
74 #include "GPU_immediate.h"
75 #include "GPU_immediate_util.h"
76 #include "GPU_matrix.h"
77 #include "GPU_state.h"
78
79 #include "ED_image.h"
80 #include "ED_keyframing.h"
81 #include "ED_screen.h"
82 #include "ED_space_api.h"
83 #include "ED_markers.h"
84 #include "ED_view3d.h"
85 #include "ED_mesh.h"
86 #include "ED_clip.h"
87 #include "ED_node.h"
88
89 #include "WM_types.h"
90 #include "WM_api.h"
91
92 #include "UI_view2d.h"
93 #include "UI_interface.h"
94 #include "UI_interface_icons.h"
95 #include "UI_resources.h"
96
97 #include "RNA_access.h"
98
99 #include "BLF_api.h"
100 #include "BLT_translation.h"
101
102 #include "transform.h"
103
104 /* Disabling, since when you type you know what you are doing, and being able to set it to zero is handy. */
105 // #define USE_NUM_NO_ZERO
106
107 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
108 static void doEdgeSlide(TransInfo *t, float perc);
109 static void doVertSlide(TransInfo *t, float perc);
110
111 static void drawEdgeSlide(TransInfo *t);
112 static void drawVertSlide(TransInfo *t);
113 static void postInputRotation(TransInfo *t, float values[3]);
114
115 static void ElementRotation(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around);
116 static void initSnapSpatial(TransInfo *t, float r_snap[3]);
117
118
119 /* Transform Callbacks */
120 static void initBend(TransInfo *t);
121 static eRedrawFlag handleEventBend(TransInfo *t, const struct wmEvent *event);
122 static void Bend(TransInfo *t, const int mval[2]);
123
124 static void initShear(TransInfo *t);
125 static eRedrawFlag handleEventShear(TransInfo *t, const struct wmEvent *event);
126 static void applyShear(TransInfo *t, const int mval[2]);
127
128 static void initResize(TransInfo *t);
129 static void applyResize(TransInfo *t, const int mval[2]);
130
131 static void initSkinResize(TransInfo *t);
132 static void applySkinResize(TransInfo *t, const int mval[2]);
133
134 static void initTranslation(TransInfo *t);
135 static void applyTranslation(TransInfo *t, const int mval[2]);
136
137 static void initToSphere(TransInfo *t);
138 static void applyToSphere(TransInfo *t, const int mval[2]);
139
140 static void initRotation(TransInfo *t);
141 static void applyRotation(TransInfo *t, const int mval[2]);
142
143 static void initShrinkFatten(TransInfo *t);
144 static void applyShrinkFatten(TransInfo *t, const int mval[2]);
145
146 static void initTilt(TransInfo *t);
147 static void applyTilt(TransInfo *t, const int mval[2]);
148
149 static void initCurveShrinkFatten(TransInfo *t);
150 static void applyCurveShrinkFatten(TransInfo *t, const int mval[2]);
151
152 static void initMaskShrinkFatten(TransInfo *t);
153 static void applyMaskShrinkFatten(TransInfo *t, const int mval[2]);
154
155 static void initGPShrinkFatten(TransInfo *t);
156 static void applyGPShrinkFatten(TransInfo *t, const int mval[2]);
157
158 static void initTrackball(TransInfo *t);
159 static void applyTrackball(TransInfo *t, const int mval[2]);
160
161 static void initPushPull(TransInfo *t);
162 static void applyPushPull(TransInfo *t, const int mval[2]);
163
164 static void initBevelWeight(TransInfo *t);
165 static void applyBevelWeight(TransInfo *t, const int mval[2]);
166
167 static void initCrease(TransInfo *t);
168 static void applyCrease(TransInfo *t, const int mval[2]);
169
170 static void initBoneSize(TransInfo *t);
171 static void applyBoneSize(TransInfo *t, const int mval[2]);
172
173 static void initBoneEnvelope(TransInfo *t);
174 static void applyBoneEnvelope(TransInfo *t, const int mval[2]);
175
176 static void initBoneRoll(TransInfo *t);
177 static void applyBoneRoll(TransInfo *t, const int mval[2]);
178
179 static void initEdgeSlide_ex(TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp);
180 static void initEdgeSlide(TransInfo *t);
181 static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
182 static void applyEdgeSlide(TransInfo *t, const int mval[2]);
183
184 static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp);
185 static void initVertSlide(TransInfo *t);
186 static eRedrawFlag handleEventVertSlide(TransInfo *t, const struct wmEvent *event);
187 static void applyVertSlide(TransInfo *t, const int mval[2]);
188
189 static void initTimeTranslate(TransInfo *t);
190 static void applyTimeTranslate(TransInfo *t, const int mval[2]);
191
192 static void initTimeSlide(TransInfo *t);
193 static void applyTimeSlide(TransInfo *t, const int mval[2]);
194
195 static void initTimeScale(TransInfo *t);
196 static void applyTimeScale(TransInfo *t, const int mval[2]);
197
198 static void initBakeTime(TransInfo *t);
199 static void applyBakeTime(TransInfo *t, const int mval[2]);
200
201 static void initMirror(TransInfo *t);
202 static void applyMirror(TransInfo *t, const int mval[2]);
203
204 static void initAlign(TransInfo *t);
205 static void applyAlign(TransInfo *t, const int mval[2]);
206
207 static void initSeqSlide(TransInfo *t);
208 static void applySeqSlide(TransInfo *t, const int mval[2]);
209 /* end transform callbacks */
210
211
212 static bool transdata_check_local_center(TransInfo *t, short around)
213 {
214         return ((around == V3D_AROUND_LOCAL_ORIGINS) && (
215                     (t->flag & (T_OBJECT | T_POSE)) ||
216                     /* implicit: (t->flag & T_EDIT) */
217                     (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) ||
218                     (t->spacetype == SPACE_IPO) ||
219                     (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))
220                 );
221 }
222
223 bool transdata_check_local_islands(TransInfo *t, short around)
224 {
225         return ((around == V3D_AROUND_LOCAL_ORIGINS) && (
226                 (ELEM(t->obedit_type, OB_MESH))));
227 }
228
229 /* ************************** SPACE DEPENDANT CODE **************************** */
230
231 void setTransformViewMatrices(TransInfo *t)
232 {
233         if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
234                 RegionView3D *rv3d = t->ar->regiondata;
235
236                 copy_m4_m4(t->viewmat, rv3d->viewmat);
237                 copy_m4_m4(t->viewinv, rv3d->viewinv);
238                 copy_m4_m4(t->persmat, rv3d->persmat);
239                 copy_m4_m4(t->persinv, rv3d->persinv);
240                 t->persp = rv3d->persp;
241         }
242         else {
243                 unit_m4(t->viewmat);
244                 unit_m4(t->viewinv);
245                 unit_m4(t->persmat);
246                 unit_m4(t->persinv);
247                 t->persp = RV3D_ORTHO;
248         }
249
250         calculateCenter2D(t);
251         calculateCenterLocal(t, t->center_global);
252 }
253
254 void setTransformViewAspect(TransInfo *t, float r_aspect[3])
255 {
256         copy_v3_fl(r_aspect, 1.0f);
257
258         if (t->spacetype == SPACE_IMAGE) {
259                 SpaceImage *sima = t->sa->spacedata.first;
260
261                 if (t->options & CTX_MASK) {
262                         ED_space_image_get_aspect(sima, &r_aspect[0], &r_aspect[1]);
263                 }
264                 else if (t->options & CTX_PAINT_CURVE) {
265                         /* pass */
266                 }
267                 else {
268                         ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]);
269                 }
270         }
271         else if (t->spacetype == SPACE_CLIP) {
272                 SpaceClip *sclip = t->sa->spacedata.first;
273
274                 if (t->options & CTX_MOVIECLIP) {
275                         ED_space_clip_get_aspect_dimension_aware(sclip, &r_aspect[0], &r_aspect[1]);
276                 }
277                 else {
278                         ED_space_clip_get_aspect(sclip, &r_aspect[0], &r_aspect[1]);
279                 }
280         }
281         else if (t->spacetype == SPACE_IPO) {
282                 /* depemds on context of usage */
283         }
284 }
285
286 static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
287 {
288         float divx = BLI_rcti_size_x(&v2d->mask);
289         float divy = BLI_rcti_size_y(&v2d->mask);
290
291         r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
292         r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
293         r_vec[2] = 0.0f;
294 }
295
296 static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
297 {
298         float divx = BLI_rcti_size_x(&v2d->mask);
299         float divy = BLI_rcti_size_y(&v2d->mask);
300
301         float mulx = BLI_rctf_size_x(&v2d->cur);
302         float muly = BLI_rctf_size_y(&v2d->cur);
303
304         /* difference with convertViewVec2D */
305         /* clamp w/h, mask only */
306         if (mulx / divx < muly / divy) {
307                 divy = divx;
308                 muly = mulx;
309         }
310         else {
311                 divx = divy;
312                 mulx = muly;
313         }
314         /* end difference */
315
316         r_vec[0] = mulx * dx / divx;
317         r_vec[1] = muly * dy / divy;
318         r_vec[2] = 0.0f;
319 }
320
321 void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
322 {
323         if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
324                 if (t->options & CTX_PAINT_CURVE) {
325                         r_vec[0] = dx;
326                         r_vec[1] = dy;
327                 }
328                 else {
329                         const float mval_f[2] = {(float)dx, (float)dy};
330                         ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac);
331                 }
332         }
333         else if (t->spacetype == SPACE_IMAGE) {
334                 if (t->options & CTX_MASK) {
335                         convertViewVec2D_mask(t->view, r_vec, dx, dy);
336                 }
337                 else if (t->options & CTX_PAINT_CURVE) {
338                         r_vec[0] = dx;
339                         r_vec[1] = dy;
340                 }
341                 else {
342                         convertViewVec2D(t->view, r_vec, dx, dy);
343                 }
344
345                 r_vec[0] *= t->aspect[0];
346                 r_vec[1] *= t->aspect[1];
347         }
348         else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
349                 convertViewVec2D(t->view, r_vec, dx, dy);
350         }
351         else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
352                 convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
353         }
354         else if (t->spacetype == SPACE_CLIP) {
355                 if (t->options & CTX_MASK) {
356                         convertViewVec2D_mask(t->view, r_vec, dx, dy);
357                 }
358                 else {
359                         convertViewVec2D(t->view, r_vec, dx, dy);
360                 }
361
362                 r_vec[0] *= t->aspect[0];
363                 r_vec[1] *= t->aspect[1];
364         }
365         else {
366                 printf("%s: called in an invalid context\n", __func__);
367                 zero_v3(r_vec);
368         }
369 }
370
371 void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
372 {
373         if (t->spacetype == SPACE_VIEW3D) {
374                 if (t->ar->regiontype == RGN_TYPE_WINDOW) {
375                         if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
376                                 adr[0] = (int)2140000000.0f;  /* this is what was done in 2.64, perhaps we can be smarter? */
377                                 adr[1] = (int)2140000000.0f;
378                         }
379                 }
380         }
381         else if (t->spacetype == SPACE_IMAGE) {
382                 SpaceImage *sima = t->sa->spacedata.first;
383
384                 if (t->options & CTX_MASK) {
385                         float v[2];
386
387                         v[0] = vec[0] / t->aspect[0];
388                         v[1] = vec[1] / t->aspect[1];
389
390                         BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
391
392                         ED_image_point_pos__reverse(sima, t->ar, v, v);
393
394                         adr[0] = v[0];
395                         adr[1] = v[1];
396                 }
397                 else if (t->options & CTX_PAINT_CURVE) {
398                         adr[0] = vec[0];
399                         adr[1] = vec[1];
400                 }
401                 else {
402                         float v[2];
403
404                         v[0] = vec[0] / t->aspect[0];
405                         v[1] = vec[1] / t->aspect[1];
406
407                         UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
408                 }
409         }
410         else if (t->spacetype == SPACE_ACTION) {
411                 int out[2] = {0, 0};
412 #if 0
413                 SpaceAction *sact = t->sa->spacedata.first;
414
415                 if (sact->flag & SACTION_DRAWTIME) {
416                         //vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
417                         /* same as below */
418                         UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
419                 }
420                 else
421 #endif
422                 {
423                         UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
424                 }
425
426                 adr[0] = out[0];
427                 adr[1] = out[1];
428         }
429         else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
430                 int out[2] = {0, 0};
431
432                 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
433                 adr[0] = out[0];
434                 adr[1] = out[1];
435         }
436         else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
437                 int out[2] = {0, 0};
438
439                 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
440                 adr[0] = out[0];
441                 adr[1] = out[1];
442         }
443         else if (t->spacetype == SPACE_CLIP) {
444                 SpaceClip *sc = t->sa->spacedata.first;
445
446                 if (t->options & CTX_MASK) {
447                         MovieClip *clip = ED_space_clip_get_clip(sc);
448
449                         if (clip) {
450                                 float v[2];
451
452                                 v[0] = vec[0] / t->aspect[0];
453                                 v[1] = vec[1] / t->aspect[1];
454
455                                 BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v);
456
457                                 ED_clip_point_stable_pos__reverse(sc, t->ar, v, v);
458
459                                 adr[0] = v[0];
460                                 adr[1] = v[1];
461                         }
462                         else {
463                                 adr[0] = 0;
464                                 adr[1] = 0;
465                         }
466                 }
467                 else if (t->options & CTX_MOVIECLIP) {
468                         float v[2];
469
470                         v[0] = vec[0] / t->aspect[0];
471                         v[1] = vec[1] / t->aspect[1];
472
473                         UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
474                 }
475                 else {
476                         BLI_assert(0);
477                 }
478         }
479         else if (t->spacetype == SPACE_NODE) {
480                 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &adr[0], &adr[1]);
481         }
482 }
483 void projectIntView(TransInfo *t, const float vec[3], int adr[2])
484 {
485         projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
486 }
487
488 void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
489 {
490         switch (t->spacetype) {
491                 case SPACE_VIEW3D:
492                 {
493                         if (t->options & CTX_PAINT_CURVE) {
494                                 adr[0] = vec[0];
495                                 adr[1] = vec[1];
496                         }
497                         else if (t->ar->regiontype == RGN_TYPE_WINDOW) {
498                                 /* allow points behind the view [#33643] */
499                                 if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
500                                         /* XXX, 2.64 and prior did this, weak! */
501                                         adr[0] = t->ar->winx / 2.0f;
502                                         adr[1] = t->ar->winy / 2.0f;
503                                 }
504                                 return;
505                         }
506                         break;
507                 }
508                 default:
509                 {
510                         int a[2] = {0, 0};
511                         projectIntView(t, vec, a);
512                         adr[0] = a[0];
513                         adr[1] = a[1];
514                         break;
515                 }
516         }
517 }
518 void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
519 {
520         projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
521 }
522
523 void applyAspectRatio(TransInfo *t, float vec[2])
524 {
525         if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) && !(t->options & CTX_PAINT_CURVE)) {
526                 SpaceImage *sima = t->sa->spacedata.first;
527
528                 if ((sima->flag & SI_COORDFLOATS) == 0) {
529                         int width, height;
530                         ED_space_image_get_size(sima, &width, &height);
531
532                         vec[0] *= width;
533                         vec[1] *= height;
534                 }
535
536                 vec[0] /= t->aspect[0];
537                 vec[1] /= t->aspect[1];
538         }
539         else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
540                 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
541                         vec[0] /= t->aspect[0];
542                         vec[1] /= t->aspect[1];
543                 }
544         }
545 }
546
547 void removeAspectRatio(TransInfo *t, float vec[2])
548 {
549         if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
550                 SpaceImage *sima = t->sa->spacedata.first;
551
552                 if ((sima->flag & SI_COORDFLOATS) == 0) {
553                         int width, height;
554                         ED_space_image_get_size(sima, &width, &height);
555
556                         vec[0] /= width;
557                         vec[1] /= height;
558                 }
559
560                 vec[0] *= t->aspect[0];
561                 vec[1] *= t->aspect[1];
562         }
563         else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
564                 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
565                         vec[0] *= t->aspect[0];
566                         vec[1] *= t->aspect[1];
567                 }
568         }
569 }
570
571 static void viewRedrawForce(const bContext *C, TransInfo *t)
572 {
573         if (t->options & CTX_GPENCIL_STROKES) {
574                 WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
575         }
576         else if (t->spacetype == SPACE_VIEW3D) {
577                 if (t->options & CTX_PAINT_CURVE) {
578                         wmWindow *window = CTX_wm_window(C);
579                         WM_paint_cursor_tag_redraw(window, t->ar);
580                 }
581                 else {
582                         /* Do we need more refined tags? */
583                         if (t->flag & T_POSE)
584                                 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
585                         else
586                                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
587
588                         /* for realtime animation record - send notifiers recognised by animation editors */
589                         // XXX: is this notifier a lame duck?
590                         if ((t->animtimer) && IS_AUTOKEY_ON(t->scene))
591                                 WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
592
593                 }
594         }
595         else if (t->spacetype == SPACE_ACTION) {
596                 //SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
597                 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
598         }
599         else if (t->spacetype == SPACE_IPO) {
600                 //SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
601                 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
602         }
603         else if (t->spacetype == SPACE_NLA) {
604                 WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
605         }
606         else if (t->spacetype == SPACE_NODE) {
607                 //ED_area_tag_redraw(t->sa);
608                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
609         }
610         else if (t->spacetype == SPACE_SEQ) {
611                 WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
612         }
613         else if (t->spacetype == SPACE_IMAGE) {
614                 if (t->options & CTX_MASK) {
615                         Mask *mask = CTX_data_edit_mask(C);
616
617                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
618                 }
619                 else if (t->options & CTX_PAINT_CURVE) {
620                         wmWindow *window = CTX_wm_window(C);
621                         WM_paint_cursor_tag_redraw(window, t->ar);
622                 }
623                 else {
624                         // XXX how to deal with lock?
625                         SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
626                         if (sima->lock) {
627                                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, OBEDIT_FROM_VIEW_LAYER(t->view_layer)->data);
628                         }
629                         else {
630                                 ED_area_tag_redraw(t->sa);
631                         }
632                 }
633         }
634         else if (t->spacetype == SPACE_CLIP) {
635                 SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
636
637                 if (ED_space_clip_check_show_trackedit(sc)) {
638                         MovieClip *clip = ED_space_clip_get_clip(sc);
639
640                         /* objects could be parented to tracking data, so send this for viewport refresh */
641                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
642
643                         WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
644                 }
645                 else if (ED_space_clip_check_show_maskedit(sc)) {
646                         Mask *mask = CTX_data_edit_mask(C);
647
648                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
649                 }
650         }
651 }
652
653 static void viewRedrawPost(bContext *C, TransInfo *t)
654 {
655         ED_area_status_text(t->sa, NULL);
656
657         if (t->spacetype == SPACE_VIEW3D) {
658                 /* if autokeying is enabled, send notifiers that keyframes were added */
659                 if (IS_AUTOKEY_ON(t->scene))
660                         WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
661
662                 /* redraw UV editor */
663                 if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) &&
664                     (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT))
665                 {
666                         WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
667                 }
668
669                 /* XXX temp, first hack to get auto-render in compositor work (ton) */
670                 WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
671
672         }
673
674 #if 0 // TRANSFORM_FIX_ME
675         if (t->spacetype == SPACE_VIEW3D) {
676                 allqueue(REDRAWBUTSOBJECT, 0);
677                 allqueue(REDRAWVIEW3D, 0);
678         }
679         else if (t->spacetype == SPACE_IMAGE) {
680                 allqueue(REDRAWIMAGE, 0);
681                 allqueue(REDRAWVIEW3D, 0);
682         }
683         else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) {
684                 allqueue(REDRAWVIEW3D, 0);
685                 allqueue(REDRAWACTION, 0);
686                 allqueue(REDRAWNLA, 0);
687                 allqueue(REDRAWIPO, 0);
688                 allqueue(REDRAWTIME, 0);
689                 allqueue(REDRAWBUTSOBJECT, 0);
690         }
691
692         scrarea_queue_headredraw(curarea);
693 #endif
694 }
695
696 /* ************************** TRANSFORMATIONS **************************** */
697
698 static void view_editmove(unsigned short UNUSED(event))
699 {
700 #if 0 // TRANSFORM_FIX_ME
701         int refresh = 0;
702         /* Regular:   Zoom in */
703         /* Shift:     Scroll up */
704         /* Ctrl:      Scroll right */
705         /* Alt-Shift: Rotate up */
706         /* Alt-Ctrl:  Rotate right */
707
708         /* only work in 3D window for now
709          * In the end, will have to send to event to a 2D window handler instead
710          */
711         if (Trans.flag & T_2D_EDIT)
712                 return;
713
714         switch (event) {
715                 case WHEELUPMOUSE:
716                         if (G.qual & LR_SHIFTKEY) {
717                                 if (G.qual & LR_ALTKEY) {
718                                         G.qual &= ~LR_SHIFTKEY;
719                                         persptoetsen(PAD2);
720                                         G.qual |= LR_SHIFTKEY;
721                                 }
722                                 else {
723                                         persptoetsen(PAD2);
724                                 }
725                         }
726                         else if (G.qual & LR_CTRLKEY) {
727                                 if (G.qual & LR_ALTKEY) {
728                                         G.qual &= ~LR_CTRLKEY;
729                                         persptoetsen(PAD4);
730                                         G.qual |= LR_CTRLKEY;
731                                 }
732                                 else {
733                                         persptoetsen(PAD4);
734                                 }
735                         }
736                         else if (U.uiflag & USER_WHEELZOOMDIR)
737                                 persptoetsen(PADMINUS);
738                         else
739                                 persptoetsen(PADPLUSKEY);
740
741                         refresh = 1;
742                         break;
743                 case WHEELDOWNMOUSE:
744                         if (G.qual & LR_SHIFTKEY) {
745                                 if (G.qual & LR_ALTKEY) {
746                                         G.qual &= ~LR_SHIFTKEY;
747                                         persptoetsen(PAD8);
748                                         G.qual |= LR_SHIFTKEY;
749                                 }
750                                 else {
751                                         persptoetsen(PAD8);
752                                 }
753                         }
754                         else if (G.qual & LR_CTRLKEY) {
755                                 if (G.qual & LR_ALTKEY) {
756                                         G.qual &= ~LR_CTRLKEY;
757                                         persptoetsen(PAD6);
758                                         G.qual |= LR_CTRLKEY;
759                                 }
760                                 else {
761                                         persptoetsen(PAD6);
762                                 }
763                         }
764                         else if (U.uiflag & USER_WHEELZOOMDIR)
765                                 persptoetsen(PADPLUSKEY);
766                         else
767                                 persptoetsen(PADMINUS);
768
769                         refresh = 1;
770                         break;
771         }
772
773         if (refresh)
774                 setTransformViewMatrices(&Trans);
775 #endif
776 }
777
778 /* ************************************************* */
779
780 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
781 enum {
782         TFM_MODAL_CANCEL         = 1,
783         TFM_MODAL_CONFIRM        = 2,
784         TFM_MODAL_TRANSLATE      = 3,
785         TFM_MODAL_ROTATE         = 4,
786         TFM_MODAL_RESIZE         = 5,
787         TFM_MODAL_SNAP_INV_ON    = 6,
788         TFM_MODAL_SNAP_INV_OFF   = 7,
789         TFM_MODAL_SNAP_TOGGLE    = 8,
790         TFM_MODAL_AXIS_X         = 9,
791         TFM_MODAL_AXIS_Y         = 10,
792         TFM_MODAL_AXIS_Z         = 11,
793         TFM_MODAL_PLANE_X        = 12,
794         TFM_MODAL_PLANE_Y        = 13,
795         TFM_MODAL_PLANE_Z        = 14,
796         TFM_MODAL_CONS_OFF       = 15,
797         TFM_MODAL_ADD_SNAP       = 16,
798         TFM_MODAL_REMOVE_SNAP    = 17,
799
800 /*      18 and 19 used by numinput, defined in transform.h */
801
802         TFM_MODAL_PROPSIZE_UP    = 20,
803         TFM_MODAL_PROPSIZE_DOWN  = 21,
804         TFM_MODAL_AUTOIK_LEN_INC = 22,
805         TFM_MODAL_AUTOIK_LEN_DEC = 23,
806
807         TFM_MODAL_EDGESLIDE_UP   = 24,
808         TFM_MODAL_EDGESLIDE_DOWN = 25,
809
810 /* for analog input, like trackpad */
811         TFM_MODAL_PROPSIZE       = 26,
812 /* node editor insert offset (aka auto-offset) direction toggle */
813         TFM_MODAL_INSERTOFS_TOGGLE_DIR         = 27,
814 };
815
816 static bool transform_modal_item_poll(const wmOperator *op, int value)
817 {
818         const TransInfo *t = op->customdata;
819         switch (value) {
820                 case TFM_MODAL_PROPSIZE:
821                 case TFM_MODAL_PROPSIZE_UP:
822                 case TFM_MODAL_PROPSIZE_DOWN:
823                 {
824                         if ((t->flag & T_PROP_EDIT) == 0) {
825                                 return false;
826                         }
827                         break;
828                 }
829                 case TFM_MODAL_ADD_SNAP:
830                 case TFM_MODAL_REMOVE_SNAP:
831                 {
832                         if (t->spacetype != SPACE_VIEW3D) {
833                                 return false;
834                         }
835                         else if (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) {
836                                 return false;
837                         }
838                         else if (!validSnap(t)) {
839                                 return false;
840                         }
841                         break;
842                 }
843                 case TFM_MODAL_AXIS_X:
844                 case TFM_MODAL_AXIS_Y:
845                 case TFM_MODAL_AXIS_Z:
846                 case TFM_MODAL_PLANE_X:
847                 case TFM_MODAL_PLANE_Y:
848                 case TFM_MODAL_PLANE_Z:
849                 {
850                         if (t->flag & T_NO_CONSTRAINT) {
851                                 return false;
852                         }
853                         if (!ELEM(value, TFM_MODAL_AXIS_X, TFM_MODAL_AXIS_Y)) {
854                                 if (t->flag & T_2D_EDIT) {
855                                         return false;
856                                 }
857                         }
858                         break;
859                 }
860                 case TFM_MODAL_CONS_OFF:
861                 {
862                         if ((t->con.mode & CON_APPLY) == 0) {
863                                 return false;
864                         }
865                         break;
866                 }
867                 case TFM_MODAL_EDGESLIDE_UP:
868                 case TFM_MODAL_EDGESLIDE_DOWN:
869                 {
870                         if (t->mode != TFM_EDGE_SLIDE) {
871                                 return false;
872                         }
873                         break;
874                 }
875                 case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
876                 {
877                         if (t->spacetype != SPACE_NODE) {
878                                 return false;
879                         }
880                         break;
881                 }
882                 case TFM_MODAL_AUTOIK_LEN_INC:
883                 case TFM_MODAL_AUTOIK_LEN_DEC:
884                 {
885                         if ((t->flag & T_AUTOIK) == 0) {
886                                 return false;
887                         }
888                         break;
889                 }
890         }
891         return true;
892 }
893
894 /* called in transform_ops.c, on each regeneration of keymaps */
895 wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
896 {
897         static const EnumPropertyItem modal_items[] = {
898                 {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
899                 {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
900                 {TFM_MODAL_AXIS_X, "AXIS_X", 0, "X axis", ""},
901                 {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Y axis", ""},
902                 {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Z axis", ""},
903                 {TFM_MODAL_PLANE_X, "PLANE_X", 0, "X plane", ""},
904                 {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Y plane", ""},
905                 {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Z plane", ""},
906                 {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Clear Constraints", ""},
907                 {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Invert Snap", ""},
908                 {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Invert Snap Off", ""},
909                 {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
910                 {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
911                 {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
912                 {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
913                 {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
914                 {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
915                 {TFM_MODAL_PROPSIZE_DOWN, "PROPORTIONAL_SIZE_DOWN", 0, "Decrease Proportional Influence", ""},
916                 {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
917                 {TFM_MODAL_AUTOIK_LEN_DEC, "AUTOIK_CHAIN_LEN_DOWN", 0, "Decrease Max AutoIK Chain Length", ""},
918                 {TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
919                 {TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
920                 {TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
921                 {TFM_MODAL_INSERTOFS_TOGGLE_DIR, "INSERTOFS_TOGGLE_DIR", 0, "Toggle Direction for Node Auto-offset", ""},
922                 {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Translate", ""},
923                 {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
924                 {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
925                 {0, NULL, 0, NULL, NULL}
926         };
927
928         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
929
930         /* this function is called for each spacetype, only needs to add map once */
931         if (keymap && keymap->modal_items) return NULL;
932
933         keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
934         keymap->poll_modal_item = transform_modal_item_poll;
935
936         /* items for modal map */
937         WM_modalkeymap_add_item(keymap, LEFTMOUSE,  KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
938         WM_modalkeymap_add_item(keymap, RETKEY,     KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
939         WM_modalkeymap_add_item(keymap, PADENTER,   KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
940         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL);
941         WM_modalkeymap_add_item(keymap, ESCKEY,     KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL);
942
943         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, TFM_MODAL_AXIS_X);
944         WM_modalkeymap_add_item(keymap, YKEY, KM_PRESS, 0, 0, TFM_MODAL_AXIS_Y);
945         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, TFM_MODAL_AXIS_Z);
946
947         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PLANE_X);
948         WM_modalkeymap_add_item(keymap, YKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PLANE_Y);
949         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PLANE_Z);
950
951         WM_modalkeymap_add_item(keymap, CKEY, KM_PRESS, 0, 0, TFM_MODAL_CONS_OFF);
952
953         WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE);
954         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE);
955         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, TFM_MODAL_RESIZE);
956
957         WM_modalkeymap_add_item(keymap, TABKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_SNAP_TOGGLE);
958
959         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
960         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
961
962         WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
963         WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
964
965         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, TFM_MODAL_ADD_SNAP);
966         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, KM_ALT, 0, TFM_MODAL_REMOVE_SNAP);
967
968         WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
969         WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
970         WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP);
971         WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN);
972         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
973         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
974         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP);
975         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN);
976         WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, TFM_MODAL_PROPSIZE);
977
978         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_UP);
979         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_DOWN);
980
981         WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
982         WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
983         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
984         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
985
986         /* node editor only */
987         WM_modalkeymap_add_item(keymap, TKEY, KM_PRESS, 0, 0, TFM_MODAL_INSERTOFS_TOGGLE_DIR);
988
989         return keymap;
990 }
991
992 static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode, bool is_plane)
993 {
994         if (!(t->flag & T_NO_CONSTRAINT)) {
995                 int constraint_axis, constraint_plane;
996                 const bool edit_2d = (t->flag & T_2D_EDIT) != 0;
997                 const char *msg1 = "", *msg2 = "", *msg3 = "";
998                 char axis;
999
1000                 /* Initialize */
1001                 switch (key_type) {
1002                         case XKEY:
1003                                 msg1 = IFACE_("along X");
1004                                 msg2 = IFACE_("along %s X");
1005                                 msg3 = IFACE_("locking %s X");
1006                                 axis = 'X';
1007                                 constraint_axis = CON_AXIS0;
1008                                 break;
1009                         case YKEY:
1010                                 msg1 = IFACE_("along Y");
1011                                 msg2 = IFACE_("along %s Y");
1012                                 msg3 = IFACE_("locking %s Y");
1013                                 axis = 'Y';
1014                                 constraint_axis = CON_AXIS1;
1015                                 break;
1016                         case ZKEY:
1017                                 msg1 = IFACE_("along Z");
1018                                 msg2 = IFACE_("along %s Z");
1019                                 msg3 = IFACE_("locking %s Z");
1020                                 axis = 'Z';
1021                                 constraint_axis = CON_AXIS2;
1022                                 break;
1023                         default:
1024                                 /* Invalid key */
1025                                 return;
1026                 }
1027                 constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
1028
1029                 if (edit_2d && (key_type != ZKEY)) {
1030                         if (cmode == axis) {
1031                                 stopConstraint(t);
1032                         }
1033                         else {
1034                                 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg1);
1035                         }
1036                 }
1037                 else if (!edit_2d) {
1038                         if (cmode == axis) {
1039                                 if (t->con.orientation != V3D_MANIP_GLOBAL) {
1040                                         stopConstraint(t);
1041                                 }
1042                                 else {
1043                                         short orientation = (t->current_orientation != V3D_MANIP_GLOBAL ?
1044                                                              t->current_orientation : V3D_MANIP_LOCAL);
1045                                         if (is_plane == false) {
1046                                                 setUserConstraint(t, orientation, constraint_axis, msg2);
1047                                         }
1048                                         else {
1049                                                 setUserConstraint(t, orientation, constraint_plane, msg3);
1050                                         }
1051                                 }
1052                         }
1053                         else {
1054                                 if (is_plane == false) {
1055                                         setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg2);
1056                                 }
1057                                 else {
1058                                         setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_plane, msg3);
1059                                 }
1060                         }
1061                 }
1062                 t->redraw |= TREDRAW_HARD;
1063         }
1064 }
1065
1066 int transformEvent(TransInfo *t, const wmEvent *event)
1067 {
1068         char cmode = constraintModeToChar(t);
1069         bool handled = false;
1070         const int modifiers_prev = t->modifiers;
1071
1072         t->redraw |= handleMouseInput(t, &t->mouse, event);
1073
1074         /* Handle modal numinput events first, if already activated. */
1075         if (((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1076             hasNumInput(&t->num) && handleNumInput(t->context, &(t->num), event))
1077         {
1078                 t->redraw |= TREDRAW_HARD;
1079                 handled = true;
1080         }
1081         else if (event->type == MOUSEMOVE) {
1082                 if (t->modifiers & MOD_CONSTRAINT_SELECT)
1083                         t->con.mode |= CON_SELECT;
1084
1085                 copy_v2_v2_int(t->mval, event->mval);
1086
1087                 // t->redraw |= TREDRAW_SOFT; /* Use this for soft redraw. Might cause flicker in object mode */
1088                 t->redraw |= TREDRAW_HARD;
1089
1090                 if (t->state == TRANS_STARTING) {
1091                         t->state = TRANS_RUNNING;
1092                 }
1093
1094                 applyMouseInput(t, &t->mouse, t->mval, t->values);
1095
1096                 // Snapping mouse move events
1097                 t->redraw |= handleSnapping(t, event);
1098                 handled = true;
1099         }
1100         /* handle modal keymap first */
1101         else if (event->type == EVT_MODAL_MAP) {
1102                 switch (event->val) {
1103                         case TFM_MODAL_CANCEL:
1104                                 t->state = TRANS_CANCEL;
1105                                 handled = true;
1106                                 break;
1107                         case TFM_MODAL_CONFIRM:
1108                                 t->state = TRANS_CONFIRM;
1109                                 handled = true;
1110                                 break;
1111                         case TFM_MODAL_TRANSLATE:
1112                                 /* only switch when... */
1113                                 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1114                                         restoreTransObjects(t);
1115                                         resetTransModal(t);
1116                                         resetTransRestrictions(t);
1117                                         initTranslation(t);
1118                                         initSnapping(t, NULL); // need to reinit after mode change
1119                                         t->redraw |= TREDRAW_HARD;
1120                                         WM_event_add_mousemove(t->context);
1121                                         handled = true;
1122                                 }
1123                                 else if (t->mode == TFM_SEQ_SLIDE) {
1124                                         t->flag ^= T_ALT_TRANSFORM;
1125                                         t->redraw |= TREDRAW_HARD;
1126                                         handled = true;
1127                                 }
1128                                 else {
1129                                         if (t->obedit_type == OB_MESH) {
1130                                                 if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) {
1131                                                         restoreTransObjects(t);
1132                                                         resetTransModal(t);
1133                                                         resetTransRestrictions(t);
1134
1135                                                         /* first try edge slide */
1136                                                         initEdgeSlide(t);
1137                                                         /* if that fails, do vertex slide */
1138                                                         if (t->state == TRANS_CANCEL) {
1139                                                                 resetTransModal(t);
1140                                                                 t->state = TRANS_STARTING;
1141                                                                 initVertSlide(t);
1142                                                         }
1143                                                         /* vert slide can fail on unconnected vertices (rare but possible) */
1144                                                         if (t->state == TRANS_CANCEL) {
1145                                                                 resetTransModal(t);
1146                                                                 t->mode = TFM_TRANSLATION;
1147                                                                 t->state = TRANS_STARTING;
1148                                                                 restoreTransObjects(t);
1149                                                                 resetTransRestrictions(t);
1150                                                                 initTranslation(t);
1151                                                         }
1152                                                         initSnapping(t, NULL); // need to reinit after mode change
1153                                                         t->redraw |= TREDRAW_HARD;
1154                                                         handled = true;
1155                                                         WM_event_add_mousemove(t->context);
1156                                                 }
1157                                         }
1158                                         else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
1159                                                 if (t->mode == TFM_TRANSLATION) {
1160                                                         restoreTransObjects(t);
1161
1162                                                         t->flag ^= T_ALT_TRANSFORM;
1163                                                         t->redraw |= TREDRAW_HARD;
1164                                                         handled = true;
1165                                                 }
1166                                         }
1167                                 }
1168                                 break;
1169                         case TFM_MODAL_ROTATE:
1170                                 /* only switch when... */
1171                                 if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
1172                                         if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1173                                                 restoreTransObjects(t);
1174                                                 resetTransModal(t);
1175                                                 resetTransRestrictions(t);
1176
1177                                                 if (t->mode == TFM_ROTATION) {
1178                                                         initTrackball(t);
1179                                                 }
1180                                                 else {
1181                                                         initRotation(t);
1182                                                 }
1183                                                 initSnapping(t, NULL); // need to reinit after mode change
1184                                                 t->redraw |= TREDRAW_HARD;
1185                                                 handled = true;
1186                                         }
1187                                 }
1188                                 break;
1189                         case TFM_MODAL_RESIZE:
1190                                 /* only switch when... */
1191                                 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1192
1193                                         /* Scale isn't normally very useful after extrude along normals, see T39756 */
1194                                         if ((t->con.mode & CON_APPLY) && (t->con.orientation == V3D_MANIP_NORMAL)) {
1195                                                 stopConstraint(t);
1196                                         }
1197
1198                                         restoreTransObjects(t);
1199                                         resetTransModal(t);
1200                                         resetTransRestrictions(t);
1201                                         initResize(t);
1202                                         initSnapping(t, NULL); // need to reinit after mode change
1203                                         t->redraw |= TREDRAW_HARD;
1204                                         handled = true;
1205                                 }
1206                                 else if (t->mode == TFM_SHRINKFATTEN) {
1207                                         t->flag ^= T_ALT_TRANSFORM;
1208                                         t->redraw |= TREDRAW_HARD;
1209                                         handled = true;
1210                                 }
1211                                 else if (t->mode == TFM_RESIZE) {
1212                                         if (t->options & CTX_MOVIECLIP) {
1213                                                 restoreTransObjects(t);
1214
1215                                                 t->flag ^= T_ALT_TRANSFORM;
1216                                                 t->redraw |= TREDRAW_HARD;
1217                                                 handled = true;
1218                                         }
1219                                 }
1220                                 break;
1221
1222                         case TFM_MODAL_SNAP_INV_ON:
1223                                 t->modifiers |= MOD_SNAP_INVERT;
1224                                 t->redraw |= TREDRAW_HARD;
1225                                 handled = true;
1226                                 break;
1227                         case TFM_MODAL_SNAP_INV_OFF:
1228                                 t->modifiers &= ~MOD_SNAP_INVERT;
1229                                 t->redraw |= TREDRAW_HARD;
1230                                 handled = true;
1231                                 break;
1232                         case TFM_MODAL_SNAP_TOGGLE:
1233                                 t->modifiers ^= MOD_SNAP;
1234                                 t->redraw |= TREDRAW_HARD;
1235                                 handled = true;
1236                                 break;
1237                         case TFM_MODAL_AXIS_X:
1238                                 if (!(t->flag & T_NO_CONSTRAINT)) {
1239                                         transform_event_xyz_constraint(t, XKEY, cmode, false);
1240                                         t->redraw |= TREDRAW_HARD;
1241                                         handled = true;
1242                                 }
1243                                 break;
1244                         case TFM_MODAL_AXIS_Y:
1245                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1246                                         transform_event_xyz_constraint(t, YKEY, cmode, false);
1247                                         t->redraw |= TREDRAW_HARD;
1248                                         handled = true;
1249                                 }
1250                                 break;
1251                         case TFM_MODAL_AXIS_Z:
1252                                 if ((t->flag & (T_NO_CONSTRAINT)) == 0) {
1253                                         transform_event_xyz_constraint(t, ZKEY, cmode, false);
1254                                         t->redraw |= TREDRAW_HARD;
1255                                         handled = true;
1256                                 }
1257                                 break;
1258                         case TFM_MODAL_PLANE_X:
1259                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1260                                         transform_event_xyz_constraint(t, XKEY, cmode, true);
1261                                         t->redraw |= TREDRAW_HARD;
1262                                         handled = true;
1263                                 }
1264                                 break;
1265                         case TFM_MODAL_PLANE_Y:
1266                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1267                                         transform_event_xyz_constraint(t, YKEY, cmode, true);
1268                                         t->redraw |= TREDRAW_HARD;
1269                                         handled = true;
1270                                 }
1271                                 break;
1272                         case TFM_MODAL_PLANE_Z:
1273                                 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1274                                         transform_event_xyz_constraint(t, ZKEY, cmode, true);
1275                                         t->redraw |= TREDRAW_HARD;
1276                                         handled = true;
1277                                 }
1278                                 break;
1279                         case TFM_MODAL_CONS_OFF:
1280                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1281                                         stopConstraint(t);
1282                                         t->redraw |= TREDRAW_HARD;
1283                                         handled = true;
1284                                 }
1285                                 break;
1286                         case TFM_MODAL_ADD_SNAP:
1287                                 addSnapPoint(t);
1288                                 t->redraw |= TREDRAW_HARD;
1289                                 handled = true;
1290                                 break;
1291                         case TFM_MODAL_REMOVE_SNAP:
1292                                 removeSnapPoint(t);
1293                                 t->redraw |= TREDRAW_HARD;
1294                                 handled = true;
1295                                 break;
1296                         case TFM_MODAL_PROPSIZE:
1297                                 /* MOUSEPAN usage... */
1298                                 if (t->flag & T_PROP_EDIT) {
1299                                         float fac = 1.0f + 0.005f *(event->y - event->prevy);
1300                                         t->prop_size *= fac;
1301                                         if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1302                                                 t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->far), T_PROP_SIZE_MIN);
1303                                         }
1304                                         else {
1305                                                 t->prop_size = max_ff(min_ff(t->prop_size, T_PROP_SIZE_MAX), T_PROP_SIZE_MIN);
1306                                         }
1307                                         calculatePropRatio(t);
1308                                         t->redraw |= TREDRAW_HARD;
1309                                         handled = true;
1310                                 }
1311                                 break;
1312                         case TFM_MODAL_PROPSIZE_UP:
1313                                 if (t->flag & T_PROP_EDIT) {
1314                                         t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1315                                         if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1316                                                 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1317                                         }
1318                                         else {
1319                                                 t->prop_size = min_ff(t->prop_size, T_PROP_SIZE_MAX);
1320                                         }
1321                                         calculatePropRatio(t);
1322                                         t->redraw |= TREDRAW_HARD;
1323                                         handled = true;
1324                                 }
1325                                 break;
1326                         case TFM_MODAL_PROPSIZE_DOWN:
1327                                 if (t->flag & T_PROP_EDIT) {
1328                                         t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1329                                         t->prop_size = max_ff(t->prop_size, T_PROP_SIZE_MIN);
1330                                         calculatePropRatio(t);
1331                                         t->redraw |= TREDRAW_HARD;
1332                                         handled = true;
1333                                 }
1334                                 break;
1335                         case TFM_MODAL_AUTOIK_LEN_INC:
1336                                 if (t->flag & T_AUTOIK) {
1337                                         transform_autoik_update(t, 1);
1338                                         t->redraw |= TREDRAW_HARD;
1339                                         handled = true;
1340                                 }
1341                                 break;
1342                         case TFM_MODAL_AUTOIK_LEN_DEC:
1343                                 if (t->flag & T_AUTOIK) {
1344                                         transform_autoik_update(t, -1);
1345                                         t->redraw |= TREDRAW_HARD;
1346                                         handled = true;
1347                                 }
1348                                 break;
1349                         case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
1350                                 if (t->spacetype == SPACE_NODE) {
1351                                         SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
1352
1353                                         BLI_assert(t->sa->spacetype == t->spacetype);
1354
1355                                         if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT) {
1356                                                 snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_LEFT;
1357                                         }
1358                                         else if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_LEFT) {
1359                                                 snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_RIGHT;
1360                                         }
1361                                         else {
1362                                                 BLI_assert(0);
1363                                         }
1364
1365                                         t->redraw |= TREDRAW_SOFT;
1366                                 }
1367                                 break;
1368                         /* Those two are only handled in transform's own handler, see T44634! */
1369                         case TFM_MODAL_EDGESLIDE_UP:
1370                         case TFM_MODAL_EDGESLIDE_DOWN:
1371                         default:
1372                                 break;
1373                 }
1374         }
1375         /* else do non-mapped events */
1376         else if (event->val == KM_PRESS) {
1377                 switch (event->type) {
1378                         case RIGHTMOUSE:
1379                                 t->state = TRANS_CANCEL;
1380                                 handled = true;
1381                                 break;
1382                         /* enforce redraw of transform when modifiers are used */
1383                         case LEFTSHIFTKEY:
1384                         case RIGHTSHIFTKEY:
1385                                 t->modifiers |= MOD_CONSTRAINT_PLANE;
1386                                 t->redraw |= TREDRAW_HARD;
1387                                 handled = true;
1388                                 break;
1389
1390                         case SPACEKEY:
1391                                 t->state = TRANS_CONFIRM;
1392                                 handled = true;
1393                                 break;
1394
1395                         case MIDDLEMOUSE:
1396                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1397                                         /* exception for switching to dolly, or trackball, in camera view */
1398                                         if (t->flag & T_CAMERA) {
1399                                                 if (t->mode == TFM_TRANSLATION)
1400                                                         setLocalConstraint(t, (CON_AXIS2), IFACE_("along local Z"));
1401                                                 else if (t->mode == TFM_ROTATION) {
1402                                                         restoreTransObjects(t);
1403                                                         initTrackball(t);
1404                                                 }
1405                                         }
1406                                         else {
1407                                                 t->modifiers |= MOD_CONSTRAINT_SELECT;
1408                                                 if (t->con.mode & CON_APPLY) {
1409                                                         stopConstraint(t);
1410                                                 }
1411                                                 else {
1412                                                         if (event->shift) {
1413                                                                 initSelectConstraint(t, t->spacemtx);
1414                                                         }
1415                                                         else {
1416                                                                 /* bit hackish... but it prevents mmb select to print the orientation from menu */
1417                                                                 float mati[3][3];
1418                                                                 strcpy(t->spacename, "global");
1419                                                                 unit_m3(mati);
1420                                                                 initSelectConstraint(t, mati);
1421                                                         }
1422                                                         postSelectConstraint(t);
1423                                                 }
1424                                         }
1425                                         t->redraw |= TREDRAW_HARD;
1426                                         handled = true;
1427                                 }
1428                                 break;
1429                         case ESCKEY:
1430                                 t->state = TRANS_CANCEL;
1431                                 handled = true;
1432                                 break;
1433                         case PADENTER:
1434                         case RETKEY:
1435                                 t->state = TRANS_CONFIRM;
1436                                 handled = true;
1437                                 break;
1438                         case GKEY:
1439                                 /* only switch when... */
1440                                 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) {
1441                                         restoreTransObjects(t);
1442                                         resetTransModal(t);
1443                                         resetTransRestrictions(t);
1444                                         initTranslation(t);
1445                                         initSnapping(t, NULL); // need to reinit after mode change
1446                                         t->redraw |= TREDRAW_HARD;
1447                                         handled = true;
1448                                 }
1449                                 break;
1450                         case SKEY:
1451                                 /* only switch when... */
1452                                 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL)) {
1453                                         restoreTransObjects(t);
1454                                         resetTransModal(t);
1455                                         resetTransRestrictions(t);
1456                                         initResize(t);
1457                                         initSnapping(t, NULL); // need to reinit after mode change
1458                                         t->redraw |= TREDRAW_HARD;
1459                                         handled = true;
1460                                 }
1461                                 break;
1462                         case RKEY:
1463                                 /* only switch when... */
1464                                 if (!(t->options & CTX_TEXTURE)) {
1465                                         if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION)) {
1466                                                 restoreTransObjects(t);
1467                                                 resetTransModal(t);
1468                                                 resetTransRestrictions(t);
1469
1470                                                 if (t->mode == TFM_ROTATION) {
1471                                                         initTrackball(t);
1472                                                 }
1473                                                 else {
1474                                                         initRotation(t);
1475                                                 }
1476                                                 initSnapping(t, NULL); // need to reinit after mode change
1477                                                 t->redraw |= TREDRAW_HARD;
1478                                                 handled = true;
1479                                         }
1480                                 }
1481                                 break;
1482                         case CKEY:
1483                                 if (event->alt) {
1484                                         if (!(t->options & CTX_NO_PET)) {
1485                                                 t->flag ^= T_PROP_CONNECTED;
1486                                                 sort_trans_data_dist(t);
1487                                                 calculatePropRatio(t);
1488                                                 t->redraw = TREDRAW_HARD;
1489                                                 handled = true;
1490                                         }
1491                                 }
1492                                 break;
1493                         case OKEY:
1494                                 if (t->flag & T_PROP_EDIT && event->shift) {
1495                                         t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1496                                         calculatePropRatio(t);
1497                                         t->redraw |= TREDRAW_HARD;
1498                                         handled = true;
1499                                 }
1500                                 break;
1501                         case PADPLUSKEY:
1502                                 if (event->alt && t->flag & T_PROP_EDIT) {
1503                                         t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1504                                         if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1505                                                 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
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                         default:
1550                                 break;
1551                 }
1552
1553                 /* Snapping key events */
1554                 t->redraw |= handleSnapping(t, event);
1555         }
1556         else if (event->val == KM_RELEASE) {
1557                 switch (event->type) {
1558                         case LEFTSHIFTKEY:
1559                         case RIGHTSHIFTKEY:
1560                                 t->modifiers &= ~MOD_CONSTRAINT_PLANE;
1561                                 t->redraw |= TREDRAW_HARD;
1562                                 handled = true;
1563                                 break;
1564
1565                         case MIDDLEMOUSE:
1566                                 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1567                                         t->modifiers &= ~MOD_CONSTRAINT_SELECT;
1568                                         postSelectConstraint(t);
1569                                         t->redraw |= TREDRAW_HARD;
1570                                         handled = true;
1571                                 }
1572                                 break;
1573                         case LEFTALTKEY:
1574                         case RIGHTALTKEY:
1575                                 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1576                                         t->flag &= ~T_ALT_TRANSFORM;
1577                                         t->redraw |= TREDRAW_HARD;
1578                                         handled = true;
1579                                 }
1580                                 break;
1581                         default:
1582                                 break;
1583                 }
1584
1585                 /* confirm transform if launch key is released after mouse move */
1586                 if (t->flag & T_RELEASE_CONFIRM) {
1587                         /* XXX Keyrepeat bug in Xorg messes this up, will test when fixed */
1588                         if ((event->type == t->launch_event) && ISMOUSE(t->launch_event)) {
1589                                 t->state = TRANS_CONFIRM;
1590                         }
1591                 }
1592         }
1593
1594         /* if we change snap options, get the unsnapped values back */
1595         if ((t->modifiers   & (MOD_SNAP | MOD_SNAP_INVERT)) !=
1596             (modifiers_prev & (MOD_SNAP | MOD_SNAP_INVERT)))
1597         {
1598                 applyMouseInput(t, &t->mouse, t->mval, t->values);
1599         }
1600
1601         /* Per transform event, if present */
1602         if (t->handleEvent &&
1603             (!handled ||
1604              /* Needed for vertex slide, see [#38756] */
1605              (event->type == MOUSEMOVE)))
1606         {
1607                 t->redraw |= t->handleEvent(t, event);
1608         }
1609
1610         /* Try to init modal numinput now, if possible. */
1611         if (!(handled || t->redraw) && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1612             handleNumInput(t->context, &(t->num), event))
1613         {
1614                 t->redraw |= TREDRAW_HARD;
1615                 handled = true;
1616         }
1617
1618         if (t->redraw &&
1619             !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))
1620         {
1621                 WM_window_status_area_tag_redraw(CTX_wm_window(t->context));
1622         }
1623
1624         if (handled || t->redraw) {
1625                 return 0;
1626         }
1627         else {
1628                 return OPERATOR_PASS_THROUGH;
1629         }
1630 }
1631
1632 bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
1633 {
1634         TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
1635         bool success;
1636
1637         t->context = C;
1638
1639         t->state = TRANS_RUNNING;
1640
1641         /* avoid calculating PET */
1642         t->options = CTX_NO_PET;
1643
1644         t->mode = TFM_DUMMY;
1645
1646         initTransInfo(C, t, NULL, NULL);
1647
1648         /* avoid doing connectivity lookups (when V3D_AROUND_LOCAL_ORIGINS is set) */
1649         t->around = V3D_AROUND_CENTER_BOUNDS;
1650
1651         createTransData(C, t);              // make TransData structs from selection
1652
1653         t->around = centerMode;             // override userdefined mode
1654
1655         if (t->data_len_all == 0) {
1656                 success = false;
1657         }
1658         else {
1659                 success = true;
1660
1661                 calculateCenter(t);
1662
1663                 if (cent2d) {
1664                         copy_v2_v2(cent2d, t->center2d);
1665                 }
1666
1667                 if (cent3d) {
1668                         // Copy center from constraint center. Transform center can be local
1669                         copy_v3_v3(cent3d, t->center_global);
1670                 }
1671         }
1672
1673
1674         /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
1675         special_aftertrans_update(C, t);
1676
1677         postTrans(C, t);
1678
1679         MEM_freeN(t);
1680
1681         return success;
1682 }
1683
1684 typedef enum {
1685         UP,
1686         DOWN,
1687         LEFT,
1688         RIGHT
1689 } ArrowDirection;
1690
1691 #define POS_INDEX 0
1692 /* NOTE: this --^ is a bit hackish, but simplifies Gwn_VertFormat usage among functions
1693  * private to this file  - merwin
1694  */
1695
1696 static void drawArrow(ArrowDirection d, short offset, short length, short size)
1697 {
1698         immBegin(GWN_PRIM_LINES, 6);
1699
1700         switch (d) {
1701                 case LEFT:
1702                         offset = -offset;
1703                         length = -length;
1704                         size = -size;
1705                         ATTR_FALLTHROUGH;
1706                 case RIGHT:
1707                         immVertex2f(POS_INDEX, offset, 0);
1708                         immVertex2f(POS_INDEX, offset + length, 0);
1709                         immVertex2f(POS_INDEX, offset + length, 0);
1710                         immVertex2f(POS_INDEX, offset + length - size, -size);
1711                         immVertex2f(POS_INDEX, offset + length, 0);
1712                         immVertex2f(POS_INDEX, offset + length - size,  size);
1713                         break;
1714
1715                 case DOWN:
1716                         offset = -offset;
1717                         length = -length;
1718                         size = -size;
1719                         ATTR_FALLTHROUGH;
1720                 case UP:
1721                         immVertex2f(POS_INDEX, 0, offset);
1722                         immVertex2f(POS_INDEX, 0, offset + length);
1723                         immVertex2f(POS_INDEX, 0, offset + length);
1724                         immVertex2f(POS_INDEX, -size, offset + length - size);
1725                         immVertex2f(POS_INDEX, 0, offset + length);
1726                         immVertex2f(POS_INDEX, size, offset + length - size);
1727                         break;
1728         }
1729
1730         immEnd();
1731 }
1732
1733 static void drawArrowHead(ArrowDirection d, short size)
1734 {
1735         immBegin(GWN_PRIM_LINES, 4);
1736
1737         switch (d) {
1738                 case LEFT:
1739                         size = -size;
1740                         ATTR_FALLTHROUGH;
1741                 case RIGHT:
1742                         immVertex2f(POS_INDEX, 0, 0);
1743                         immVertex2f(POS_INDEX, -size, -size);
1744                         immVertex2f(POS_INDEX, 0, 0);
1745                         immVertex2f(POS_INDEX, -size,  size);
1746                         break;
1747
1748                 case DOWN:
1749                         size = -size;
1750                         ATTR_FALLTHROUGH;
1751                 case UP:
1752                         immVertex2f(POS_INDEX, 0, 0);
1753                         immVertex2f(POS_INDEX, -size, -size);
1754                         immVertex2f(POS_INDEX, 0, 0);
1755                         immVertex2f(POS_INDEX, size, -size);
1756                         break;
1757         }
1758
1759         immEnd();
1760 }
1761
1762 static void drawArc(float size, float angle_start, float angle_end, int segments)
1763 {
1764         float delta = (angle_end - angle_start) / segments;
1765         float angle;
1766         int a;
1767
1768         immBegin(GWN_PRIM_LINE_STRIP, segments + 1);
1769
1770         for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
1771                 immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size);
1772         }
1773         immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size);
1774
1775         immEnd();
1776 }
1777
1778 static bool helpline_poll(bContext *C)
1779 {
1780         ARegion *ar = CTX_wm_region(C);
1781
1782         if (ar && ar->regiontype == RGN_TYPE_WINDOW)
1783                 return 1;
1784         return 0;
1785 }
1786
1787 static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
1788 {
1789         TransInfo *t = (TransInfo *)customdata;
1790
1791         if (t->helpline != HLP_NONE) {
1792                 float cent[2];
1793                 float mval[3] = {
1794                     x,
1795                     y,
1796                     0.0f,
1797                 };
1798                 float tmval[2] = {
1799                     (float)t->mval[0],
1800                     (float)t->mval[1],
1801                 };
1802
1803                 projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO);
1804
1805                 /* Offset the values for the area region. */
1806                 const float offset[2] = {
1807                     t->ar->winrct.xmin,
1808                     t->ar->winrct.ymin,
1809                 };
1810
1811                 for (int i = 0; i < 2; i++) {
1812                         cent[i] += offset[i];
1813                         tmval[i] += offset[i];
1814                 }
1815
1816                 gpuPushMatrix();
1817
1818                 /* Dashed lines first. */
1819                 if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) {
1820                         const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
1821
1822                         UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */
1823                         BLI_assert(shdr_pos == POS_INDEX);
1824
1825                         GPU_line_width(1.0f);
1826
1827                         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
1828
1829                         float viewport_size[4];
1830                         GPU_viewport_size_get_f(viewport_size);
1831                         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
1832
1833                         immUniform1i("colors_len", 0);  /* "simple" mode */
1834                         immUniformThemeColor(TH_VIEW_OVERLAY);
1835                         immUniform1f("dash_width", 6.0f);
1836                         immUniform1f("dash_factor", 0.5f);
1837
1838                         immBegin(GWN_PRIM_LINES, 2);
1839                         immVertex2fv(POS_INDEX, cent);
1840                         immVertex2f(POS_INDEX, tmval[0], tmval[1]);
1841                         immEnd();
1842
1843                         immUnbindProgram();
1844                 }
1845
1846                 /* And now, solid lines. */
1847                 uint pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
1848                 UNUSED_VARS_NDEBUG(pos); /* silence warning */
1849                 BLI_assert(pos == POS_INDEX);
1850                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1851
1852                 switch (t->helpline) {
1853                         case HLP_SPRING:
1854                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1855
1856                                 gpuTranslate3fv(mval);
1857                                 gpuRotateAxis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z');
1858
1859                                 GPU_line_width(3.0f);
1860                                 drawArrow(UP, 5, 10, 5);
1861                                 drawArrow(DOWN, 5, 10, 5);
1862                                 break;
1863                         case HLP_HARROW:
1864                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1865                                 gpuTranslate3fv(mval);
1866
1867                                 GPU_line_width(3.0f);
1868                                 drawArrow(RIGHT, 5, 10, 5);
1869                                 drawArrow(LEFT, 5, 10, 5);
1870                                 break;
1871                         case HLP_VARROW:
1872                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1873
1874                                 gpuTranslate3fv(mval);
1875
1876                                 GPU_line_width(3.0f);
1877                                 drawArrow(UP, 5, 10, 5);
1878                                 drawArrow(DOWN, 5, 10, 5);
1879                                 break;
1880                         case HLP_ANGLE:
1881                         {
1882                                 float dx = tmval[0] - cent[0], dy = tmval[1] - cent[1];
1883                                 float angle = atan2f(dy, dx);
1884                                 float dist = hypotf(dx, dy);
1885                                 float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
1886                                 float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
1887
1888                                 immUniformThemeColor(TH_VIEW_OVERLAY);
1889
1890                                 gpuTranslate3f(cent[0] - tmval[0] + mval[0], cent[1] - tmval[1] + mval[1], 0);
1891
1892                                 GPU_line_width(3.0f);
1893                                 drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
1894                                 drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
1895
1896                                 gpuPushMatrix();
1897
1898                                 gpuTranslate3f(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
1899                                 gpuRotateAxis(RAD2DEGF(angle - delta_angle), 'Z');
1900
1901                                 drawArrowHead(DOWN, 5);
1902
1903                                 gpuPopMatrix();
1904
1905                                 gpuTranslate3f(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
1906                                 gpuRotateAxis(RAD2DEGF(angle + delta_angle), 'Z');
1907
1908                                 drawArrowHead(UP, 5);
1909                                 break;
1910                         }
1911                         case HLP_TRACKBALL:
1912                         {
1913                                 unsigned char col[3], col2[3];
1914                                 UI_GetThemeColor3ubv(TH_GRID, col);
1915
1916                                 gpuTranslate3fv(mval);
1917
1918                                 GPU_line_width(3.0f);
1919
1920                                 UI_make_axis_color(col, col2, 'X');
1921                                 immUniformColor3ubv(col2);
1922
1923                                 drawArrow(RIGHT, 5, 10, 5);
1924                                 drawArrow(LEFT, 5, 10, 5);
1925
1926                                 UI_make_axis_color(col, col2, 'Y');
1927                                 immUniformColor3ubv(col2);
1928
1929                                 drawArrow(UP, 5, 10, 5);
1930                                 drawArrow(DOWN, 5, 10, 5);
1931                                 break;
1932                         }
1933                 }
1934
1935                 immUnbindProgram();
1936                 gpuPopMatrix();
1937         }
1938 }
1939
1940 static void drawTransformView(const struct bContext *C, ARegion *UNUSED(ar), void *arg)
1941 {
1942         TransInfo *t = arg;
1943
1944         GPU_line_width(1.0f);
1945
1946         drawConstraint(t);
1947         drawPropCircle(C, t);
1948         drawSnapping(C, t);
1949
1950         /* edge slide, vert slide */
1951         drawEdgeSlide(t);
1952         drawVertSlide(t);
1953 }
1954
1955 /* just draw a little warning message in the top-right corner of the viewport to warn that autokeying is enabled */
1956 static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar)
1957 {
1958         rcti rect;
1959         const char *printable = IFACE_("Auto Keying On");
1960         float      printable_size[2];
1961         int xco, yco;
1962
1963         ED_region_visible_rect(ar, &rect);
1964
1965         const int font_id = BLF_default();
1966         BLF_width_and_height(font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
1967
1968         xco = (rect.xmax - U.widget_unit) - (int)printable_size[0];
1969         yco = (rect.ymax - U.widget_unit);
1970
1971         /* warning text (to clarify meaning of overlays)
1972          * - original color was red to match the icon, but that clashes badly with a less nasty border
1973          */
1974         unsigned char color[3];
1975         UI_GetThemeColorShade3ubv(TH_TEXT_HI, -50, color);
1976         BLF_color3ubv(font_id, color);
1977 #ifdef WITH_INTERNATIONAL
1978         BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1979 #else
1980         BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1981 #endif
1982
1983         /* autokey recording icon... */
1984         GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
1985         GPU_blend(true);
1986
1987         xco -= U.widget_unit;
1988         yco -= (int)printable_size[1] / 2;
1989
1990         UI_icon_draw(xco, yco, ICON_REC);
1991
1992         GPU_blend(false);
1993 }
1994
1995 static void drawTransformPixel(const struct bContext *UNUSED(C), ARegion *ar, void *arg)
1996 {
1997         TransInfo *t = arg;
1998         Scene *scene = t->scene;
1999         ViewLayer *view_layer = t->view_layer;
2000         Object *ob = OBACT(view_layer);
2001
2002         /* draw autokeyframing hint in the corner
2003          * - only draw if enabled (advanced users may be distracted/annoyed),
2004          *   for objects that will be autokeyframed (no point ohterwise),
2005          *   AND only for the active region (as showing all is too overwhelming)
2006          */
2007         if ((U.autokey_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
2008                 if (ar == t->ar) {
2009                         if (t->flag & (T_OBJECT | T_POSE)) {
2010                                 if (ob && autokeyframe_cfra_can_key(scene, &ob->id)) {
2011                                         drawAutoKeyWarning(t, ar);
2012                                 }
2013                         }
2014                 }
2015         }
2016 }
2017
2018 /**
2019  * \see #initTransform which reads values from the operator.
2020  */
2021 void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
2022 {
2023         ToolSettings *ts = CTX_data_tool_settings(C);
2024         bool constraint_axis[3] = {false, false, false};
2025         int proportional = 0;
2026         PropertyRNA *prop;
2027
2028         // Save back mode in case we're in the generic operator
2029         if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
2030                 RNA_property_enum_set(op->ptr, prop, t->mode);
2031         }
2032
2033         if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
2034                 float values[4];
2035
2036                 copy_v4_v4(values, (t->flag & T_AUTOVALUES) ? t->auto_values : t->values);
2037
2038                 if (RNA_property_array_check(prop)) {
2039                         RNA_property_float_set_array(op->ptr, prop, values);
2040                 }
2041                 else {
2042                         RNA_property_float_set(op->ptr, prop, values[0]);
2043                 }
2044         }
2045
2046         /* convert flag to enum */
2047         switch (t->flag & T_PROP_EDIT_ALL) {
2048                 case T_PROP_EDIT:
2049                         proportional = PROP_EDIT_ON;
2050                         break;
2051                 case (T_PROP_EDIT | T_PROP_CONNECTED):
2052                         proportional = PROP_EDIT_CONNECTED;
2053                         break;
2054                 case (T_PROP_EDIT | T_PROP_PROJECTED):
2055                         proportional = PROP_EDIT_PROJECTED;
2056                         break;
2057                 default:
2058                         proportional = PROP_EDIT_OFF;
2059                         break;
2060         }
2061
2062         // If modal, save settings back in scene if not set as operator argument
2063         if ((t->flag & T_MODAL) || (op->flag & OP_IS_REPEAT)) {
2064                 /* save settings if not set in operator */
2065
2066                 /* skip saving proportional edit if it was not actually used */
2067                 if (!(t->options & CTX_NO_PET)) {
2068                         if ((prop = RNA_struct_find_property(op->ptr, "proportional")) &&
2069                             !RNA_property_is_set(op->ptr, prop))
2070                         {
2071                                 if (t->spacetype == SPACE_IPO)
2072                                         ts->proportional_fcurve = proportional;
2073                                 else if (t->spacetype == SPACE_ACTION)
2074                                         ts->proportional_action = proportional;
2075                                 else if (t->obedit_type != -1)
2076                                         ts->proportional = proportional;
2077                                 else if (t->options & CTX_MASK)
2078                                         ts->proportional_mask = (proportional != PROP_EDIT_OFF);
2079                                 else
2080                                         ts->proportional_objects = (proportional != PROP_EDIT_OFF);
2081                         }
2082
2083                         if ((prop = RNA_struct_find_property(op->ptr, "proportional_size"))) {
2084                                 ts->proportional_size =
2085                                         RNA_property_is_set(op->ptr, prop) ? RNA_property_float_get(op->ptr, prop) : t->prop_size;
2086                         }
2087
2088                         if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
2089                             !RNA_property_is_set(op->ptr, prop))
2090                         {
2091                                 ts->prop_mode = t->prop_mode;
2092                         }
2093                 }
2094
2095                 /* do we check for parameter? */
2096                 if (t->modifiers & MOD_SNAP) {
2097                         ts->snap_flag |= SCE_SNAP;
2098                 }
2099                 else {
2100                         ts->snap_flag &= ~SCE_SNAP;
2101                 }
2102
2103                 if (t->spacetype == SPACE_VIEW3D) {
2104                         if ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) &&
2105                             !RNA_property_is_set(op->ptr, prop))
2106                         {
2107                                 t->scene->orientation_type = t->current_orientation;
2108                                 BLI_assert(((t->scene->orientation_index_custom == -1) && (t->custom_orientation == NULL)) ||
2109                                            (BKE_scene_transform_orientation_get_index(
2110                                                     t->scene, t->custom_orientation) == t->scene->orientation_index_custom));
2111                         }
2112                 }
2113         }
2114
2115         if ((prop = RNA_struct_find_property(op->ptr, "proportional"))) {
2116                 RNA_property_enum_set(op->ptr, prop, proportional);
2117                 RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
2118                 RNA_float_set(op->ptr, "proportional_size", t->prop_size);
2119         }
2120
2121         if ((prop = RNA_struct_find_property(op->ptr, "axis"))) {
2122                 RNA_property_float_set_array(op->ptr, prop, t->axis);
2123         }
2124
2125         if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
2126                 RNA_property_boolean_set(op->ptr, prop, (t->flag & T_MIRROR) != 0);
2127         }
2128
2129         if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
2130                 /* constraint orientation can be global, even if user selects something else
2131                  * so use the orientation in the constraint if set */
2132                 short orientation = (t->con.mode & CON_APPLY) ? t->con.orientation : t->current_orientation;
2133
2134                 if (orientation == V3D_MANIP_CUSTOM) {
2135                         const int orientation_index_custom = BKE_scene_transform_orientation_get_index(
2136                                 t->scene, t->custom_orientation);
2137
2138                         /* Maybe we need a t->con.custom_orientation? Seems like it would always match t->custom_orientation. */
2139                         orientation = V3D_MANIP_CUSTOM + orientation_index_custom;
2140                         BLI_assert(orientation >= V3D_MANIP_CUSTOM);
2141                 }
2142                 RNA_enum_set(op->ptr, "constraint_orientation", orientation);
2143
2144                 if (t->con.mode & CON_APPLY) {
2145                         if (t->con.mode & CON_AXIS0) {
2146                                 constraint_axis[0] = true;
2147                         }
2148                         if (t->con.mode & CON_AXIS1) {
2149                                 constraint_axis[1] = true;
2150                         }
2151                         if (t->con.mode & CON_AXIS2) {
2152                                 constraint_axis[2] = true;
2153                         }
2154                 }
2155
2156                 RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
2157         }
2158
2159         {
2160                 const char *prop_id = NULL;
2161                 if (t->mode == TFM_SHRINKFATTEN) {
2162                         prop_id = "use_even_offset";
2163                 }
2164
2165                 if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) {
2166
2167                         RNA_property_boolean_set(op->ptr, prop, (t->flag & T_ALT_TRANSFORM) != 0);
2168                 }
2169         }
2170
2171         if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
2172                 RNA_property_boolean_set(op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
2173         }
2174 }
2175
2176 /**
2177  * \note  caller needs to free 't' on a 0 return
2178  * \warning  \a event might be NULL (when tweaking from redo panel)
2179  * \see #saveTransform which writes these values back.
2180  */
2181 bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
2182 {
2183         int options = 0;
2184         PropertyRNA *prop;
2185
2186         t->context = C;
2187
2188         /* added initialize, for external calls to set stuff in TransInfo, like undo string */
2189
2190         t->state = TRANS_STARTING;
2191
2192         if ((prop = RNA_struct_find_property(op->ptr, "cursor_transform")) && RNA_property_is_set(op->ptr, prop)) {
2193                 if (RNA_property_boolean_get(op->ptr, prop)) {
2194                         options |= CTX_CURSOR;
2195                 }
2196         }
2197
2198         if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) && RNA_property_is_set(op->ptr, prop)) {
2199                 if (RNA_property_boolean_get(op->ptr, prop)) {
2200                         options |= CTX_TEXTURE;
2201                 }
2202         }
2203
2204         if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) && RNA_property_is_set(op->ptr, prop)) {
2205                 if (RNA_property_boolean_get(op->ptr, prop)) {
2206                         options |= CTX_GPENCIL_STROKES;
2207                 }
2208         }
2209
2210         t->options = options;
2211
2212         t->mode = mode;
2213
2214         /* Needed to translate tweak events to mouse buttons. */
2215         t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1;
2216
2217         // XXX Remove this when wm_operator_call_internal doesn't use window->eventstate (which can have type = 0)
2218         // For manipulator only, so assume LEFTMOUSE
2219         if (t->launch_event == 0) {
2220                 t->launch_event = LEFTMOUSE;
2221         }
2222
2223         unit_m3(t->spacemtx);
2224
2225         initTransInfo(C, t, op, event);
2226         initTransformOrientation(C, t);
2227
2228         if (t->spacetype == SPACE_VIEW3D) {
2229                 t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
2230                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2231                 t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2232                 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2233         }
2234         else if (t->spacetype == SPACE_IMAGE) {
2235                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2236                 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2237                 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2238         }
2239         else if (t->spacetype == SPACE_CLIP) {
2240                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2241                 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2242         }
2243         else if (t->spacetype == SPACE_NODE) {
2244                 /*t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);*/
2245                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2246                 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2247         }
2248         else if (t->spacetype == SPACE_IPO) {
2249                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2250                 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2251                 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2252         }
2253         else if (t->spacetype == SPACE_ACTION) {
2254                 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2255                 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2256                 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2257         }
2258
2259         createTransData(C, t);          // make TransData structs from selection
2260
2261         if (t->data_len_all == 0) {
2262                 postTrans(C, t);
2263                 return 0;
2264         }
2265
2266         if (event) {
2267                 /* keymap for shortcut header prints */
2268                 t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
2269
2270                 /* Stupid code to have Ctrl-Click on manipulator work ok
2271                  *
2272                  * do this only for translation/rotation/resize due to only this
2273                  * moded are available from manipulator and doing such check could
2274                  * lead to keymap conflicts for other modes (see #31584)
2275                  */
2276                 if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
2277                         wmKeyMapItem *kmi;
2278
2279                         for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) {
2280                                 if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
2281                                         if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) &&   event->ctrl)  ||
2282                                             (ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
2283                                             (ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) &&     event->alt)   ||
2284                                             ((kmi->type == OSKEY) &&                         event->oskey) )
2285                                         {
2286                                                 t->modifiers |= MOD_SNAP_INVERT;
2287                                         }
2288                                         break;
2289                                 }
2290                         }
2291                 }
2292         }
2293
2294         initSnapping(t, op); // Initialize snapping data AFTER mode flags
2295
2296         initSnapSpatial(t, t->snap_spatial);
2297
2298         /* EVIL! posemode code can switch translation to rotate when 1 bone is selected. will be removed (ton) */
2299         /* EVIL2: we gave as argument also texture space context bit... was cleared */
2300         /* EVIL3: extend mode for animation editors also switches modes... but is best way to avoid duplicate code */
2301         mode = t->mode;
2302
2303         calculatePropRatio(t);
2304         calculateCenter(t);
2305
2306         /* Overwrite initial values if operator supplied a non-null vector.
2307          *
2308          * Run before init functions so 'values_modal_offset' can be applied on mouse input.
2309          */
2310         BLI_assert(is_zero_v4(t->values_modal_offset));
2311         if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
2312                 float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory  */
2313
2314                 if (RNA_property_array_check(prop)) {
2315                         RNA_float_get_array(op->ptr, "value", values);
2316                 }
2317                 else {
2318                         values[0] = RNA_float_get(op->ptr, "value");
2319                 }
2320
2321                 copy_v4_v4(t->values, values);
2322
2323                 if (t->flag & T_MODAL) {
2324                         copy_v4_v4(t->values_modal_offset, values);
2325                         t->redraw = TREDRAW_HARD;
2326                 }
2327                 else {
2328                         copy_v4_v4(t->auto_values, values);
2329                         t->flag |= T_AUTOVALUES;
2330                 }
2331         }
2332
2333         /* Transformation axis from operator */
2334         if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) {
2335                 RNA_property_float_get_array(op->ptr, prop, t->axis);
2336                 normalize_v3(t->axis);
2337                 copy_v3_v3(t->axis_orig, t->axis);
2338         }
2339
2340         /* Constraint init from operator */
2341         if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) {
2342                 bool constraint_axis[3];
2343
2344                 RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
2345
2346                 if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
2347                         t->con.mode |= CON_APPLY;
2348
2349                         if (constraint_axis[0]) {
2350                                 t->con.mode |= CON_AXIS0;
2351                         }
2352                         if (constraint_axis[1]) {
2353                                 t->con.mode |= CON_AXIS1;
2354                         }
2355                         if (constraint_axis[2]) {
2356                                 t->con.mode |= CON_AXIS2;
2357                         }
2358
2359                         setUserConstraint(t, t->current_orientation, t->con.mode, "%s");
2360                 }
2361         }
2362
2363         if (event) {
2364                 /* Initialize accurate transform to settings requested by keymap. */
2365                 bool use_accurate = false;
2366                 if ((prop = RNA_struct_find_property(op->ptr, "use_accurate")) && RNA_property_is_set(op->ptr, prop)) {
2367                         if (RNA_property_boolean_get(op->ptr, prop)) {
2368                                 use_accurate = true;
2369                         }
2370                 }
2371                 initMouseInput(t, &t->mouse, t->center2d, event->mval, use_accurate);
2372         }
2373
2374         switch (mode) {
2375                 case TFM_TRANSLATION:
2376                         initTranslation(t);
2377                         break;
2378                 case TFM_ROTATION:
2379                         initRotation(t);
2380                         break;
2381                 case TFM_RESIZE:
2382                         initResize(t);
2383                         break;
2384                 case TFM_SKIN_RESIZE:
2385                         initSkinResize(t);
2386                         break;
2387                 case TFM_TOSPHERE:
2388                         initToSphere(t);
2389                         break;
2390                 case TFM_SHEAR:
2391                         initShear(t);
2392                         break;
2393                 case TFM_BEND:
2394                         initBend(t);
2395                         break;
2396                 case TFM_SHRINKFATTEN:
2397                         initShrinkFatten(t);
2398                         break;
2399                 case TFM_TILT:
2400                         initTilt(t);
2401                         break;
2402                 case TFM_CURVE_SHRINKFATTEN:
2403                         initCurveShrinkFatten(t);
2404                         break;
2405                 case TFM_MASK_SHRINKFATTEN:
2406                         initMaskShrinkFatten(t);
2407                         break;
2408                 case TFM_GPENCIL_SHRINKFATTEN:
2409                         initGPShrinkFatten(t);
2410                         break;
2411                 case TFM_TRACKBALL:
2412                         initTrackball(t);
2413                         break;
2414                 case TFM_PUSHPULL:
2415                         initPushPull(t);
2416                         break;
2417                 case TFM_CREASE:
2418                         initCrease(t);
2419                         break;
2420                 case TFM_BONESIZE:
2421                 {   /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
2422                         /* Note: we have to pick one, use the active object. */
2423                         TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t);
2424                         bArmature *arm = tc->poseobj->data;
2425                         if (arm->drawtype == ARM_ENVELOPE) {
2426                                 initBoneEnvelope(t);
2427                                 t->mode = TFM_BONE_ENVELOPE_DIST;
2428                         }
2429                         else {
2430                                 initBoneSize(t);
2431                         }
2432                         break;
2433                 }
2434                 case TFM_BONE_ENVELOPE:
2435                         initBoneEnvelope(t);
2436                         break;
2437                 case TFM_BONE_ENVELOPE_DIST:
2438                         initBoneEnvelope(t);
2439                         t->mode = TFM_BONE_ENVELOPE_DIST;
2440                         break;
2441                 case TFM_EDGE_SLIDE:
2442                 case TFM_VERT_SLIDE:
2443                 {
2444                         const bool use_even = (op ? RNA_boolean_get(op->ptr, "use_even") : false);
2445                         const bool flipped = (op ? RNA_boolean_get(op->ptr, "flipped") : false);
2446                         const bool use_clamp = (op ? RNA_boolean_get(op->ptr, "use_clamp") : true);
2447                         if (mode == TFM_EDGE_SLIDE) {
2448                                 const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true);
2449                                 initEdgeSlide_ex(t, use_double_side, use_even, flipped, use_clamp);
2450                         }
2451                         else {
2452                                 initVertSlide_ex(t, use_even, flipped, use_clamp);
2453                         }
2454                         break;
2455                 }
2456                 case TFM_BONE_ROLL:
2457                         initBoneRoll(t);
2458                         break;
2459                 case TFM_TIME_TRANSLATE:
2460                         initTimeTranslate(t);
2461                         break;
2462                 case TFM_TIME_SLIDE:
2463                         initTimeSlide(t);
2464                         break;
2465                 case TFM_TIME_SCALE:
2466                         initTimeScale(t);
2467                         break;
2468                 case TFM_TIME_DUPLICATE:
2469                         /* same as TFM_TIME_EXTEND, but we need the mode info for later
2470                          * so that duplicate-culling will work properly
2471                          */
2472                         if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2473                                 initTranslation(t);
2474                         else
2475                                 initTimeTranslate(t);
2476                         t->mode = mode;
2477                         break;
2478                 case TFM_TIME_EXTEND:
2479                         /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
2480                          * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
2481                          * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
2482                          * depending on which editor this was called from
2483                          */
2484                         if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2485                                 initTranslation(t);
2486                         else
2487                                 initTimeTranslate(t);
2488                         break;
2489                 case TFM_BAKE_TIME:
2490                         initBakeTime(t);
2491                         break;
2492                 case TFM_MIRROR:
2493                         initMirror(t);
2494                         break;
2495                 case TFM_BWEIGHT:
2496                         initBevelWeight(t);
2497                         break;
2498                 case TFM_ALIGN:
2499                         initAlign(t);
2500                         break;
2501                 case TFM_SEQ_SLIDE:
2502                         initSeqSlide(t);
2503                         break;
2504         }
2505
2506         if (t->state == TRANS_CANCEL) {
2507                 postTrans(C, t);
2508                 return 0;
2509         }
2510
2511         t->context = NULL;
2512
2513         return 1;
2514 }
2515
2516 void transformApply(bContext *C, TransInfo *t)
2517 {
2518         t->context = C;
2519
2520         if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
2521                 selectConstraint(t);
2522                 if (t->transform) {
2523                         t->transform(t, t->mval);  // calls recalcData()
2524                         viewRedrawForce(C, t);
2525                 }
2526                 t->redraw = TREDRAW_NOTHING;
2527         }
2528         else if (t->redraw & TREDRAW_SOFT) {
2529                 viewRedrawForce(C, t);
2530         }
2531
2532         /* If auto confirm is on, break after one pass */
2533         if (t->options & CTX_AUTOCONFIRM) {
2534                 t->state = TRANS_CONFIRM;
2535         }
2536
2537         t->context = NULL;
2538 }
2539
2540 static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
2541 {
2542         TransInfo *t = arg;
2543
2544         if (t->redraw & TREDRAW_SOFT) {
2545                 t->redraw |= TREDRAW_HARD;
2546                 transformApply((bContext *)C, t);
2547         }
2548 }
2549
2550 int transformEnd(bContext *C, TransInfo *t)
2551 {
2552         int exit_code = OPERATOR_RUNNING_MODAL;
2553
2554         t->context = C;
2555
2556         if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
2557                 /* handle restoring objects */
2558                 if (t->state == TRANS_CANCEL) {
2559                         /* exception, edge slide transformed UVs too */
2560                         if (t->mode == TFM_EDGE_SLIDE) {
2561                                 doEdgeSlide(t, 0.0f);
2562                         }
2563                         else if (t->mode == TFM_VERT_SLIDE) {
2564                                 doVertSlide(t, 0.0f);
2565                         }
2566
2567                         exit_code = OPERATOR_CANCELLED;
2568                         restoreTransObjects(t); // calls recalcData()
2569                 }
2570                 else {
2571                         exit_code = OPERATOR_FINISHED;
2572                 }
2573
2574                 /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
2575                 special_aftertrans_update(C, t);
2576
2577                 /* free data */
2578                 postTrans(C, t);
2579
2580                 /* send events out for redraws */
2581                 viewRedrawPost(C, t);
2582
2583                 viewRedrawForce(C, t);
2584         }
2585
2586         t->context = NULL;
2587
2588         return exit_code;
2589 }
2590
2591 /* ************************** TRANSFORM LOCKS **************************** */
2592
2593 static void protectedTransBits(short protectflag, float vec[3])
2594 {
2595         if (protectflag & OB_LOCK_LOCX)
2596                 vec[0] = 0.0f;
2597         if (protectflag & OB_LOCK_LOCY)
2598                 vec[1] = 0.0f;
2599         if (protectflag & OB_LOCK_LOCZ)
2600                 vec[2] = 0.0f;
2601 }
2602
2603 static void protectedSizeBits(short protectflag, float size[3])
2604 {
2605         if (protectflag & OB_LOCK_SCALEX)
2606                 size[0] = 1.0f;
2607         if (protectflag & OB_LOCK_SCALEY)
2608                 size[1] = 1.0f;
2609         if (protectflag & OB_LOCK_SCALEZ)
2610                 size[2] = 1.0f;
2611 }
2612
2613 static void protectedRotateBits(short protectflag, float eul[3], const float oldeul[3])
2614 {
2615         if (protectflag & OB_LOCK_ROTX)
2616                 eul[0] = oldeul[0];
2617         if (protectflag & OB_LOCK_ROTY)
2618                 eul[1] = oldeul[1];
2619         if (protectflag & OB_LOCK_ROTZ)
2620                 eul[2] = oldeul[2];
2621 }
2622
2623
2624 /* this function only does the delta rotation */
2625 /* axis-angle is usually internally stored as quats... */
2626 static void protectedAxisAngleBits(short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle)
2627 {
2628         /* check that protection flags are set */
2629         if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2630                 return;
2631
2632         if (protectflag & OB_LOCK_ROT4D) {
2633                 /* axis-angle getting limited as 4D entities that they are... */
2634                 if (protectflag & OB_LOCK_ROTW)
2635                         *angle = oldAngle;
2636                 if (protectflag & OB_LOCK_ROTX)
2637                         axis[0] = oldAxis[0];
2638                 if (protectflag & OB_LOCK_ROTY)
2639                         axis[1] = oldAxis[1];
2640                 if (protectflag & OB_LOCK_ROTZ)
2641                         axis[2] = oldAxis[2];
2642         }
2643         else {
2644                 /* axis-angle get limited with euler... */
2645                 float eul[3], oldeul[3];
2646
2647                 axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle);
2648                 axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle);
2649
2650                 if (protectflag & OB_LOCK_ROTX)
2651                         eul[0] = oldeul[0];
2652                 if (protectflag & OB_LOCK_ROTY)
2653                         eul[1] = oldeul[1];
2654                 if (protectflag & OB_LOCK_ROTZ)
2655                         eul[2] = oldeul[2];
2656
2657                 eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT);
2658
2659                 /* when converting to axis-angle, we need a special exception for the case when there is no axis */
2660                 if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) {
2661                         /* for now, rotate around y-axis then (so that it simply becomes the roll) */
2662                         axis[1] = 1.0f;
2663                 }
2664         }
2665 }
2666
2667 /* this function only does the delta rotation */
2668 static void protectedQuaternionBits(short protectflag, float quat[4], const float oldquat[4])
2669 {
2670         /* check that protection flags are set */
2671         if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2672                 return;
2673
2674         if (protectflag & OB_LOCK_ROT4D) {
2675                 /* quaternions getting limited as 4D entities that they are... */
2676                 if (protectflag & OB_LOCK_ROTW)
2677                         quat[0] = oldquat[0];
2678                 if (protectflag & OB_LOCK_ROTX)
2679                         quat[1] = oldquat[1];
2680                 if (protectflag & OB_LOCK_ROTY)
2681                         quat[2] = oldquat[2];
2682                 if (protectflag & OB_LOCK_ROTZ)
2683                         quat[3] = oldquat[3];
2684         }
2685         else {
2686                 /* quaternions get limited with euler... (compatibility mode) */
2687                 float eul[3], oldeul[3], nquat[4], noldquat[4];
2688                 float qlen;
2689
2690                 qlen = normalize_qt_qt(nquat, quat);
2691                 normalize_qt_qt(noldquat, oldquat);
2692
2693                 quat_to_eul(eul, nquat);
2694                 quat_to_eul(oldeul, noldquat);
2695
2696                 if (protectflag & OB_LOCK_ROTX)
2697                         eul[0] = oldeul[0];
2698                 if (protectflag & OB_LOCK_ROTY)
2699                         eul[1] = oldeul[1];
2700                 if (protectflag & OB_LOCK_ROTZ)
2701                         eul[2] = oldeul[2];
2702
2703                 eul_to_quat(quat, eul);
2704
2705                 /* restore original quat size */
2706                 mul_qt_fl(quat, qlen);
2707
2708                 /* quaternions flip w sign to accumulate rotations correctly */
2709                 if ((nquat[0] < 0.0f && quat[0] > 0.0f) ||
2710                     (nquat[0] > 0.0f && quat[0] < 0.0f))
2711                 {
2712                         mul_qt_fl(quat, -1.0f);
2713                 }
2714         }
2715 }
2716
2717 /* ******************* TRANSFORM LIMITS ********************** */
2718
2719 static void constraintTransLim(TransInfo *t, TransData *td)
2720 {
2721         if (td->con) {
2722                 const bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_LOCLIMIT);
2723                 const bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_DISTLIMIT);
2724
2725                 bConstraintOb cob = {NULL};
2726                 bConstraint *con;
2727                 float ctime = (float)(t->scene->r.cfra);
2728
2729                 /* Make a temporary bConstraintOb for using these limit constraints
2730                  *  - they only care that cob->matrix is correctly set ;-)
2731                  *      - current space should be local
2732                  */
2733                 unit_m4(cob.matrix);
2734                 copy_v3_v3(cob.matrix[3], td->loc);
2735
2736                 /* Evaluate valid constraints */
2737                 for (con = td->con; con; con = con->next) {
2738                         const bConstraintTypeInfo *cti = NULL;
2739                         ListBase targets = {NULL, NULL};
2740
2741                         /* only consider constraint if enabled */
2742                         if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2743                         if (con->enforce == 0.0f) continue;
2744
2745                         /* only use it if it's tagged for this purpose (and the right type) */
2746                         if (con->type == CONSTRAINT_TYPE_LOCLIMIT) {
2747                                 bLocLimitConstraint *data = con->data;
2748
2749                                 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2750                                         continue;
2751                                 cti = ctiLoc;
2752                         }
2753                         else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) {
2754                                 bDistLimitConstraint *data = con->data;
2755
2756                                 if ((data->flag & LIMITDIST_TRANSFORM) == 0)
2757                                         continue;
2758                                 cti = ctiDist;
2759                         }
2760
2761                         if (cti) {
2762                                 /* do space conversions */
2763                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2764                                         /* just multiply by td->mtx (this should be ok) */
2765                                         mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2766                                 }
2767                                 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2768                                         /* skip... incompatable spacetype */
2769                                         continue;
2770                                 }
2771
2772                                 /* get constraint targets if needed */
2773                                 BKE_constraint_targets_for_solving_get(t->depsgraph, con, &cob, &targets, ctime);
2774
2775                                 /* do constraint */
2776                                 cti->evaluate_constraint(con, &cob, &targets);
2777
2778                                 /* convert spaces again */
2779                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2780                                         /* just multiply by td->smtx (this should be ok) */
2781                                         mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2782                                 }
2783
2784                                 /* free targets list */
2785                                 BLI_freelistN(&targets);
2786                         }
2787                 }
2788
2789                 /* copy results from cob->matrix */
2790                 copy_v3_v3(td->loc, cob.matrix[3]);
2791         }
2792 }
2793
2794 static void constraintob_from_transdata(bConstraintOb *cob, TransData *td)
2795 {
2796         /* Make a temporary bConstraintOb for use by limit constraints
2797          *  - they only care that cob->matrix is correctly set ;-)
2798          *      - current space should be local
2799          */
2800         memset(cob, 0, sizeof(bConstraintOb));
2801         if (td->ext) {
2802                 if (td->ext->rotOrder == ROT_MODE_QUAT) {
2803                         /* quats */
2804                         /* objects and bones do normalization first too, otherwise
2805                          * we don't necessarily end up with a rotation matrix, and
2806                          * then conversion back to quat gives a different result */
2807                         float quat[4];
2808                         normalize_qt_qt(quat, td->ext->quat);
2809                         quat_to_mat4(cob->matrix, quat);
2810                 }
2811                 else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2812                         /* axis angle */
2813                         axis_angle_to_mat4(cob->matrix, td->ext->rotAxis, *td->ext->rotAngle);
2814                 }
2815                 else {
2816                         /* eulers */
2817                         eulO_to_mat4(cob->matrix, td->ext->rot, td->ext->rotOrder);
2818                 }
2819         }
2820 }
2821
2822 static void constraintRotLim(TransInfo *UNUSED(t), TransData *td)
2823 {
2824         if (td->con) {
2825                 const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_ROTLIMIT);
2826                 bConstraintOb cob;
2827                 bConstraint *con;
2828                 bool do_limit = false;
2829
2830                 /* Evaluate valid constraints */
2831                 for (con = td->con; con; con = con->next) {
2832                         /* only consider constraint if enabled */
2833                         if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2834                         if (con->enforce == 0.0f) continue;
2835
2836                         /* we're only interested in Limit-Rotation constraints */
2837                         if (con->type == CONSTRAINT_TYPE_ROTLIMIT) {
2838                                 bRotLimitConstraint *data = con->data;
2839
2840                                 /* only use it if it's tagged for this purpose */
2841                                 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2842                                         continue;
2843
2844                                 /* skip incompatable spacetypes */
2845                                 if (!ELEM(con->ownspace, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL))
2846                                         continue;
2847
2848                                 /* only do conversion if necessary, to preserve quats and eulers */
2849                                 if (do_limit == false) {
2850                                         constraintob_from_transdata(&cob, td);
2851                                         do_limit = true;
2852                                 }
2853
2854                                 /* do space conversions */
2855                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2856                                         /* just multiply by td->mtx (this should be ok) */
2857                                         mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2858                                 }
2859
2860                                 /* do constraint */
2861                                 cti->evaluate_constraint(con, &cob, NULL);
2862
2863                                 /* convert spaces again */
2864                                 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2865                                         /* just multiply by td->smtx (this should be ok) */
2866                                         mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2867                                 }
2868                         }
2869                 }
2870
2871                 if (do_limit) {
2872                         /* copy results from cob->matrix */
2873                         if (td->ext->rotOrder == ROT_MODE_QUAT) {
2874                                 /* quats */
2875                                 mat4_to_quat(td->ext->quat, cob.matrix);
2876                         }
2877                         else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2878                                 /* axis angle */
2879                                 mat4_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, cob.matrix);
2880                         }
2881