2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): none yet.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/transform/transform.c
29 * \ingroup edtransform
38 #include "MEM_guardedalloc.h"
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"
48 #include "BLI_alloca.h"
49 #include "BLI_utildefines.h"
52 #include "BLI_listbase.h"
53 #include "BLI_string.h"
54 #include "BLI_ghash.h"
55 #include "BLI_stackdefines.h"
56 #include "BLI_memarena.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"
66 #include "BKE_report.h"
67 #include "BKE_workspace.h"
69 #include "BIF_glutil.h"
71 #include "GPU_immediate.h"
72 #include "GPU_immediate_util.h"
73 #include "GPU_matrix.h"
76 #include "ED_keyframing.h"
77 #include "ED_screen.h"
78 #include "ED_space_api.h"
79 #include "ED_markers.h"
80 #include "ED_view3d.h"
88 #include "UI_view2d.h"
89 #include "UI_interface.h"
90 #include "UI_interface_icons.h"
91 #include "UI_resources.h"
93 #include "RNA_access.h"
96 #include "BLT_translation.h"
98 #include "transform.h"
100 /* Disabling, since when you type you know what you are doing, and being able to set it to zero is handy. */
101 // #define USE_NUM_NO_ZERO
103 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
104 static void doEdgeSlide(TransInfo *t, float perc);
105 static void doVertSlide(TransInfo *t, float perc);
107 static void drawEdgeSlide(TransInfo *t);
108 static void drawVertSlide(TransInfo *t);
109 static void postInputRotation(TransInfo *t, float values[3]);
111 static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around);
112 static void initSnapSpatial(TransInfo *t, float r_snap[3]);
115 /* Transform Callbacks */
116 static void initBend(TransInfo *t);
117 static eRedrawFlag handleEventBend(TransInfo *t, const struct wmEvent *event);
118 static void Bend(TransInfo *t, const int mval[2]);
120 static void initShear(TransInfo *t);
121 static eRedrawFlag handleEventShear(TransInfo *t, const struct wmEvent *event);
122 static void applyShear(TransInfo *t, const int mval[2]);
124 static void initResize(TransInfo *t);
125 static void applyResize(TransInfo *t, const int mval[2]);
127 static void initSkinResize(TransInfo *t);
128 static void applySkinResize(TransInfo *t, const int mval[2]);
130 static void initTranslation(TransInfo *t);
131 static void applyTranslation(TransInfo *t, const int mval[2]);
133 static void initToSphere(TransInfo *t);
134 static void applyToSphere(TransInfo *t, const int mval[2]);
136 static void initRotation(TransInfo *t);
137 static void applyRotation(TransInfo *t, const int mval[2]);
139 static void initShrinkFatten(TransInfo *t);
140 static void applyShrinkFatten(TransInfo *t, const int mval[2]);
142 static void initTilt(TransInfo *t);
143 static void applyTilt(TransInfo *t, const int mval[2]);
145 static void initCurveShrinkFatten(TransInfo *t);
146 static void applyCurveShrinkFatten(TransInfo *t, const int mval[2]);
148 static void initMaskShrinkFatten(TransInfo *t);
149 static void applyMaskShrinkFatten(TransInfo *t, const int mval[2]);
151 static void initGPShrinkFatten(TransInfo *t);
152 static void applyGPShrinkFatten(TransInfo *t, const int mval[2]);
154 static void initTrackball(TransInfo *t);
155 static void applyTrackball(TransInfo *t, const int mval[2]);
157 static void initPushPull(TransInfo *t);
158 static void applyPushPull(TransInfo *t, const int mval[2]);
160 static void initBevelWeight(TransInfo *t);
161 static void applyBevelWeight(TransInfo *t, const int mval[2]);
163 static void initCrease(TransInfo *t);
164 static void applyCrease(TransInfo *t, const int mval[2]);
166 static void initBoneSize(TransInfo *t);
167 static void applyBoneSize(TransInfo *t, const int mval[2]);
169 static void initBoneEnvelope(TransInfo *t);
170 static void applyBoneEnvelope(TransInfo *t, const int mval[2]);
172 static void initBoneRoll(TransInfo *t);
173 static void applyBoneRoll(TransInfo *t, const int mval[2]);
175 static void initEdgeSlide_ex(TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp);
176 static void initEdgeSlide(TransInfo *t);
177 static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
178 static void applyEdgeSlide(TransInfo *t, const int mval[2]);
180 static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp);
181 static void initVertSlide(TransInfo *t);
182 static eRedrawFlag handleEventVertSlide(TransInfo *t, const struct wmEvent *event);
183 static void applyVertSlide(TransInfo *t, const int mval[2]);
185 static void initTimeTranslate(TransInfo *t);
186 static void applyTimeTranslate(TransInfo *t, const int mval[2]);
188 static void initTimeSlide(TransInfo *t);
189 static void applyTimeSlide(TransInfo *t, const int mval[2]);
191 static void initTimeScale(TransInfo *t);
192 static void applyTimeScale(TransInfo *t, const int mval[2]);
194 static void initBakeTime(TransInfo *t);
195 static void applyBakeTime(TransInfo *t, const int mval[2]);
197 static void initMirror(TransInfo *t);
198 static void applyMirror(TransInfo *t, const int mval[2]);
200 static void initAlign(TransInfo *t);
201 static void applyAlign(TransInfo *t, const int mval[2]);
203 static void initSeqSlide(TransInfo *t);
204 static void applySeqSlide(TransInfo *t, const int mval[2]);
205 /* end transform callbacks */
208 static bool transdata_check_local_center(TransInfo *t, short around)
210 return ((around == V3D_AROUND_LOCAL_ORIGINS) && (
211 (t->flag & (T_OBJECT | T_POSE)) ||
212 (t->obedit && ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) ||
213 (t->spacetype == SPACE_IPO) ||
214 (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))
218 bool transdata_check_local_islands(TransInfo *t, short around)
220 return ((around == V3D_AROUND_LOCAL_ORIGINS) && (
221 (t->obedit && ELEM(t->obedit->type, OB_MESH))));
224 /* ************************** SPACE DEPENDANT CODE **************************** */
226 void setTransformViewMatrices(TransInfo *t)
228 if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
229 RegionView3D *rv3d = t->ar->regiondata;
231 copy_m4_m4(t->viewmat, rv3d->viewmat);
232 copy_m4_m4(t->viewinv, rv3d->viewinv);
233 copy_m4_m4(t->persmat, rv3d->persmat);
234 copy_m4_m4(t->persinv, rv3d->persinv);
235 t->persp = rv3d->persp;
242 t->persp = RV3D_ORTHO;
245 calculateCenter2D(t);
248 void setTransformViewAspect(TransInfo *t, float r_aspect[3])
250 copy_v3_fl(r_aspect, 1.0f);
252 if (t->spacetype == SPACE_IMAGE) {
253 SpaceImage *sima = t->sa->spacedata.first;
255 if (t->options & CTX_MASK) {
256 ED_space_image_get_aspect(sima, &r_aspect[0], &r_aspect[1]);
258 else if (t->options & CTX_PAINT_CURVE) {
262 ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]);
265 else if (t->spacetype == SPACE_CLIP) {
266 SpaceClip *sclip = t->sa->spacedata.first;
268 if (t->options & CTX_MOVIECLIP) {
269 ED_space_clip_get_aspect_dimension_aware(sclip, &r_aspect[0], &r_aspect[1]);
272 ED_space_clip_get_aspect(sclip, &r_aspect[0], &r_aspect[1]);
275 else if (t->spacetype == SPACE_IPO) {
276 /* depemds on context of usage */
280 static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
282 float divx = BLI_rcti_size_x(&v2d->mask);
283 float divy = BLI_rcti_size_y(&v2d->mask);
285 r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
286 r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
290 static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
292 float divx = BLI_rcti_size_x(&v2d->mask);
293 float divy = BLI_rcti_size_y(&v2d->mask);
295 float mulx = BLI_rctf_size_x(&v2d->cur);
296 float muly = BLI_rctf_size_y(&v2d->cur);
298 /* difference with convertViewVec2D */
299 /* clamp w/h, mask only */
300 if (mulx / divx < muly / divy) {
310 r_vec[0] = mulx * dx / divx;
311 r_vec[1] = muly * dy / divy;
315 void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
317 if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
318 if (t->options & CTX_PAINT_CURVE) {
323 const float mval_f[2] = {(float)dx, (float)dy};
324 ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac);
327 else if (t->spacetype == SPACE_IMAGE) {
328 if (t->options & CTX_MASK) {
329 convertViewVec2D_mask(t->view, r_vec, dx, dy);
331 else if (t->options & CTX_PAINT_CURVE) {
336 convertViewVec2D(t->view, r_vec, dx, dy);
339 r_vec[0] *= t->aspect[0];
340 r_vec[1] *= t->aspect[1];
342 else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
343 convertViewVec2D(t->view, r_vec, dx, dy);
345 else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
346 convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
348 else if (t->spacetype == SPACE_CLIP) {
349 if (t->options & CTX_MASK) {
350 convertViewVec2D_mask(t->view, r_vec, dx, dy);
353 convertViewVec2D(t->view, r_vec, dx, dy);
356 r_vec[0] *= t->aspect[0];
357 r_vec[1] *= t->aspect[1];
360 printf("%s: called in an invalid context\n", __func__);
365 void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
367 if (t->spacetype == SPACE_VIEW3D) {
368 if (t->ar->regiontype == RGN_TYPE_WINDOW) {
369 if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
370 adr[0] = (int)2140000000.0f; /* this is what was done in 2.64, perhaps we can be smarter? */
371 adr[1] = (int)2140000000.0f;
375 else if (t->spacetype == SPACE_IMAGE) {
376 SpaceImage *sima = t->sa->spacedata.first;
378 if (t->options & CTX_MASK) {
381 v[0] = vec[0] / t->aspect[0];
382 v[1] = vec[1] / t->aspect[1];
384 BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
386 ED_image_point_pos__reverse(sima, t->ar, v, v);
391 else if (t->options & CTX_PAINT_CURVE) {
398 v[0] = vec[0] / t->aspect[0];
399 v[1] = vec[1] / t->aspect[1];
401 UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
404 else if (t->spacetype == SPACE_ACTION) {
407 SpaceAction *sact = t->sa->spacedata.first;
409 if (sact->flag & SACTION_DRAWTIME) {
410 //vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
412 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
417 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
423 else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
426 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
430 else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
433 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
437 else if (t->spacetype == SPACE_CLIP) {
438 SpaceClip *sc = t->sa->spacedata.first;
440 if (t->options & CTX_MASK) {
441 MovieClip *clip = ED_space_clip_get_clip(sc);
446 v[0] = vec[0] / t->aspect[0];
447 v[1] = vec[1] / t->aspect[1];
449 BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v);
451 ED_clip_point_stable_pos__reverse(sc, t->ar, v, v);
461 else if (t->options & CTX_MOVIECLIP) {
464 v[0] = vec[0] / t->aspect[0];
465 v[1] = vec[1] / t->aspect[1];
467 UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
473 else if (t->spacetype == SPACE_NODE) {
474 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &adr[0], &adr[1]);
477 void projectIntView(TransInfo *t, const float vec[3], int adr[2])
479 projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
482 void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
484 switch (t->spacetype) {
487 if (t->options & CTX_PAINT_CURVE) {
491 else if (t->ar->regiontype == RGN_TYPE_WINDOW) {
492 /* allow points behind the view [#33643] */
493 if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
494 /* XXX, 2.64 and prior did this, weak! */
495 adr[0] = t->ar->winx / 2.0f;
496 adr[1] = t->ar->winy / 2.0f;
505 projectIntView(t, vec, a);
512 void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
514 projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
517 void applyAspectRatio(TransInfo *t, float vec[2])
519 if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) && !(t->options & CTX_PAINT_CURVE)) {
520 SpaceImage *sima = t->sa->spacedata.first;
522 if ((sima->flag & SI_COORDFLOATS) == 0) {
524 ED_space_image_get_size(sima, &width, &height);
530 vec[0] /= t->aspect[0];
531 vec[1] /= t->aspect[1];
533 else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
534 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
535 vec[0] /= t->aspect[0];
536 vec[1] /= t->aspect[1];
541 void removeAspectRatio(TransInfo *t, float vec[2])
543 if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
544 SpaceImage *sima = t->sa->spacedata.first;
546 if ((sima->flag & SI_COORDFLOATS) == 0) {
548 ED_space_image_get_size(sima, &width, &height);
554 vec[0] *= t->aspect[0];
555 vec[1] *= t->aspect[1];
557 else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
558 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
559 vec[0] *= t->aspect[0];
560 vec[1] *= t->aspect[1];
565 static void viewRedrawForce(const bContext *C, TransInfo *t)
567 if (t->options & CTX_GPENCIL_STROKES) {
568 WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
570 else if (t->spacetype == SPACE_VIEW3D) {
571 if (t->options & CTX_PAINT_CURVE) {
572 wmWindow *window = CTX_wm_window(C);
573 WM_paint_cursor_tag_redraw(window, t->ar);
576 /* Do we need more refined tags? */
577 if (t->flag & T_POSE)
578 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
580 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
582 /* for realtime animation record - send notifiers recognised by animation editors */
583 // XXX: is this notifier a lame duck?
584 if ((t->animtimer) && IS_AUTOKEY_ON(t->scene))
585 WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
589 else if (t->spacetype == SPACE_ACTION) {
590 //SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
591 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
593 else if (t->spacetype == SPACE_IPO) {
594 //SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
595 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
597 else if (t->spacetype == SPACE_NLA) {
598 WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
600 else if (t->spacetype == SPACE_NODE) {
601 //ED_area_tag_redraw(t->sa);
602 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
604 else if (t->spacetype == SPACE_SEQ) {
605 WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
607 else if (t->spacetype == SPACE_IMAGE) {
608 if (t->options & CTX_MASK) {
609 Mask *mask = CTX_data_edit_mask(C);
611 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
613 else if (t->options & CTX_PAINT_CURVE) {
614 wmWindow *window = CTX_wm_window(C);
615 WM_paint_cursor_tag_redraw(window, t->ar);
618 // XXX how to deal with lock?
619 SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
620 if (sima->lock) WM_event_add_notifier(C, NC_GEOM | ND_DATA, t->obedit->data);
621 else ED_area_tag_redraw(t->sa);
624 else if (t->spacetype == SPACE_CLIP) {
625 SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
627 if (ED_space_clip_check_show_trackedit(sc)) {
628 MovieClip *clip = ED_space_clip_get_clip(sc);
630 /* objects could be parented to tracking data, so send this for viewport refresh */
631 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
633 WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
635 else if (ED_space_clip_check_show_maskedit(sc)) {
636 Mask *mask = CTX_data_edit_mask(C);
638 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
643 static void viewRedrawPost(bContext *C, TransInfo *t)
645 ED_area_headerprint(t->sa, NULL);
647 if (t->spacetype == SPACE_VIEW3D) {
648 /* if autokeying is enabled, send notifiers that keyframes were added */
649 if (IS_AUTOKEY_ON(t->scene))
650 WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
652 /* redraw UV editor */
653 if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) &&
654 (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT))
656 WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
659 /* XXX temp, first hack to get auto-render in compositor work (ton) */
660 WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
664 #if 0 // TRANSFORM_FIX_ME
665 if (t->spacetype == SPACE_VIEW3D) {
666 allqueue(REDRAWBUTSOBJECT, 0);
667 allqueue(REDRAWVIEW3D, 0);
669 else if (t->spacetype == SPACE_IMAGE) {
670 allqueue(REDRAWIMAGE, 0);
671 allqueue(REDRAWVIEW3D, 0);
673 else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) {
674 allqueue(REDRAWVIEW3D, 0);
675 allqueue(REDRAWACTION, 0);
676 allqueue(REDRAWNLA, 0);
677 allqueue(REDRAWIPO, 0);
678 allqueue(REDRAWTIME, 0);
679 allqueue(REDRAWBUTSOBJECT, 0);
682 scrarea_queue_headredraw(curarea);
686 /* ************************** TRANSFORMATIONS **************************** */
688 static void view_editmove(unsigned short UNUSED(event))
690 #if 0 // TRANSFORM_FIX_ME
692 /* Regular: Zoom in */
693 /* Shift: Scroll up */
694 /* Ctrl: Scroll right */
695 /* Alt-Shift: Rotate up */
696 /* Alt-Ctrl: Rotate right */
698 /* only work in 3D window for now
699 * In the end, will have to send to event to a 2D window handler instead
701 if (Trans.flag & T_2D_EDIT)
706 if (G.qual & LR_SHIFTKEY) {
707 if (G.qual & LR_ALTKEY) {
708 G.qual &= ~LR_SHIFTKEY;
710 G.qual |= LR_SHIFTKEY;
716 else if (G.qual & LR_CTRLKEY) {
717 if (G.qual & LR_ALTKEY) {
718 G.qual &= ~LR_CTRLKEY;
720 G.qual |= LR_CTRLKEY;
726 else if (U.uiflag & USER_WHEELZOOMDIR)
727 persptoetsen(PADMINUS);
729 persptoetsen(PADPLUSKEY);
734 if (G.qual & LR_SHIFTKEY) {
735 if (G.qual & LR_ALTKEY) {
736 G.qual &= ~LR_SHIFTKEY;
738 G.qual |= LR_SHIFTKEY;
744 else if (G.qual & LR_CTRLKEY) {
745 if (G.qual & LR_ALTKEY) {
746 G.qual &= ~LR_CTRLKEY;
748 G.qual |= LR_CTRLKEY;
754 else if (U.uiflag & USER_WHEELZOOMDIR)
755 persptoetsen(PADPLUSKEY);
757 persptoetsen(PADMINUS);
764 setTransformViewMatrices(&Trans);
768 /* ************************************************* */
770 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
772 TFM_MODAL_CANCEL = 1,
773 TFM_MODAL_CONFIRM = 2,
774 TFM_MODAL_TRANSLATE = 3,
775 TFM_MODAL_ROTATE = 4,
776 TFM_MODAL_RESIZE = 5,
777 TFM_MODAL_SNAP_INV_ON = 6,
778 TFM_MODAL_SNAP_INV_OFF = 7,
779 TFM_MODAL_SNAP_TOGGLE = 8,
780 TFM_MODAL_AXIS_X = 9,
781 TFM_MODAL_AXIS_Y = 10,
782 TFM_MODAL_AXIS_Z = 11,
783 TFM_MODAL_PLANE_X = 12,
784 TFM_MODAL_PLANE_Y = 13,
785 TFM_MODAL_PLANE_Z = 14,
786 TFM_MODAL_CONS_OFF = 15,
787 TFM_MODAL_ADD_SNAP = 16,
788 TFM_MODAL_REMOVE_SNAP = 17,
790 /* 18 and 19 used by numinput, defined in transform.h */
792 TFM_MODAL_PROPSIZE_UP = 20,
793 TFM_MODAL_PROPSIZE_DOWN = 21,
794 TFM_MODAL_AUTOIK_LEN_INC = 22,
795 TFM_MODAL_AUTOIK_LEN_DEC = 23,
797 TFM_MODAL_EDGESLIDE_UP = 24,
798 TFM_MODAL_EDGESLIDE_DOWN = 25,
800 /* for analog input, like trackpad */
801 TFM_MODAL_PROPSIZE = 26,
802 /* node editor insert offset (aka auto-offset) direction toggle */
803 TFM_MODAL_INSERTOFS_TOGGLE_DIR = 27,
806 /* called in transform_ops.c, on each regeneration of keymaps */
807 wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
809 static EnumPropertyItem modal_items[] = {
810 {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
811 {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
812 {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Translate", ""},
813 {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
814 {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
815 {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Invert Snap On", ""},
816 {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Invert Snap Off", ""},
817 {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
818 {TFM_MODAL_AXIS_X, "AXIS_X", 0, "Orientation X axis", ""},
819 {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Orientation Y axis", ""},
820 {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Orientation Z axis", ""},
821 {TFM_MODAL_PLANE_X, "PLANE_X", 0, "Orientation X plane", ""},
822 {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Orientation Y plane", ""},
823 {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Orientation Z plane", ""},
824 {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Remove Constraints", ""},
825 {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
826 {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
827 {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
828 {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
829 {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
830 {TFM_MODAL_PROPSIZE_DOWN, "PROPORTIONAL_SIZE_DOWN", 0, "Decrease Proportional Influence", ""},
831 {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
832 {TFM_MODAL_AUTOIK_LEN_DEC, "AUTOIK_CHAIN_LEN_DOWN", 0, "Decrease Max AutoIK Chain Length", ""},
833 {TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
834 {TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
835 {TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
836 {TFM_MODAL_INSERTOFS_TOGGLE_DIR, "INSERTOFS_TOGGLE_DIR", 0, "Toggle Direction for Node Auto-offset", ""},
837 {0, NULL, 0, NULL, NULL}
840 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
842 /* this function is called for each spacetype, only needs to add map once */
843 if (keymap && keymap->modal_items) return NULL;
845 keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
847 /* items for modal map */
848 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL);
849 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
850 WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
851 WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
853 WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE);
854 WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE);
855 WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, TFM_MODAL_RESIZE);
857 WM_modalkeymap_add_item(keymap, TABKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_SNAP_TOGGLE);
859 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
860 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
862 WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
863 WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
865 WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, TFM_MODAL_ADD_SNAP);
866 WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, KM_ALT, 0, TFM_MODAL_REMOVE_SNAP);
868 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
869 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
870 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP);
871 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN);
872 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
873 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
874 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP);
875 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN);
876 WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, TFM_MODAL_PROPSIZE);
878 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_UP);
879 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_DOWN);
881 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
882 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
883 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
884 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
886 /* node editor only */
887 WM_modalkeymap_add_item(keymap, TKEY, KM_PRESS, 0, 0, TFM_MODAL_INSERTOFS_TOGGLE_DIR);
892 static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode)
894 if (!(t->flag & T_NO_CONSTRAINT)) {
895 int constraint_axis, constraint_plane;
896 const bool edit_2d = (t->flag & T_2D_EDIT) != 0;
897 const char *msg1 = "", *msg2 = "", *msg3 = "";
903 msg1 = IFACE_("along X");
904 msg2 = IFACE_("along %s X");
905 msg3 = IFACE_("locking %s X");
907 constraint_axis = CON_AXIS0;
910 msg1 = IFACE_("along Y");
911 msg2 = IFACE_("along %s Y");
912 msg3 = IFACE_("locking %s Y");
914 constraint_axis = CON_AXIS1;
917 msg1 = IFACE_("along Z");
918 msg2 = IFACE_("along %s Z");
919 msg3 = IFACE_("locking %s Z");
921 constraint_axis = CON_AXIS2;
927 constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
929 if (edit_2d && (key_type != ZKEY)) {
934 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg1);
939 if (t->con.orientation != V3D_MANIP_GLOBAL) {
943 short orientation = (t->current_orientation != V3D_MANIP_GLOBAL ?
944 t->current_orientation : V3D_MANIP_LOCAL);
945 if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
946 setUserConstraint(t, orientation, constraint_axis, msg2);
947 else if (t->modifiers & MOD_CONSTRAINT_PLANE)
948 setUserConstraint(t, orientation, constraint_plane, msg3);
952 if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
953 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg2);
954 else if (t->modifiers & MOD_CONSTRAINT_PLANE)
955 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_plane, msg3);
958 t->redraw |= TREDRAW_HARD;
962 int transformEvent(TransInfo *t, const wmEvent *event)
964 char cmode = constraintModeToChar(t);
965 bool handled = false;
966 const int modifiers_prev = t->modifiers;
968 t->redraw |= handleMouseInput(t, &t->mouse, event);
970 /* Handle modal numinput events first, if already activated. */
971 if (((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
972 hasNumInput(&t->num) && handleNumInput(t->context, &(t->num), event))
974 t->redraw |= TREDRAW_HARD;
977 else if (event->type == MOUSEMOVE) {
978 if (t->modifiers & MOD_CONSTRAINT_SELECT)
979 t->con.mode |= CON_SELECT;
981 copy_v2_v2_int(t->mval, event->mval);
983 // t->redraw |= TREDRAW_SOFT; /* Use this for soft redraw. Might cause flicker in object mode */
984 t->redraw |= TREDRAW_HARD;
986 if (t->state == TRANS_STARTING) {
987 t->state = TRANS_RUNNING;
990 applyMouseInput(t, &t->mouse, t->mval, t->values);
992 // Snapping mouse move events
993 t->redraw |= handleSnapping(t, event);
996 /* handle modal keymap first */
997 else if (event->type == EVT_MODAL_MAP) {
998 switch (event->val) {
999 case TFM_MODAL_CANCEL:
1000 t->state = TRANS_CANCEL;
1003 case TFM_MODAL_CONFIRM:
1004 t->state = TRANS_CONFIRM;
1007 case TFM_MODAL_TRANSLATE:
1008 /* only switch when... */
1009 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1010 restoreTransObjects(t);
1012 resetTransRestrictions(t);
1014 initSnapping(t, NULL); // need to reinit after mode change
1015 t->redraw |= TREDRAW_HARD;
1016 WM_event_add_mousemove(t->context);
1019 else if (t->mode == TFM_SEQ_SLIDE) {
1020 t->flag ^= T_ALT_TRANSFORM;
1021 t->redraw |= TREDRAW_HARD;
1025 if (t->obedit && t->obedit->type == OB_MESH) {
1026 if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) {
1027 restoreTransObjects(t);
1029 resetTransRestrictions(t);
1031 /* first try edge slide */
1033 /* if that fails, do vertex slide */
1034 if (t->state == TRANS_CANCEL) {
1035 t->state = TRANS_STARTING;
1038 /* vert slide can fail on unconnected vertices (rare but possible) */
1039 if (t->state == TRANS_CANCEL) {
1040 t->mode = TFM_TRANSLATION;
1041 t->state = TRANS_STARTING;
1042 restoreTransObjects(t);
1043 resetTransRestrictions(t);
1046 initSnapping(t, NULL); // need to reinit after mode change
1047 t->redraw |= TREDRAW_HARD;
1049 WM_event_add_mousemove(t->context);
1052 else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
1053 if (t->mode == TFM_TRANSLATION) {
1054 restoreTransObjects(t);
1056 t->flag ^= T_ALT_TRANSFORM;
1057 t->redraw |= TREDRAW_HARD;
1063 case TFM_MODAL_ROTATE:
1064 /* only switch when... */
1065 if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
1066 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1067 restoreTransObjects(t);
1069 resetTransRestrictions(t);
1071 if (t->mode == TFM_ROTATION) {
1077 initSnapping(t, NULL); // need to reinit after mode change
1078 t->redraw |= TREDRAW_HARD;
1083 case TFM_MODAL_RESIZE:
1084 /* only switch when... */
1085 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1087 /* Scale isn't normally very useful after extrude along normals, see T39756 */
1088 if ((t->con.mode & CON_APPLY) && (t->con.orientation == V3D_MANIP_NORMAL)) {
1092 restoreTransObjects(t);
1094 resetTransRestrictions(t);
1096 initSnapping(t, NULL); // need to reinit after mode change
1097 t->redraw |= TREDRAW_HARD;
1100 else if (t->mode == TFM_SHRINKFATTEN) {
1101 t->flag ^= T_ALT_TRANSFORM;
1102 t->redraw |= TREDRAW_HARD;
1105 else if (t->mode == TFM_RESIZE) {
1106 if (t->options & CTX_MOVIECLIP) {
1107 restoreTransObjects(t);
1109 t->flag ^= T_ALT_TRANSFORM;
1110 t->redraw |= TREDRAW_HARD;
1116 case TFM_MODAL_SNAP_INV_ON:
1117 t->modifiers |= MOD_SNAP_INVERT;
1118 t->redraw |= TREDRAW_HARD;
1121 case TFM_MODAL_SNAP_INV_OFF:
1122 t->modifiers &= ~MOD_SNAP_INVERT;
1123 t->redraw |= TREDRAW_HARD;
1126 case TFM_MODAL_SNAP_TOGGLE:
1127 t->modifiers ^= MOD_SNAP;
1128 t->redraw |= TREDRAW_HARD;
1131 case TFM_MODAL_AXIS_X:
1132 if (!(t->flag & T_NO_CONSTRAINT)) {
1133 transform_event_xyz_constraint(t, XKEY, cmode);
1134 t->redraw |= TREDRAW_HARD;
1138 case TFM_MODAL_AXIS_Y:
1139 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1140 transform_event_xyz_constraint(t, YKEY, cmode);
1141 t->redraw |= TREDRAW_HARD;
1145 case TFM_MODAL_AXIS_Z:
1146 if ((t->flag & (T_NO_CONSTRAINT)) == 0) {
1147 transform_event_xyz_constraint(t, ZKEY, cmode);
1148 t->redraw |= TREDRAW_HARD;
1152 case TFM_MODAL_PLANE_X:
1153 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1158 setUserConstraint(t, t->current_orientation, (CON_AXIS1 | CON_AXIS2), IFACE_("locking %s X"));
1160 t->redraw |= TREDRAW_HARD;
1164 case TFM_MODAL_PLANE_Y:
1165 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1170 setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS2), IFACE_("locking %s Y"));
1172 t->redraw |= TREDRAW_HARD;
1176 case TFM_MODAL_PLANE_Z:
1177 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1182 setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS1), IFACE_("locking %s Z"));
1184 t->redraw |= TREDRAW_HARD;
1188 case TFM_MODAL_CONS_OFF:
1189 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1191 t->redraw |= TREDRAW_HARD;
1195 case TFM_MODAL_ADD_SNAP:
1197 t->redraw |= TREDRAW_HARD;
1200 case TFM_MODAL_REMOVE_SNAP:
1202 t->redraw |= TREDRAW_HARD;
1205 case TFM_MODAL_PROPSIZE:
1206 /* MOUSEPAN usage... */
1207 if (t->flag & T_PROP_EDIT) {
1208 float fac = 1.0f + 0.005f *(event->y - event->prevy);
1209 t->prop_size *= fac;
1210 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1211 t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->far), T_PROP_SIZE_MIN);
1214 t->prop_size = max_ff(min_ff(t->prop_size, T_PROP_SIZE_MAX), T_PROP_SIZE_MIN);
1216 calculatePropRatio(t);
1217 t->redraw |= TREDRAW_HARD;
1221 case TFM_MODAL_PROPSIZE_UP:
1222 if (t->flag & T_PROP_EDIT) {
1223 t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1224 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1225 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1228 t->prop_size = min_ff(t->prop_size, T_PROP_SIZE_MAX);
1230 calculatePropRatio(t);
1231 t->redraw |= TREDRAW_HARD;
1235 case TFM_MODAL_PROPSIZE_DOWN:
1236 if (t->flag & T_PROP_EDIT) {
1237 t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1238 t->prop_size = max_ff(t->prop_size, T_PROP_SIZE_MIN);
1239 calculatePropRatio(t);
1240 t->redraw |= TREDRAW_HARD;
1244 case TFM_MODAL_AUTOIK_LEN_INC:
1245 if (t->flag & T_AUTOIK) {
1246 transform_autoik_update(t, 1);
1247 t->redraw |= TREDRAW_HARD;
1251 case TFM_MODAL_AUTOIK_LEN_DEC:
1252 if (t->flag & T_AUTOIK) {
1253 transform_autoik_update(t, -1);
1254 t->redraw |= TREDRAW_HARD;
1258 case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
1259 if (t->spacetype == SPACE_NODE) {
1260 SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
1262 BLI_assert(t->sa->spacetype == t->spacetype);
1264 if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT) {
1265 snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_LEFT;
1267 else if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_LEFT) {
1268 snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_RIGHT;
1274 t->redraw |= TREDRAW_SOFT;
1277 /* Those two are only handled in transform's own handler, see T44634! */
1278 case TFM_MODAL_EDGESLIDE_UP:
1279 case TFM_MODAL_EDGESLIDE_DOWN:
1284 /* else do non-mapped events */
1285 else if (event->val == KM_PRESS) {
1286 switch (event->type) {
1288 t->state = TRANS_CANCEL;
1291 /* enforce redraw of transform when modifiers are used */
1294 t->modifiers |= MOD_CONSTRAINT_PLANE;
1295 t->redraw |= TREDRAW_HARD;
1300 t->state = TRANS_CONFIRM;
1305 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1306 /* exception for switching to dolly, or trackball, in camera view */
1307 if (t->flag & T_CAMERA) {
1308 if (t->mode == TFM_TRANSLATION)
1309 setLocalConstraint(t, (CON_AXIS2), IFACE_("along local Z"));
1310 else if (t->mode == TFM_ROTATION) {
1311 restoreTransObjects(t);
1316 t->modifiers |= MOD_CONSTRAINT_SELECT;
1317 if (t->con.mode & CON_APPLY) {
1322 initSelectConstraint(t, t->spacemtx);
1325 /* bit hackish... but it prevents mmb select to print the orientation from menu */
1327 strcpy(t->spacename, "global");
1329 initSelectConstraint(t, mati);
1331 postSelectConstraint(t);
1334 t->redraw |= TREDRAW_HARD;
1339 t->state = TRANS_CANCEL;
1344 t->state = TRANS_CONFIRM;
1348 /* only switch when... */
1349 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) {
1350 restoreTransObjects(t);
1352 resetTransRestrictions(t);
1354 initSnapping(t, NULL); // need to reinit after mode change
1355 t->redraw |= TREDRAW_HARD;
1360 /* only switch when... */
1361 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL)) {
1362 restoreTransObjects(t);
1364 resetTransRestrictions(t);
1366 initSnapping(t, NULL); // need to reinit after mode change
1367 t->redraw |= TREDRAW_HARD;
1372 /* only switch when... */
1373 if (!(t->options & CTX_TEXTURE)) {
1374 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION)) {
1375 restoreTransObjects(t);
1377 resetTransRestrictions(t);
1379 if (t->mode == TFM_ROTATION) {
1385 initSnapping(t, NULL); // need to reinit after mode change
1386 t->redraw |= TREDRAW_HARD;
1393 if (!(t->options & CTX_NO_PET)) {
1394 t->flag ^= T_PROP_CONNECTED;
1395 sort_trans_data_dist(t);
1396 calculatePropRatio(t);
1397 t->redraw = TREDRAW_HARD;
1402 if (!(t->flag & T_NO_CONSTRAINT)) {
1404 t->redraw |= TREDRAW_HARD;
1412 if (!(t->flag & T_NO_CONSTRAINT)) {
1413 transform_event_xyz_constraint(t, event->type, cmode);
1418 if (t->flag & T_PROP_EDIT && event->shift) {
1419 t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1420 calculatePropRatio(t);
1421 t->redraw |= TREDRAW_HARD;
1426 if (event->alt && t->flag & T_PROP_EDIT) {
1427 t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1428 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1429 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1430 calculatePropRatio(t);
1431 t->redraw = TREDRAW_HARD;
1436 case WHEELDOWNMOUSE:
1437 if (t->flag & T_AUTOIK) {
1438 transform_autoik_update(t, 1);
1441 view_editmove(event->type);
1443 t->redraw = TREDRAW_HARD;
1447 if (event->alt && t->flag & T_PROP_EDIT) {
1448 t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1449 calculatePropRatio(t);
1450 t->redraw = TREDRAW_HARD;
1456 if (t->flag & T_AUTOIK) {
1457 transform_autoik_update(t, -1);
1460 view_editmove(event->type);
1462 t->redraw = TREDRAW_HARD;
1467 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1468 t->flag |= T_ALT_TRANSFORM;
1469 t->redraw |= TREDRAW_HARD;
1477 /* Snapping key events */
1478 t->redraw |= handleSnapping(t, event);
1480 else if (event->val == KM_RELEASE) {
1481 switch (event->type) {
1484 t->modifiers &= ~MOD_CONSTRAINT_PLANE;
1485 t->redraw |= TREDRAW_HARD;
1490 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1491 t->modifiers &= ~MOD_CONSTRAINT_SELECT;
1492 postSelectConstraint(t);
1493 t->redraw |= TREDRAW_HARD;
1499 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1500 t->flag &= ~T_ALT_TRANSFORM;
1501 t->redraw |= TREDRAW_HARD;
1509 /* confirm transform if launch key is released after mouse move */
1510 if (t->flag & T_RELEASE_CONFIRM) {
1511 /* XXX Keyrepeat bug in Xorg messes this up, will test when fixed */
1512 if (event->type == t->launch_event && (t->launch_event == LEFTMOUSE || t->launch_event == RIGHTMOUSE)) {
1513 t->state = TRANS_CONFIRM;
1518 /* if we change snap options, get the unsnapped values back */
1519 if ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) !=
1520 (modifiers_prev & (MOD_SNAP | MOD_SNAP_INVERT)))
1522 applyMouseInput(t, &t->mouse, t->mval, t->values);
1525 /* Per transform event, if present */
1526 if (t->handleEvent &&
1528 /* Needed for vertex slide, see [#38756] */
1529 (event->type == MOUSEMOVE)))
1531 t->redraw |= t->handleEvent(t, event);
1534 /* Try to init modal numinput now, if possible. */
1535 if (!(handled || t->redraw) && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1536 handleNumInput(t->context, &(t->num), event))
1538 t->redraw |= TREDRAW_HARD;
1542 if (handled || t->redraw) {
1546 return OPERATOR_PASS_THROUGH;
1550 bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
1552 TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
1555 t->state = TRANS_RUNNING;
1557 /* avoid calculating PET */
1558 t->options = CTX_NO_PET;
1560 t->mode = TFM_DUMMY;
1562 initTransInfo(C, t, NULL, NULL);
1564 /* avoid doing connectivity lookups (when V3D_AROUND_LOCAL_ORIGINS is set) */
1565 t->around = V3D_AROUND_CENTER_BOUNDS;
1567 createTransData(C, t); // make TransData structs from selection
1569 t->around = centerMode; // override userdefined mode
1571 if (t->total == 0) {
1580 copy_v2_v2(cent2d, t->center2d);
1584 // Copy center from constraint center. Transform center can be local
1585 copy_v3_v3(cent3d, t->center_global);
1590 /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
1591 special_aftertrans_update(C, t);
1608 /* NOTE: this --^ is a bit hackish, but simplifies Gwn_VertFormat usage among functions
1609 * private to this file - merwin
1612 static void drawArrow(ArrowDirection d, short offset, short length, short size)
1614 immBegin(GWN_PRIM_LINES, 6);
1623 immVertex2f(POS_INDEX, offset, 0);
1624 immVertex2f(POS_INDEX, offset + length, 0);
1625 immVertex2f(POS_INDEX, offset + length, 0);
1626 immVertex2f(POS_INDEX, offset + length - size, -size);
1627 immVertex2f(POS_INDEX, offset + length, 0);
1628 immVertex2f(POS_INDEX, offset + length - size, size);
1637 immVertex2f(POS_INDEX, 0, offset);
1638 immVertex2f(POS_INDEX, 0, offset + length);
1639 immVertex2f(POS_INDEX, 0, offset + length);
1640 immVertex2f(POS_INDEX, -size, offset + length - size);
1641 immVertex2f(POS_INDEX, 0, offset + length);
1642 immVertex2f(POS_INDEX, size, offset + length - size);
1649 static void drawArrowHead(ArrowDirection d, short size)
1651 immBegin(GWN_PRIM_LINES, 4);
1658 immVertex2f(POS_INDEX, 0, 0);
1659 immVertex2f(POS_INDEX, -size, -size);
1660 immVertex2f(POS_INDEX, 0, 0);
1661 immVertex2f(POS_INDEX, -size, size);
1668 immVertex2f(POS_INDEX, 0, 0);
1669 immVertex2f(POS_INDEX, -size, -size);
1670 immVertex2f(POS_INDEX, 0, 0);
1671 immVertex2f(POS_INDEX, size, -size);
1678 static void drawArc(float size, float angle_start, float angle_end, int segments)
1680 float delta = (angle_end - angle_start) / segments;
1684 immBegin(GWN_PRIM_LINE_STRIP, segments + 1);
1686 for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
1687 immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size);
1689 immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size);
1694 static int helpline_poll(bContext *C)
1696 ARegion *ar = CTX_wm_region(C);
1698 if (ar && ar->regiontype == RGN_TYPE_WINDOW)
1703 static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
1705 TransInfo *t = (TransInfo *)customdata;
1707 if (t->helpline != HLP_NONE) {
1708 float vecrot[3], cent[2];
1709 float mval[3] = { x, y, 0.0f };
1711 copy_v3_v3(vecrot, t->center);
1712 if (t->flag & T_EDIT) {
1713 Object *ob = t->obedit;
1714 if (ob) mul_m4_v3(ob->obmat, vecrot);
1716 else if (t->flag & T_POSE) {
1717 Object *ob = t->poseobj;
1718 if (ob) mul_m4_v3(ob->obmat, vecrot);
1721 projectFloatViewEx(t, vecrot, cent, V3D_PROJ_TEST_CLIP_ZERO);
1725 /* Dashed lines first. */
1726 if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) {
1727 const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
1729 UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */
1730 BLI_assert(shdr_pos == POS_INDEX);
1734 immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
1736 float viewport_size[4];
1737 glGetFloatv(GL_VIEWPORT, viewport_size);
1738 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
1740 immUniform1i("num_colors", 0); /* "simple" mode */
1741 immUniformThemeColor(TH_VIEW_OVERLAY);
1742 immUniform1f("dash_width", 6.0f);
1743 immUniform1f("dash_factor", 0.5f);
1745 immBegin(GWN_PRIM_LINES, 2);
1746 immVertex2fv(POS_INDEX, cent);
1747 immVertex2f(POS_INDEX, (float)t->mval[0], (float)t->mval[1]);
1753 /* And now, solid lines. */
1754 unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
1755 UNUSED_VARS_NDEBUG(pos); /* silence warning */
1756 BLI_assert(pos == POS_INDEX);
1757 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1759 switch (t->helpline) {
1761 immUniformThemeColor(TH_VIEW_OVERLAY);
1763 gpuTranslate3fv(mval);
1764 gpuRotateAxis(-RAD2DEGF(atan2f(cent[0] - t->mval[0], cent[1] - t->mval[1])), 'Z');
1767 drawArrow(UP, 5, 10, 5);
1768 drawArrow(DOWN, 5, 10, 5);
1771 immUniformThemeColor(TH_VIEW_OVERLAY);
1772 gpuTranslate3fv(mval);
1775 drawArrow(RIGHT, 5, 10, 5);
1776 drawArrow(LEFT, 5, 10, 5);
1779 immUniformThemeColor(TH_VIEW_OVERLAY);
1781 gpuTranslate3fv(mval);
1784 drawArrow(UP, 5, 10, 5);
1785 drawArrow(DOWN, 5, 10, 5);
1789 float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1];
1790 float angle = atan2f(dy, dx);
1791 float dist = hypotf(dx, dy);
1792 float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
1793 float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
1795 immUniformThemeColor(TH_VIEW_OVERLAY);
1797 gpuTranslate3f(cent[0] - t->mval[0] + mval[0], cent[1] - t->mval[1] + mval[1], 0);
1800 drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
1801 drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
1805 gpuTranslate3f(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
1806 gpuRotateAxis(RAD2DEGF(angle - delta_angle), 'Z');
1808 drawArrowHead(DOWN, 5);
1812 gpuTranslate3f(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
1813 gpuRotateAxis(RAD2DEGF(angle + delta_angle), 'Z');
1815 drawArrowHead(UP, 5);
1820 unsigned char col[3], col2[3];
1821 UI_GetThemeColor3ubv(TH_GRID, col);
1823 gpuTranslate3fv(mval);
1827 UI_make_axis_color(col, col2, 'X');
1828 immUniformColor3ubv((GLubyte *)col2);
1830 drawArrow(RIGHT, 5, 10, 5);
1831 drawArrow(LEFT, 5, 10, 5);
1833 UI_make_axis_color(col, col2, 'Y');
1834 immUniformColor3ubv((GLubyte *)col2);
1836 drawArrow(UP, 5, 10, 5);
1837 drawArrow(DOWN, 5, 10, 5);
1847 static void drawTransformView(const struct bContext *C, ARegion *UNUSED(ar), void *arg)
1854 drawPropCircle(C, t);
1857 /* edge slide, vert slide */
1862 /* just draw a little warning message in the top-right corner of the viewport to warn that autokeying is enabled */
1863 static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar)
1866 const char *printable = IFACE_("Auto Keying On");
1867 float printable_size[2];
1870 ED_region_visible_rect(ar, &rect);
1872 const int font_id = BLF_default();
1873 BLF_width_and_height(font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
1875 xco = (rect.xmax - U.widget_unit) - (int)printable_size[0];
1876 yco = (rect.ymax - U.widget_unit);
1878 /* warning text (to clarify meaning of overlays)
1879 * - original color was red to match the icon, but that clashes badly with a less nasty border
1881 unsigned char color[3];
1882 UI_GetThemeColorShade3ubv(TH_TEXT_HI, -50, color);
1883 BLF_color3ubv(font_id, color);
1884 #ifdef WITH_INTERNATIONAL
1885 BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1887 BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1890 /* autokey recording icon... */
1891 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1894 xco -= U.widget_unit;
1895 yco -= (int)printable_size[1] / 2;
1897 UI_icon_draw(xco, yco, ICON_REC);
1899 glDisable(GL_BLEND);
1902 static void drawTransformPixel(const struct bContext *UNUSED(C), ARegion *ar, void *arg)
1905 Scene *scene = t->scene;
1906 SceneLayer *sl = t->scene_layer;
1907 Object *ob = OBACT_NEW;
1909 /* draw autokeyframing hint in the corner
1910 * - only draw if enabled (advanced users may be distracted/annoyed),
1911 * for objects that will be autokeyframed (no point ohterwise),
1912 * AND only for the active region (as showing all is too overwhelming)
1914 if ((U.autokey_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
1916 if (t->flag & (T_OBJECT | T_POSE)) {
1917 if (ob && autokeyframe_cfra_can_key(scene, &ob->id)) {
1918 drawAutoKeyWarning(t, ar);
1926 * \see #initTransform which reads values from the operator.
1928 void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
1930 ToolSettings *ts = CTX_data_tool_settings(C);
1931 int constraint_axis[3] = {0, 0, 0};
1932 int proportional = 0;
1935 // Save back mode in case we're in the generic operator
1936 if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
1937 RNA_property_enum_set(op->ptr, prop, t->mode);
1940 if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
1943 copy_v4_v4(values, (t->flag & T_AUTOVALUES) ? t->auto_values : t->values);
1945 if (RNA_property_array_check(prop)) {
1946 RNA_property_float_set_array(op->ptr, prop, values);
1949 RNA_property_float_set(op->ptr, prop, values[0]);
1953 /* convert flag to enum */
1954 switch (t->flag & T_PROP_EDIT_ALL) {
1956 proportional = PROP_EDIT_ON;
1958 case (T_PROP_EDIT | T_PROP_CONNECTED):
1959 proportional = PROP_EDIT_CONNECTED;
1961 case (T_PROP_EDIT | T_PROP_PROJECTED):
1962 proportional = PROP_EDIT_PROJECTED;
1965 proportional = PROP_EDIT_OFF;
1969 // If modal, save settings back in scene if not set as operator argument
1970 if (t->flag & T_MODAL) {
1971 /* save settings if not set in operator */
1973 /* skip saving proportional edit if it was not actually used */
1974 if (!(t->options & CTX_NO_PET)) {
1975 if ((prop = RNA_struct_find_property(op->ptr, "proportional")) &&
1976 !RNA_property_is_set(op->ptr, prop))
1978 if (t->spacetype == SPACE_IPO)
1979 ts->proportional_fcurve = proportional;
1980 else if (t->spacetype == SPACE_ACTION)
1981 ts->proportional_action = proportional;
1983 ts->proportional = proportional;
1984 else if (t->options & CTX_MASK)
1985 ts->proportional_mask = (proportional != PROP_EDIT_OFF);
1987 ts->proportional_objects = (proportional != PROP_EDIT_OFF);
1990 if ((prop = RNA_struct_find_property(op->ptr, "proportional_size")) &&
1991 !RNA_property_is_set(op->ptr, prop))
1993 ts->proportional_size = t->prop_size;
1996 if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
1997 !RNA_property_is_set(op->ptr, prop))
1999 ts->prop_mode = t->prop_mode;
2003 /* do we check for parameter? */
2004 if (t->modifiers & MOD_SNAP) {
2005 ts->snap_flag |= SCE_SNAP;
2008 ts->snap_flag &= ~SCE_SNAP;
2011 if (t->spacetype == SPACE_VIEW3D) {
2012 if ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) &&
2013 !RNA_property_is_set(op->ptr, prop))
2015 View3D *v3d = t->view;
2017 v3d->twmode = t->current_orientation;
2019 BLI_assert(((v3d->custom_orientation_index == -1) && (t->custom_orientation == NULL)) ||
2020 (BKE_workspace_transform_orientation_get_index(
2021 CTX_wm_workspace(C), t->custom_orientation) == v3d->custom_orientation_index));
2026 if ((prop = RNA_struct_find_property(op->ptr, "proportional"))) {
2027 RNA_property_enum_set(op->ptr, prop, proportional);
2028 RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
2029 RNA_float_set(op->ptr, "proportional_size", t->prop_size);
2032 if ((prop = RNA_struct_find_property(op->ptr, "axis"))) {
2033 RNA_property_float_set_array(op->ptr, prop, t->axis);
2036 if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
2037 RNA_property_boolean_set(op->ptr, prop, (t->flag & T_MIRROR) != 0);
2040 if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
2041 /* constraint orientation can be global, even if user selects something else
2042 * so use the orientation in the constraint if set */
2043 short orientation = (t->con.mode & CON_APPLY) ? t->con.orientation : t->current_orientation;
2045 if (orientation == V3D_MANIP_CUSTOM) {
2046 WorkSpace *workspace = CTX_wm_workspace(C);
2047 const int custom_orientation_index = BKE_workspace_transform_orientation_get_index(
2048 workspace, t->custom_orientation);
2050 /* Maybe we need a t->con.custom_orientation? Seems like it would always match t->custom_orientation. */
2051 orientation = V3D_MANIP_CUSTOM + custom_orientation_index;
2052 BLI_assert(orientation >= V3D_MANIP_CUSTOM);
2054 RNA_enum_set(op->ptr, "constraint_orientation", orientation);
2056 if (t->con.mode & CON_APPLY) {
2057 if (t->con.mode & CON_AXIS0) {
2058 constraint_axis[0] = 1;
2060 if (t->con.mode & CON_AXIS1) {
2061 constraint_axis[1] = 1;
2063 if (t->con.mode & CON_AXIS2) {
2064 constraint_axis[2] = 1;
2068 RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
2072 const char *prop_id = NULL;
2073 if (t->mode == TFM_SHRINKFATTEN) {
2074 prop_id = "use_even_offset";
2077 if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) {
2079 RNA_property_boolean_set(op->ptr, prop, (t->flag & T_ALT_TRANSFORM) != 0);
2083 if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
2084 RNA_property_boolean_set(op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
2089 * \note caller needs to free 't' on a 0 return
2090 * \warning \a event might be NULL (when tweaking from redo panel)
2091 * \see #saveTransform which writes these values back.
2093 bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
2100 /* added initialize, for external calls to set stuff in TransInfo, like undo string */
2102 t->state = TRANS_STARTING;
2104 if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) && RNA_property_is_set(op->ptr, prop)) {
2105 if (RNA_property_boolean_get(op->ptr, prop)) {
2106 options |= CTX_TEXTURE;
2110 if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) && RNA_property_is_set(op->ptr, prop)) {
2111 if (RNA_property_boolean_get(op->ptr, prop)) {
2112 options |= CTX_GPENCIL_STROKES;
2116 t->options = options;
2120 t->launch_event = event ? event->type : -1;
2122 if (t->launch_event == EVT_TWEAK_R) {
2123 t->launch_event = RIGHTMOUSE;
2125 else if (t->launch_event == EVT_TWEAK_L) {
2126 t->launch_event = LEFTMOUSE;
2129 // XXX Remove this when wm_operator_call_internal doesn't use window->eventstate (which can have type = 0)
2130 // For manipulator only, so assume LEFTMOUSE
2131 if (t->launch_event == 0) {
2132 t->launch_event = LEFTMOUSE;
2135 unit_m3(t->spacemtx);
2137 initTransInfo(C, t, op, event);
2138 initTransformOrientation(C, t);
2140 if (t->spacetype == SPACE_VIEW3D) {
2141 t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
2142 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2143 t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2144 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2146 else if (t->spacetype == SPACE_IMAGE) {
2147 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2148 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2149 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2151 else if (t->spacetype == SPACE_CLIP) {
2152 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2153 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2155 else if (t->spacetype == SPACE_NODE) {
2156 /*t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);*/
2157 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2158 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2160 else if (t->spacetype == SPACE_IPO) {
2161 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2162 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2163 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2165 else if (t->spacetype == SPACE_ACTION) {
2166 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2167 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2168 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2171 createTransData(C, t); // make TransData structs from selection
2173 if (t->total == 0) {
2179 /* keymap for shortcut header prints */
2180 t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
2182 /* Stupid code to have Ctrl-Click on manipulator work ok
2184 * do this only for translation/rotation/resize due to only this
2185 * moded are available from manipulator and doing such check could
2186 * lead to keymap conflicts for other modes (see #31584)
2188 if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
2191 for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) {
2192 if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
2193 if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) && event->ctrl) ||
2194 (ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
2195 (ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) && event->alt) ||
2196 ((kmi->type == OSKEY) && event->oskey) )
2198 t->modifiers |= MOD_SNAP_INVERT;
2206 initSnapping(t, op); // Initialize snapping data AFTER mode flags
2208 initSnapSpatial(t, t->snap_spatial);
2210 /* EVIL! posemode code can switch translation to rotate when 1 bone is selected. will be removed (ton) */
2211 /* EVIL2: we gave as argument also texture space context bit... was cleared */
2212 /* EVIL3: extend mode for animation editors also switches modes... but is best way to avoid duplicate code */
2215 calculatePropRatio(t);
2219 /* Initialize accurate transform to settings requested by keymap. */
2220 bool use_accurate = false;
2221 if ((prop = RNA_struct_find_property(op->ptr, "use_accurate")) && RNA_property_is_set(op->ptr, prop)) {
2222 if (RNA_property_boolean_get(op->ptr, prop)) {
2223 use_accurate = true;
2226 initMouseInput(t, &t->mouse, t->center2d, event->mval, use_accurate);
2230 case TFM_TRANSLATION:
2239 case TFM_SKIN_RESIZE:
2251 case TFM_SHRINKFATTEN:
2252 initShrinkFatten(t);
2257 case TFM_CURVE_SHRINKFATTEN:
2258 initCurveShrinkFatten(t);
2260 case TFM_MASK_SHRINKFATTEN:
2261 initMaskShrinkFatten(t);
2263 case TFM_GPENCIL_SHRINKFATTEN:
2264 initGPShrinkFatten(t);
2276 { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
2277 bArmature *arm = t->poseobj->data;
2278 if (arm->drawtype == ARM_ENVELOPE) {
2279 initBoneEnvelope(t);
2280 t->mode = TFM_BONE_ENVELOPE_DIST;
2287 case TFM_BONE_ENVELOPE:
2288 initBoneEnvelope(t);
2290 case TFM_BONE_ENVELOPE_DIST:
2291 initBoneEnvelope(t);
2292 t->mode = TFM_BONE_ENVELOPE_DIST;
2294 case TFM_EDGE_SLIDE:
2295 case TFM_VERT_SLIDE:
2297 const bool use_even = (op ? RNA_boolean_get(op->ptr, "use_even") : false);
2298 const bool flipped = (op ? RNA_boolean_get(op->ptr, "flipped") : false);
2299 const bool use_clamp = (op ? RNA_boolean_get(op->ptr, "use_clamp") : true);
2300 if (mode == TFM_EDGE_SLIDE) {
2301 const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true);
2302 initEdgeSlide_ex(t, use_double_side, use_even, flipped, use_clamp);
2305 initVertSlide_ex(t, use_even, flipped, use_clamp);
2312 case TFM_TIME_TRANSLATE:
2313 initTimeTranslate(t);
2315 case TFM_TIME_SLIDE:
2318 case TFM_TIME_SCALE:
2321 case TFM_TIME_DUPLICATE:
2322 /* same as TFM_TIME_EXTEND, but we need the mode info for later
2323 * so that duplicate-culling will work properly
2325 if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2328 initTimeTranslate(t);
2331 case TFM_TIME_EXTEND:
2332 /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
2333 * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
2334 * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
2335 * depending on which editor this was called from
2337 if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2340 initTimeTranslate(t);
2359 if (t->state == TRANS_CANCEL) {
2364 /* Transformation axis from operator */
2365 if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) {
2366 RNA_property_float_get_array(op->ptr, prop, t->axis);
2367 normalize_v3(t->axis);
2368 copy_v3_v3(t->axis_orig, t->axis);
2371 /* Constraint init from operator */
2372 if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) {
2373 int constraint_axis[3];
2375 RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
2377 if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
2378 t->con.mode |= CON_APPLY;
2380 if (constraint_axis[0]) {
2381 t->con.mode |= CON_AXIS0;
2383 if (constraint_axis[1]) {
2384 t->con.mode |= CON_AXIS1;
2386 if (constraint_axis[2]) {
2387 t->con.mode |= CON_AXIS2;
2390 setUserConstraint(t, t->current_orientation, t->con.mode, "%s");
2394 /* overwrite initial values if operator supplied a non-null vector
2396 * keep last so we can apply the constraints space.
2398 if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
2399 float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory */
2401 if (RNA_property_array_check(prop)) {
2402 RNA_float_get_array(op->ptr, "value", values);
2405 values[0] = RNA_float_get(op->ptr, "value");
2408 copy_v4_v4(t->values, values);
2409 copy_v4_v4(t->auto_values, values);
2410 t->flag |= T_AUTOVALUES;
2418 void transformApply(bContext *C, TransInfo *t)
2422 if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
2423 selectConstraint(t);
2425 t->transform(t, t->mval); // calls recalcData()
2426 viewRedrawForce(C, t);
2428 t->redraw = TREDRAW_NOTHING;
2430 else if (t->redraw & TREDRAW_SOFT) {
2431 viewRedrawForce(C, t);
2434 /* If auto confirm is on, break after one pass */
2435 if (t->options & CTX_AUTOCONFIRM) {
2436 t->state = TRANS_CONFIRM;
2442 static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
2446 if (t->redraw & TREDRAW_SOFT) {
2447 t->redraw |= TREDRAW_HARD;
2448 transformApply((bContext *)C, t);
2452 int transformEnd(bContext *C, TransInfo *t)
2454 int exit_code = OPERATOR_RUNNING_MODAL;
2458 if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
2459 /* handle restoring objects */
2460 if (t->state == TRANS_CANCEL) {
2461 /* exception, edge slide transformed UVs too */
2462 if (t->mode == TFM_EDGE_SLIDE) {
2463 doEdgeSlide(t, 0.0f);
2465 else if (t->mode == TFM_VERT_SLIDE) {
2466 doVertSlide(t, 0.0f);
2469 exit_code = OPERATOR_CANCELLED;
2470 restoreTransObjects(t); // calls recalcData()
2473 exit_code = OPERATOR_FINISHED;
2476 /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
2477 special_aftertrans_update(C, t);
2482 /* send events out for redraws */
2483 viewRedrawPost(C, t);
2485 viewRedrawForce(C, t);
2493 /* ************************** TRANSFORM LOCKS **************************** */
2495 static void protectedTransBits(short protectflag, float vec[3])
2497 if (protectflag & OB_LOCK_LOCX)
2499 if (protectflag & OB_LOCK_LOCY)
2501 if (protectflag & OB_LOCK_LOCZ)
2505 static void protectedSizeBits(short protectflag, float size[3])
2507 if (protectflag & OB_LOCK_SCALEX)
2509 if (protectflag & OB_LOCK_SCALEY)
2511 if (protectflag & OB_LOCK_SCALEZ)
2515 static void protectedRotateBits(short protectflag, float eul[3], const float oldeul[3])
2517 if (protectflag & OB_LOCK_ROTX)
2519 if (protectflag & OB_LOCK_ROTY)
2521 if (protectflag & OB_LOCK_ROTZ)
2526 /* this function only does the delta rotation */
2527 /* axis-angle is usually internally stored as quats... */
2528 static void protectedAxisAngleBits(short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle)
2530 /* check that protection flags are set */
2531 if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2534 if (protectflag & OB_LOCK_ROT4D) {
2535 /* axis-angle getting limited as 4D entities that they are... */
2536 if (protectflag & OB_LOCK_ROTW)
2538 if (protectflag & OB_LOCK_ROTX)
2539 axis[0] = oldAxis[0];
2540 if (protectflag & OB_LOCK_ROTY)
2541 axis[1] = oldAxis[1];
2542 if (protectflag & OB_LOCK_ROTZ)
2543 axis[2] = oldAxis[2];
2546 /* axis-angle get limited with euler... */
2547 float eul[3], oldeul[3];
2549 axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle);
2550 axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle);
2552 if (protectflag & OB_LOCK_ROTX)
2554 if (protectflag & OB_LOCK_ROTY)
2556 if (protectflag & OB_LOCK_ROTZ)
2559 eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT);
2561 /* when converting to axis-angle, we need a special exception for the case when there is no axis */
2562 if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) {
2563 /* for now, rotate around y-axis then (so that it simply becomes the roll) */
2569 /* this function only does the delta rotation */
2570 static void protectedQuaternionBits(short protectflag, float quat[4], const float oldquat[4])
2572 /* check that protection flags are set */
2573 if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2576 if (protectflag & OB_LOCK_ROT4D) {
2577 /* quaternions getting limited as 4D entities that they are... */
2578 if (protectflag & OB_LOCK_ROTW)
2579 quat[0] = oldquat[0];
2580 if (protectflag & OB_LOCK_ROTX)
2581 quat[1] = oldquat[1];
2582 if (protectflag & OB_LOCK_ROTY)
2583 quat[2] = oldquat[2];
2584 if (protectflag & OB_LOCK_ROTZ)
2585 quat[3] = oldquat[3];
2588 /* quaternions get limited with euler... (compatibility mode) */
2589 float eul[3], oldeul[3], nquat[4], noldquat[4];
2592 qlen = normalize_qt_qt(nquat, quat);
2593 normalize_qt_qt(noldquat, oldquat);
2595 quat_to_eul(eul, nquat);
2596 quat_to_eul(oldeul, noldquat);
2598 if (protectflag & OB_LOCK_ROTX)
2600 if (protectflag & OB_LOCK_ROTY)
2602 if (protectflag & OB_LOCK_ROTZ)
2605 eul_to_quat(quat, eul);
2607 /* restore original quat size */
2608 mul_qt_fl(quat, qlen);
2610 /* quaternions flip w sign to accumulate rotations correctly */
2611 if ((nquat[0] < 0.0f && quat[0] > 0.0f) ||
2612 (nquat[0] > 0.0f && quat[0] < 0.0f))
2614 mul_qt_fl(quat, -1.0f);
2619 /* ******************* TRANSFORM LIMITS ********************** */
2621 static void constraintTransLim(TransInfo *t, TransData *td)
2624 const bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_LOCLIMIT);
2625 const bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_DISTLIMIT);
2627 bConstraintOb cob = {NULL};
2629 float ctime = (float)(t->scene->r.cfra);
2631 /* Make a temporary bConstraintOb for using these limit constraints
2632 * - they only care that cob->matrix is correctly set ;-)
2633 * - current space should be local
2635 unit_m4(cob.matrix);
2636 copy_v3_v3(cob.matrix[3], td->loc);
2638 /* Evaluate valid constraints */
2639 for (con = td->con; con; con = con->next) {
2640 const bConstraintTypeInfo *cti = NULL;
2641 ListBase targets = {NULL, NULL};
2643 /* only consider constraint if enabled */
2644 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2645 if (con->enforce == 0.0f) continue;
2647 /* only use it if it's tagged for this purpose (and the right type) */
2648 if (con->type == CONSTRAINT_TYPE_LOCLIMIT) {
2649 bLocLimitConstraint *data = con->data;
2651 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2655 else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) {
2656 bDistLimitConstraint *data = con->data;
2658 if ((data->flag & LIMITDIST_TRANSFORM) == 0)
2664 /* do space conversions */
2665 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2666 /* just multiply by td->mtx (this should be ok) */
2667 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2669 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2670 /* skip... incompatable spacetype */
2674 /* get constraint targets if needed */
2675 BKE_constraint_targets_for_solving_get(con, &cob, &targets, ctime);
2678 cti->evaluate_constraint(con, &cob, &targets);
2680 /* convert spaces again */
2681 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2682 /* just multiply by td->smtx (this should be ok) */
2683 mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2686 /* free targets list */
2687 BLI_freelistN(&targets);
2691 /* copy results from cob->matrix */
2692 copy_v3_v3(td->loc, cob.matrix[3]);
2696 static void constraintob_from_transdata(bConstraintOb *cob, TransData *td)
2698 /* Make a temporary bConstraintOb for use by limit constraints
2699 * - they only care that cob->matrix is correctly set ;-)
2700 * - current space should be local
2702 memset(cob, 0, sizeof(bConstraintOb));
2704 if (td->ext->rotOrder == ROT_MODE_QUAT) {
2706 /* objects and bones do normalization first too, otherwise
2707 * we don't necessarily end up with a rotation matrix, and
2708 * then conversion back to quat gives a different result */
2710 normalize_qt_qt(quat, td->ext->quat);
2711 quat_to_mat4(cob->matrix, quat);
2713 else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2715 axis_angle_to_mat4(cob->matrix, td->ext->rotAxis, *td->ext->rotAngle);
2719 eulO_to_mat4(cob->matrix, td->ext->rot, td->ext->rotOrder);
2724 static void constraintRotLim(TransInfo *UNUSED(t), TransData *td)
2727 const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_ROTLIMIT);
2730 bool do_limit = false;
2732 /* Evaluate valid constraints */
2733 for (con = td->con; con; con = con->next) {
2734 /* only consider constraint if enabled */
2735 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2736 if (con->enforce == 0.0f) continue;
2738 /* we're only interested in Limit-Rotation constraints */
2739 if (con->type == CONSTRAINT_TYPE_ROTLIMIT) {
2740 bRotLimitConstraint *data = con->data;
2742 /* only use it if it's tagged for this purpose */
2743 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2746 /* skip incompatable spacetypes */
2747 if (!ELEM(con->ownspace, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL))
2750 /* only do conversion if necessary, to preserve quats and eulers */
2751 if (do_limit == false) {
2752 constraintob_from_transdata(&cob, td);
2756 /* do space conversions */
2757 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2758 /* just multiply by td->mtx (this should be ok) */
2759 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2763 cti->evaluate_constraint(con, &cob, NULL);
2765 /* convert spaces again */
2766 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2767 /* just multiply by td->smtx (this should be ok) */
2768 mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2774 /* copy results from cob->matrix */
2775 if (td->ext->rotOrder == ROT_MODE_QUAT) {
2777 mat4_to_quat(td->ext->quat, cob.matrix);
2779 else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2781 mat4_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, cob.matrix);
2785 mat4_to_eulO(td->ext->rot, td->ext->rotOrder, cob.matrix);
2791 static void constraintSizeLim(TransInfo *t, TransData *td)
2793 if (td->con && td->ext) {
2794 const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_SIZELIMIT);
2795 bConstraintOb cob = {NULL};
2797 float size_sign[3], size_abs[3];
2800 /* Make a temporary bConstraintOb for using these limit constraints
2801 * - they only care that cob->matrix is correctly set ;-)
2802 * - current space should be local
2804 if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2805 /* scale val and reset size */
2806 return; // TODO: fix this case
2809 /* Reset val if SINGLESIZE but using a constraint */
2810 if (td->flag & TD_SINGLESIZE)
2813 /* separate out sign to apply back later */
2814 for (i = 0; i < 3; i++) {
2815 size_sign[i] = signf(td->ext->size[i]);
2816 size_abs[i] = fabsf(td->ext->size[i]);
2819 size_to_mat4(cob.matrix, size_abs);
2822 /* Evaluate valid constraints */
2823 for (con = td->con; con; con = con->next) {
2824 /* only consider constraint if enabled */
2825 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2826 if (con->enforce == 0.0f) continue;
2828 /* we're only interested in Limit-Scale constraints */
2829 if (con->type == CONSTRAINT_TYPE_SIZELIMIT) {
2830 bSizeLimitConstraint *data = con->data;
2832 /* only use it if it's tagged for this purpose */
2833 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2836 /* do space conversions */
2837 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2838 /* just multiply by td->mtx (this should be ok) */
2839 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2841 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2842 /* skip... incompatible spacetype */
2847 cti->evaluate_constraint(con, &cob, NULL);