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 */
47 #include "BLI_alloca.h"
48 #include "BLI_utildefines.h"
51 #include "BLI_listbase.h"
52 #include "BLI_string.h"
53 #include "BLI_ghash.h"
54 #include "BLI_utildefines_stack.h"
55 #include "BLI_memarena.h"
58 #include "BKE_editmesh.h"
59 #include "BKE_editmesh_bvh.h"
60 #include "BKE_context.h"
61 #include "BKE_constraint.h"
62 #include "BKE_particle.h"
65 #include "BKE_report.h"
68 #include "BIF_glutil.h"
71 #include "ED_keyframing.h"
72 #include "ED_screen.h"
73 #include "ED_space_api.h"
74 #include "ED_markers.h"
75 #include "ED_view3d.h"
83 #include "UI_view2d.h"
84 #include "UI_interface.h"
85 #include "UI_interface_icons.h"
86 #include "UI_resources.h"
88 #include "RNA_access.h"
91 #include "BLT_translation.h"
93 #include "transform.h"
95 /* Disabling, since when you type you know what you are doing, and being able to set it to zero is handy. */
96 // #define USE_NUM_NO_ZERO
98 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
99 static void doEdgeSlide(TransInfo *t, float perc);
100 static void doVertSlide(TransInfo *t, float perc);
102 static void drawEdgeSlide(TransInfo *t);
103 static void drawVertSlide(TransInfo *t);
104 static void postInputRotation(TransInfo *t, float values[3]);
106 static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around);
107 static void initSnapSpatial(TransInfo *t, float r_snap[3]);
110 /* Transform Callbacks */
111 static void initBend(TransInfo *t);
112 static eRedrawFlag handleEventBend(TransInfo *t, const struct wmEvent *event);
113 static void Bend(TransInfo *t, const int mval[2]);
115 static void initShear(TransInfo *t);
116 static eRedrawFlag handleEventShear(TransInfo *t, const struct wmEvent *event);
117 static void applyShear(TransInfo *t, const int mval[2]);
119 static void initResize(TransInfo *t);
120 static void applyResize(TransInfo *t, const int mval[2]);
122 static void initSkinResize(TransInfo *t);
123 static void applySkinResize(TransInfo *t, const int mval[2]);
125 static void initTranslation(TransInfo *t);
126 static void applyTranslation(TransInfo *t, const int mval[2]);
128 static void initToSphere(TransInfo *t);
129 static void applyToSphere(TransInfo *t, const int mval[2]);
131 static void initRotation(TransInfo *t);
132 static void applyRotation(TransInfo *t, const int mval[2]);
134 static void initShrinkFatten(TransInfo *t);
135 static void applyShrinkFatten(TransInfo *t, const int mval[2]);
137 static void initTilt(TransInfo *t);
138 static void applyTilt(TransInfo *t, const int mval[2]);
140 static void initCurveShrinkFatten(TransInfo *t);
141 static void applyCurveShrinkFatten(TransInfo *t, const int mval[2]);
143 static void initMaskShrinkFatten(TransInfo *t);
144 static void applyMaskShrinkFatten(TransInfo *t, const int mval[2]);
146 static void initGPShrinkFatten(TransInfo *t);
147 static void applyGPShrinkFatten(TransInfo *t, const int mval[2]);
149 static void initTrackball(TransInfo *t);
150 static void applyTrackball(TransInfo *t, const int mval[2]);
152 static void initPushPull(TransInfo *t);
153 static void applyPushPull(TransInfo *t, const int mval[2]);
155 static void initBevelWeight(TransInfo *t);
156 static void applyBevelWeight(TransInfo *t, const int mval[2]);
158 static void initCrease(TransInfo *t);
159 static void applyCrease(TransInfo *t, const int mval[2]);
161 static void initBoneSize(TransInfo *t);
162 static void applyBoneSize(TransInfo *t, const int mval[2]);
164 static void initBoneEnvelope(TransInfo *t);
165 static void applyBoneEnvelope(TransInfo *t, const int mval[2]);
167 static void initBoneRoll(TransInfo *t);
168 static void applyBoneRoll(TransInfo *t, const int mval[2]);
170 static void initEdgeSlide_ex(TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp);
171 static void initEdgeSlide(TransInfo *t);
172 static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
173 static void applyEdgeSlide(TransInfo *t, const int mval[2]);
175 static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp);
176 static void initVertSlide(TransInfo *t);
177 static eRedrawFlag handleEventVertSlide(TransInfo *t, const struct wmEvent *event);
178 static void applyVertSlide(TransInfo *t, const int mval[2]);
180 static void initTimeTranslate(TransInfo *t);
181 static void applyTimeTranslate(TransInfo *t, const int mval[2]);
183 static void initTimeSlide(TransInfo *t);
184 static void applyTimeSlide(TransInfo *t, const int mval[2]);
186 static void initTimeScale(TransInfo *t);
187 static void applyTimeScale(TransInfo *t, const int mval[2]);
189 static void initBakeTime(TransInfo *t);
190 static void applyBakeTime(TransInfo *t, const int mval[2]);
192 static void initMirror(TransInfo *t);
193 static void applyMirror(TransInfo *t, const int mval[2]);
195 static void initAlign(TransInfo *t);
196 static void applyAlign(TransInfo *t, const int mval[2]);
198 static void initSeqSlide(TransInfo *t);
199 static void applySeqSlide(TransInfo *t, const int mval[2]);
200 /* end transform callbacks */
203 static bool transdata_check_local_center(TransInfo *t, short around)
205 return ((around == V3D_AROUND_LOCAL_ORIGINS) && (
206 (t->flag & (T_OBJECT | T_POSE)) ||
207 (t->obedit && ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) ||
208 (t->spacetype == SPACE_IPO) ||
209 (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))
213 bool transdata_check_local_islands(TransInfo *t, short around)
215 return ((around == V3D_AROUND_LOCAL_ORIGINS) && (
216 (t->obedit && ELEM(t->obedit->type, OB_MESH))));
219 /* ************************** SPACE DEPENDANT CODE **************************** */
221 void setTransformViewMatrices(TransInfo *t)
223 if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
224 RegionView3D *rv3d = t->ar->regiondata;
226 copy_m4_m4(t->viewmat, rv3d->viewmat);
227 copy_m4_m4(t->viewinv, rv3d->viewinv);
228 copy_m4_m4(t->persmat, rv3d->persmat);
229 copy_m4_m4(t->persinv, rv3d->persinv);
230 t->persp = rv3d->persp;
237 t->persp = RV3D_ORTHO;
240 calculateCenter2D(t);
243 void setTransformViewAspect(TransInfo *t, float r_aspect[3])
245 copy_v3_fl(r_aspect, 1.0f);
247 if (t->spacetype == SPACE_IMAGE) {
248 SpaceImage *sima = t->sa->spacedata.first;
250 if (t->options & CTX_MASK) {
251 ED_space_image_get_aspect(sima, &r_aspect[0], &r_aspect[1]);
253 else if (t->options & CTX_PAINT_CURVE) {
257 ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]);
260 else if (t->spacetype == SPACE_CLIP) {
261 SpaceClip *sclip = t->sa->spacedata.first;
263 if (t->options & CTX_MOVIECLIP) {
264 ED_space_clip_get_aspect_dimension_aware(sclip, &r_aspect[0], &r_aspect[1]);
267 ED_space_clip_get_aspect(sclip, &r_aspect[0], &r_aspect[1]);
270 else if (t->spacetype == SPACE_IPO) {
271 /* depemds on context of usage */
275 static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
277 float divx = BLI_rcti_size_x(&v2d->mask);
278 float divy = BLI_rcti_size_y(&v2d->mask);
280 r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
281 r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
285 static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
287 float divx = BLI_rcti_size_x(&v2d->mask);
288 float divy = BLI_rcti_size_y(&v2d->mask);
290 float mulx = BLI_rctf_size_x(&v2d->cur);
291 float muly = BLI_rctf_size_y(&v2d->cur);
293 /* difference with convertViewVec2D */
294 /* clamp w/h, mask only */
295 if (mulx / divx < muly / divy) {
305 r_vec[0] = mulx * dx / divx;
306 r_vec[1] = muly * dy / divy;
310 void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
312 if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
313 if (t->options & CTX_PAINT_CURVE) {
318 const float mval_f[2] = {(float)dx, (float)dy};
319 ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac);
322 else if (t->spacetype == SPACE_IMAGE) {
323 if (t->options & CTX_MASK) {
324 convertViewVec2D_mask(t->view, r_vec, dx, dy);
326 else if (t->options & CTX_PAINT_CURVE) {
331 convertViewVec2D(t->view, r_vec, dx, dy);
334 r_vec[0] *= t->aspect[0];
335 r_vec[1] *= t->aspect[1];
337 else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
338 convertViewVec2D(t->view, r_vec, dx, dy);
340 else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
341 convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
343 else if (t->spacetype == SPACE_CLIP) {
344 if (t->options & CTX_MASK) {
345 convertViewVec2D_mask(t->view, r_vec, dx, dy);
348 convertViewVec2D(t->view, r_vec, dx, dy);
351 r_vec[0] *= t->aspect[0];
352 r_vec[1] *= t->aspect[1];
355 printf("%s: called in an invalid context\n", __func__);
360 void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
362 if (t->spacetype == SPACE_VIEW3D) {
363 if (t->ar->regiontype == RGN_TYPE_WINDOW) {
364 if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
365 adr[0] = (int)2140000000.0f; /* this is what was done in 2.64, perhaps we can be smarter? */
366 adr[1] = (int)2140000000.0f;
370 else if (t->spacetype == SPACE_IMAGE) {
371 SpaceImage *sima = t->sa->spacedata.first;
373 if (t->options & CTX_MASK) {
376 v[0] = vec[0] / t->aspect[0];
377 v[1] = vec[1] / t->aspect[1];
379 BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
381 ED_image_point_pos__reverse(sima, t->ar, v, v);
386 else if (t->options & CTX_PAINT_CURVE) {
393 v[0] = vec[0] / t->aspect[0];
394 v[1] = vec[1] / t->aspect[1];
396 UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
399 else if (t->spacetype == SPACE_ACTION) {
402 SpaceAction *sact = t->sa->spacedata.first;
404 if (sact->flag & SACTION_DRAWTIME) {
405 //vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
407 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
412 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
418 else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
421 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
425 else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
428 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
432 else if (t->spacetype == SPACE_CLIP) {
433 SpaceClip *sc = t->sa->spacedata.first;
435 if (t->options & CTX_MASK) {
436 MovieClip *clip = ED_space_clip_get_clip(sc);
441 v[0] = vec[0] / t->aspect[0];
442 v[1] = vec[1] / t->aspect[1];
444 BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v);
446 ED_clip_point_stable_pos__reverse(sc, t->ar, v, v);
456 else if (t->options & CTX_MOVIECLIP) {
459 v[0] = vec[0] / t->aspect[0];
460 v[1] = vec[1] / t->aspect[1];
462 UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
468 else if (t->spacetype == SPACE_NODE) {
469 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &adr[0], &adr[1]);
472 void projectIntView(TransInfo *t, const float vec[3], int adr[2])
474 projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
477 void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
479 switch (t->spacetype) {
482 if (t->options & CTX_PAINT_CURVE) {
486 else if (t->ar->regiontype == RGN_TYPE_WINDOW) {
487 /* allow points behind the view [#33643] */
488 if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
489 /* XXX, 2.64 and prior did this, weak! */
490 adr[0] = t->ar->winx / 2.0f;
491 adr[1] = t->ar->winy / 2.0f;
500 projectIntView(t, vec, a);
507 void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
509 projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
512 void applyAspectRatio(TransInfo *t, float vec[2])
514 if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) && !(t->options & CTX_PAINT_CURVE)) {
515 SpaceImage *sima = t->sa->spacedata.first;
517 if ((sima->flag & SI_COORDFLOATS) == 0) {
519 ED_space_image_get_size(sima, &width, &height);
525 vec[0] /= t->aspect[0];
526 vec[1] /= t->aspect[1];
528 else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
529 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
530 vec[0] /= t->aspect[0];
531 vec[1] /= t->aspect[1];
536 void removeAspectRatio(TransInfo *t, float vec[2])
538 if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
539 SpaceImage *sima = t->sa->spacedata.first;
541 if ((sima->flag & SI_COORDFLOATS) == 0) {
543 ED_space_image_get_size(sima, &width, &height);
549 vec[0] *= t->aspect[0];
550 vec[1] *= t->aspect[1];
552 else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
553 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
554 vec[0] *= t->aspect[0];
555 vec[1] *= t->aspect[1];
560 static void viewRedrawForce(const bContext *C, TransInfo *t)
562 if (t->options & CTX_GPENCIL_STROKES) {
563 WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
565 else if (t->spacetype == SPACE_VIEW3D) {
566 if (t->options & CTX_PAINT_CURVE) {
567 wmWindow *window = CTX_wm_window(C);
568 WM_paint_cursor_tag_redraw(window, t->ar);
571 /* Do we need more refined tags? */
572 if (t->flag & T_POSE)
573 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
575 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
577 /* for realtime animation record - send notifiers recognised by animation editors */
578 // XXX: is this notifier a lame duck?
579 if ((t->animtimer) && IS_AUTOKEY_ON(t->scene))
580 WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
584 else if (t->spacetype == SPACE_ACTION) {
585 //SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
586 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
588 else if (t->spacetype == SPACE_IPO) {
589 //SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
590 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
592 else if (t->spacetype == SPACE_NLA) {
593 WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
595 else if (t->spacetype == SPACE_NODE) {
596 //ED_area_tag_redraw(t->sa);
597 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
599 else if (t->spacetype == SPACE_SEQ) {
600 WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
602 else if (t->spacetype == SPACE_IMAGE) {
603 if (t->options & CTX_MASK) {
604 Mask *mask = CTX_data_edit_mask(C);
606 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
608 else if (t->options & CTX_PAINT_CURVE) {
609 wmWindow *window = CTX_wm_window(C);
610 WM_paint_cursor_tag_redraw(window, t->ar);
613 // XXX how to deal with lock?
614 SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
615 if (sima->lock) WM_event_add_notifier(C, NC_GEOM | ND_DATA, t->obedit->data);
616 else ED_area_tag_redraw(t->sa);
619 else if (t->spacetype == SPACE_CLIP) {
620 SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
622 if (ED_space_clip_check_show_trackedit(sc)) {
623 MovieClip *clip = ED_space_clip_get_clip(sc);
625 /* objects could be parented to tracking data, so send this for viewport refresh */
626 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
628 WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
630 else if (ED_space_clip_check_show_maskedit(sc)) {
631 Mask *mask = CTX_data_edit_mask(C);
633 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
638 static void viewRedrawPost(bContext *C, TransInfo *t)
640 ED_area_headerprint(t->sa, NULL);
642 if (t->spacetype == SPACE_VIEW3D) {
643 /* if autokeying is enabled, send notifiers that keyframes were added */
644 if (IS_AUTOKEY_ON(t->scene))
645 WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
647 /* redraw UV editor */
648 if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) &&
649 (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT))
651 WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
654 /* XXX temp, first hack to get auto-render in compositor work (ton) */
655 WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
659 #if 0 // TRANSFORM_FIX_ME
660 if (t->spacetype == SPACE_VIEW3D) {
661 allqueue(REDRAWBUTSOBJECT, 0);
662 allqueue(REDRAWVIEW3D, 0);
664 else if (t->spacetype == SPACE_IMAGE) {
665 allqueue(REDRAWIMAGE, 0);
666 allqueue(REDRAWVIEW3D, 0);
668 else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) {
669 allqueue(REDRAWVIEW3D, 0);
670 allqueue(REDRAWACTION, 0);
671 allqueue(REDRAWNLA, 0);
672 allqueue(REDRAWIPO, 0);
673 allqueue(REDRAWTIME, 0);
674 allqueue(REDRAWBUTSOBJECT, 0);
677 scrarea_queue_headredraw(curarea);
681 /* ************************** TRANSFORMATIONS **************************** */
683 static void view_editmove(unsigned short UNUSED(event))
685 #if 0 // TRANSFORM_FIX_ME
687 /* Regular: Zoom in */
688 /* Shift: Scroll up */
689 /* Ctrl: Scroll right */
690 /* Alt-Shift: Rotate up */
691 /* Alt-Ctrl: Rotate right */
693 /* only work in 3D window for now
694 * In the end, will have to send to event to a 2D window handler instead
696 if (Trans.flag & T_2D_EDIT)
701 if (G.qual & LR_SHIFTKEY) {
702 if (G.qual & LR_ALTKEY) {
703 G.qual &= ~LR_SHIFTKEY;
705 G.qual |= LR_SHIFTKEY;
711 else if (G.qual & LR_CTRLKEY) {
712 if (G.qual & LR_ALTKEY) {
713 G.qual &= ~LR_CTRLKEY;
715 G.qual |= LR_CTRLKEY;
721 else if (U.uiflag & USER_WHEELZOOMDIR)
722 persptoetsen(PADMINUS);
724 persptoetsen(PADPLUSKEY);
729 if (G.qual & LR_SHIFTKEY) {
730 if (G.qual & LR_ALTKEY) {
731 G.qual &= ~LR_SHIFTKEY;
733 G.qual |= LR_SHIFTKEY;
739 else if (G.qual & LR_CTRLKEY) {
740 if (G.qual & LR_ALTKEY) {
741 G.qual &= ~LR_CTRLKEY;
743 G.qual |= LR_CTRLKEY;
749 else if (U.uiflag & USER_WHEELZOOMDIR)
750 persptoetsen(PADPLUSKEY);
752 persptoetsen(PADMINUS);
759 setTransformViewMatrices(&Trans);
763 /* ************************************************* */
765 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
767 TFM_MODAL_CANCEL = 1,
768 TFM_MODAL_CONFIRM = 2,
769 TFM_MODAL_TRANSLATE = 3,
770 TFM_MODAL_ROTATE = 4,
771 TFM_MODAL_RESIZE = 5,
772 TFM_MODAL_SNAP_INV_ON = 6,
773 TFM_MODAL_SNAP_INV_OFF = 7,
774 TFM_MODAL_SNAP_TOGGLE = 8,
775 TFM_MODAL_AXIS_X = 9,
776 TFM_MODAL_AXIS_Y = 10,
777 TFM_MODAL_AXIS_Z = 11,
778 TFM_MODAL_PLANE_X = 12,
779 TFM_MODAL_PLANE_Y = 13,
780 TFM_MODAL_PLANE_Z = 14,
781 TFM_MODAL_CONS_OFF = 15,
782 TFM_MODAL_ADD_SNAP = 16,
783 TFM_MODAL_REMOVE_SNAP = 17,
785 /* 18 and 19 used by numinput, defined in transform.h */
787 TFM_MODAL_PROPSIZE_UP = 20,
788 TFM_MODAL_PROPSIZE_DOWN = 21,
789 TFM_MODAL_AUTOIK_LEN_INC = 22,
790 TFM_MODAL_AUTOIK_LEN_DEC = 23,
792 TFM_MODAL_EDGESLIDE_UP = 24,
793 TFM_MODAL_EDGESLIDE_DOWN = 25,
795 /* for analog input, like trackpad */
796 TFM_MODAL_PROPSIZE = 26,
797 /* node editor insert offset (aka auto-offset) direction toggle */
798 TFM_MODAL_INSERTOFS_TOGGLE_DIR = 27,
801 /* called in transform_ops.c, on each regeneration of keymaps */
802 wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
804 static const EnumPropertyItem modal_items[] = {
805 {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
806 {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
807 {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Translate", ""},
808 {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
809 {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
810 {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Invert Snap On", ""},
811 {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Invert Snap Off", ""},
812 {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
813 {TFM_MODAL_AXIS_X, "AXIS_X", 0, "Orientation X axis", ""},
814 {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Orientation Y axis", ""},
815 {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Orientation Z axis", ""},
816 {TFM_MODAL_PLANE_X, "PLANE_X", 0, "Orientation X plane", ""},
817 {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Orientation Y plane", ""},
818 {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Orientation Z plane", ""},
819 {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Remove Constraints", ""},
820 {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
821 {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
822 {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
823 {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
824 {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
825 {TFM_MODAL_PROPSIZE_DOWN, "PROPORTIONAL_SIZE_DOWN", 0, "Decrease Proportional Influence", ""},
826 {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
827 {TFM_MODAL_AUTOIK_LEN_DEC, "AUTOIK_CHAIN_LEN_DOWN", 0, "Decrease Max AutoIK Chain Length", ""},
828 {TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
829 {TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
830 {TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
831 {TFM_MODAL_INSERTOFS_TOGGLE_DIR, "INSERTOFS_TOGGLE_DIR", 0, "Toggle Direction for Node Auto-offset", ""},
832 {0, NULL, 0, NULL, NULL}
835 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
837 /* this function is called for each spacetype, only needs to add map once */
838 if (keymap && keymap->modal_items) return NULL;
840 keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
842 /* items for modal map */
843 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL);
844 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
845 WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
846 WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
848 WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE);
849 WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE);
850 WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, TFM_MODAL_RESIZE);
852 WM_modalkeymap_add_item(keymap, TABKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_SNAP_TOGGLE);
854 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
855 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
857 WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
858 WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
860 WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, TFM_MODAL_ADD_SNAP);
861 WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, KM_ALT, 0, TFM_MODAL_REMOVE_SNAP);
863 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
864 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
865 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP);
866 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN);
867 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
868 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
869 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP);
870 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN);
871 WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, TFM_MODAL_PROPSIZE);
873 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_UP);
874 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_DOWN);
876 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
877 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
878 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
879 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
881 /* node editor only */
882 WM_modalkeymap_add_item(keymap, TKEY, KM_PRESS, 0, 0, TFM_MODAL_INSERTOFS_TOGGLE_DIR);
887 static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode)
889 if (!(t->flag & T_NO_CONSTRAINT)) {
890 int constraint_axis, constraint_plane;
891 const bool edit_2d = (t->flag & T_2D_EDIT) != 0;
892 const char *msg1 = "", *msg2 = "", *msg3 = "";
898 msg1 = IFACE_("along X");
899 msg2 = IFACE_("along %s X");
900 msg3 = IFACE_("locking %s X");
902 constraint_axis = CON_AXIS0;
905 msg1 = IFACE_("along Y");
906 msg2 = IFACE_("along %s Y");
907 msg3 = IFACE_("locking %s Y");
909 constraint_axis = CON_AXIS1;
912 msg1 = IFACE_("along Z");
913 msg2 = IFACE_("along %s Z");
914 msg3 = IFACE_("locking %s Z");
916 constraint_axis = CON_AXIS2;
922 constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
924 if (edit_2d && (key_type != ZKEY)) {
929 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg1);
934 if (t->con.orientation != V3D_MANIP_GLOBAL) {
938 short orientation = (t->current_orientation != V3D_MANIP_GLOBAL ?
939 t->current_orientation : V3D_MANIP_LOCAL);
940 if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
941 setUserConstraint(t, orientation, constraint_axis, msg2);
942 else if (t->modifiers & MOD_CONSTRAINT_PLANE)
943 setUserConstraint(t, orientation, constraint_plane, msg3);
947 if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
948 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg2);
949 else if (t->modifiers & MOD_CONSTRAINT_PLANE)
950 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_plane, msg3);
953 t->redraw |= TREDRAW_HARD;
957 int transformEvent(TransInfo *t, const wmEvent *event)
959 char cmode = constraintModeToChar(t);
960 bool handled = false;
961 const int modifiers_prev = t->modifiers;
963 t->redraw |= handleMouseInput(t, &t->mouse, event);
965 /* Handle modal numinput events first, if already activated. */
966 if (((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
967 hasNumInput(&t->num) && handleNumInput(t->context, &(t->num), event))
969 t->redraw |= TREDRAW_HARD;
972 else if (event->type == MOUSEMOVE) {
973 if (t->modifiers & MOD_CONSTRAINT_SELECT)
974 t->con.mode |= CON_SELECT;
976 copy_v2_v2_int(t->mval, event->mval);
978 // t->redraw |= TREDRAW_SOFT; /* Use this for soft redraw. Might cause flicker in object mode */
979 t->redraw |= TREDRAW_HARD;
981 if (t->state == TRANS_STARTING) {
982 t->state = TRANS_RUNNING;
985 applyMouseInput(t, &t->mouse, t->mval, t->values);
987 // Snapping mouse move events
988 t->redraw |= handleSnapping(t, event);
991 /* handle modal keymap first */
992 else if (event->type == EVT_MODAL_MAP) {
993 switch (event->val) {
994 case TFM_MODAL_CANCEL:
995 t->state = TRANS_CANCEL;
998 case TFM_MODAL_CONFIRM:
999 t->state = TRANS_CONFIRM;
1002 case TFM_MODAL_TRANSLATE:
1003 /* only switch when... */
1004 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1005 restoreTransObjects(t);
1007 resetTransRestrictions(t);
1009 initSnapping(t, NULL); // need to reinit after mode change
1010 t->redraw |= TREDRAW_HARD;
1011 WM_event_add_mousemove(t->context);
1014 else if (t->mode == TFM_SEQ_SLIDE) {
1015 t->flag ^= T_ALT_TRANSFORM;
1016 t->redraw |= TREDRAW_HARD;
1020 if (t->obedit && t->obedit->type == OB_MESH) {
1021 if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) {
1022 restoreTransObjects(t);
1024 resetTransRestrictions(t);
1026 /* first try edge slide */
1028 /* if that fails, do vertex slide */
1029 if (t->state == TRANS_CANCEL) {
1030 t->state = TRANS_STARTING;
1033 /* vert slide can fail on unconnected vertices (rare but possible) */
1034 if (t->state == TRANS_CANCEL) {
1035 t->mode = TFM_TRANSLATION;
1036 t->state = TRANS_STARTING;
1037 restoreTransObjects(t);
1038 resetTransRestrictions(t);
1041 initSnapping(t, NULL); // need to reinit after mode change
1042 t->redraw |= TREDRAW_HARD;
1044 WM_event_add_mousemove(t->context);
1047 else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
1048 if (t->mode == TFM_TRANSLATION) {
1049 restoreTransObjects(t);
1051 t->flag ^= T_ALT_TRANSFORM;
1052 t->redraw |= TREDRAW_HARD;
1058 case TFM_MODAL_ROTATE:
1059 /* only switch when... */
1060 if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
1061 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1062 restoreTransObjects(t);
1064 resetTransRestrictions(t);
1066 if (t->mode == TFM_ROTATION) {
1072 initSnapping(t, NULL); // need to reinit after mode change
1073 t->redraw |= TREDRAW_HARD;
1078 case TFM_MODAL_RESIZE:
1079 /* only switch when... */
1080 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1082 /* Scale isn't normally very useful after extrude along normals, see T39756 */
1083 if ((t->con.mode & CON_APPLY) && (t->con.orientation == V3D_MANIP_NORMAL)) {
1087 restoreTransObjects(t);
1089 resetTransRestrictions(t);
1091 initSnapping(t, NULL); // need to reinit after mode change
1092 t->redraw |= TREDRAW_HARD;
1095 else if (t->mode == TFM_SHRINKFATTEN) {
1096 t->flag ^= T_ALT_TRANSFORM;
1097 t->redraw |= TREDRAW_HARD;
1100 else if (t->mode == TFM_RESIZE) {
1101 if (t->options & CTX_MOVIECLIP) {
1102 restoreTransObjects(t);
1104 t->flag ^= T_ALT_TRANSFORM;
1105 t->redraw |= TREDRAW_HARD;
1111 case TFM_MODAL_SNAP_INV_ON:
1112 t->modifiers |= MOD_SNAP_INVERT;
1113 t->redraw |= TREDRAW_HARD;
1116 case TFM_MODAL_SNAP_INV_OFF:
1117 t->modifiers &= ~MOD_SNAP_INVERT;
1118 t->redraw |= TREDRAW_HARD;
1121 case TFM_MODAL_SNAP_TOGGLE:
1122 t->modifiers ^= MOD_SNAP;
1123 t->redraw |= TREDRAW_HARD;
1126 case TFM_MODAL_AXIS_X:
1127 if (!(t->flag & T_NO_CONSTRAINT)) {
1128 transform_event_xyz_constraint(t, XKEY, cmode);
1129 t->redraw |= TREDRAW_HARD;
1133 case TFM_MODAL_AXIS_Y:
1134 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1135 transform_event_xyz_constraint(t, YKEY, cmode);
1136 t->redraw |= TREDRAW_HARD;
1140 case TFM_MODAL_AXIS_Z:
1141 if ((t->flag & (T_NO_CONSTRAINT)) == 0) {
1142 transform_event_xyz_constraint(t, ZKEY, cmode);
1143 t->redraw |= TREDRAW_HARD;
1147 case TFM_MODAL_PLANE_X:
1148 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1153 setUserConstraint(t, t->current_orientation, (CON_AXIS1 | CON_AXIS2), IFACE_("locking %s X"));
1155 t->redraw |= TREDRAW_HARD;
1159 case TFM_MODAL_PLANE_Y:
1160 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1165 setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS2), IFACE_("locking %s Y"));
1167 t->redraw |= TREDRAW_HARD;
1171 case TFM_MODAL_PLANE_Z:
1172 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1177 setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS1), IFACE_("locking %s Z"));
1179 t->redraw |= TREDRAW_HARD;
1183 case TFM_MODAL_CONS_OFF:
1184 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1186 t->redraw |= TREDRAW_HARD;
1190 case TFM_MODAL_ADD_SNAP:
1192 t->redraw |= TREDRAW_HARD;
1195 case TFM_MODAL_REMOVE_SNAP:
1197 t->redraw |= TREDRAW_HARD;
1200 case TFM_MODAL_PROPSIZE:
1201 /* MOUSEPAN usage... */
1202 if (t->flag & T_PROP_EDIT) {
1203 float fac = 1.0f + 0.005f *(event->y - event->prevy);
1204 t->prop_size *= fac;
1205 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1206 t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->far), T_PROP_SIZE_MIN);
1209 t->prop_size = max_ff(min_ff(t->prop_size, T_PROP_SIZE_MAX), T_PROP_SIZE_MIN);
1211 calculatePropRatio(t);
1212 t->redraw |= TREDRAW_HARD;
1216 case TFM_MODAL_PROPSIZE_UP:
1217 if (t->flag & T_PROP_EDIT) {
1218 t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1219 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1220 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1223 t->prop_size = min_ff(t->prop_size, T_PROP_SIZE_MAX);
1225 calculatePropRatio(t);
1226 t->redraw |= TREDRAW_HARD;
1230 case TFM_MODAL_PROPSIZE_DOWN:
1231 if (t->flag & T_PROP_EDIT) {
1232 t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1233 t->prop_size = max_ff(t->prop_size, T_PROP_SIZE_MIN);
1234 calculatePropRatio(t);
1235 t->redraw |= TREDRAW_HARD;
1239 case TFM_MODAL_AUTOIK_LEN_INC:
1240 if (t->flag & T_AUTOIK) {
1241 transform_autoik_update(t, 1);
1242 t->redraw |= TREDRAW_HARD;
1246 case TFM_MODAL_AUTOIK_LEN_DEC:
1247 if (t->flag & T_AUTOIK) {
1248 transform_autoik_update(t, -1);
1249 t->redraw |= TREDRAW_HARD;
1253 case TFM_MODAL_INSERTOFS_TOGGLE_DIR:
1254 if (t->spacetype == SPACE_NODE) {
1255 SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
1257 BLI_assert(t->sa->spacetype == t->spacetype);
1259 if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT) {
1260 snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_LEFT;
1262 else if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_LEFT) {
1263 snode->insert_ofs_dir = SNODE_INSERTOFS_DIR_RIGHT;
1269 t->redraw |= TREDRAW_SOFT;
1272 /* Those two are only handled in transform's own handler, see T44634! */
1273 case TFM_MODAL_EDGESLIDE_UP:
1274 case TFM_MODAL_EDGESLIDE_DOWN:
1279 /* else do non-mapped events */
1280 else if (event->val == KM_PRESS) {
1281 switch (event->type) {
1283 t->state = TRANS_CANCEL;
1286 /* enforce redraw of transform when modifiers are used */
1289 t->modifiers |= MOD_CONSTRAINT_PLANE;
1290 t->redraw |= TREDRAW_HARD;
1295 t->state = TRANS_CONFIRM;
1300 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1301 /* exception for switching to dolly, or trackball, in camera view */
1302 if (t->flag & T_CAMERA) {
1303 if (t->mode == TFM_TRANSLATION)
1304 setLocalConstraint(t, (CON_AXIS2), IFACE_("along local Z"));
1305 else if (t->mode == TFM_ROTATION) {
1306 restoreTransObjects(t);
1311 t->modifiers |= MOD_CONSTRAINT_SELECT;
1312 if (t->con.mode & CON_APPLY) {
1317 initSelectConstraint(t, t->spacemtx);
1320 /* bit hackish... but it prevents mmb select to print the orientation from menu */
1322 strcpy(t->spacename, "global");
1324 initSelectConstraint(t, mati);
1326 postSelectConstraint(t);
1329 t->redraw |= TREDRAW_HARD;
1334 t->state = TRANS_CANCEL;
1339 t->state = TRANS_CONFIRM;
1343 /* only switch when... */
1344 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) {
1345 restoreTransObjects(t);
1347 resetTransRestrictions(t);
1349 initSnapping(t, NULL); // need to reinit after mode change
1350 t->redraw |= TREDRAW_HARD;
1355 /* only switch when... */
1356 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL)) {
1357 restoreTransObjects(t);
1359 resetTransRestrictions(t);
1361 initSnapping(t, NULL); // need to reinit after mode change
1362 t->redraw |= TREDRAW_HARD;
1367 /* only switch when... */
1368 if (!(t->options & CTX_TEXTURE)) {
1369 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION)) {
1370 restoreTransObjects(t);
1372 resetTransRestrictions(t);
1374 if (t->mode == TFM_ROTATION) {
1380 initSnapping(t, NULL); // need to reinit after mode change
1381 t->redraw |= TREDRAW_HARD;
1388 if (!(t->options & CTX_NO_PET)) {
1389 t->flag ^= T_PROP_CONNECTED;
1390 sort_trans_data_dist(t);
1391 calculatePropRatio(t);
1392 t->redraw = TREDRAW_HARD;
1397 if (!(t->flag & T_NO_CONSTRAINT)) {
1399 t->redraw |= TREDRAW_HARD;
1407 if (!(t->flag & T_NO_CONSTRAINT)) {
1408 transform_event_xyz_constraint(t, event->type, cmode);
1413 if (t->flag & T_PROP_EDIT && event->shift) {
1414 t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1415 calculatePropRatio(t);
1416 t->redraw |= TREDRAW_HARD;
1421 if (event->alt && t->flag & T_PROP_EDIT) {
1422 t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1423 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1424 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1425 calculatePropRatio(t);
1426 t->redraw = TREDRAW_HARD;
1431 case WHEELDOWNMOUSE:
1432 if (t->flag & T_AUTOIK) {
1433 transform_autoik_update(t, 1);
1436 view_editmove(event->type);
1438 t->redraw = TREDRAW_HARD;
1442 if (event->alt && t->flag & T_PROP_EDIT) {
1443 t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1444 calculatePropRatio(t);
1445 t->redraw = TREDRAW_HARD;
1451 if (t->flag & T_AUTOIK) {
1452 transform_autoik_update(t, -1);
1455 view_editmove(event->type);
1457 t->redraw = TREDRAW_HARD;
1462 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1463 t->flag |= T_ALT_TRANSFORM;
1464 t->redraw |= TREDRAW_HARD;
1472 /* Snapping key events */
1473 t->redraw |= handleSnapping(t, event);
1475 else if (event->val == KM_RELEASE) {
1476 switch (event->type) {
1479 t->modifiers &= ~MOD_CONSTRAINT_PLANE;
1480 t->redraw |= TREDRAW_HARD;
1485 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1486 t->modifiers &= ~MOD_CONSTRAINT_SELECT;
1487 postSelectConstraint(t);
1488 t->redraw |= TREDRAW_HARD;
1494 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1495 t->flag &= ~T_ALT_TRANSFORM;
1496 t->redraw |= TREDRAW_HARD;
1504 /* confirm transform if launch key is released after mouse move */
1505 if (t->flag & T_RELEASE_CONFIRM) {
1506 /* XXX Keyrepeat bug in Xorg messes this up, will test when fixed */
1507 if (event->type == t->launch_event && (t->launch_event == LEFTMOUSE || t->launch_event == RIGHTMOUSE)) {
1508 t->state = TRANS_CONFIRM;
1513 /* if we change snap options, get the unsnapped values back */
1514 if ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) !=
1515 (modifiers_prev & (MOD_SNAP | MOD_SNAP_INVERT)))
1517 applyMouseInput(t, &t->mouse, t->mval, t->values);
1520 /* Per transform event, if present */
1521 if (t->handleEvent &&
1523 /* Needed for vertex slide, see [#38756] */
1524 (event->type == MOUSEMOVE)))
1526 t->redraw |= t->handleEvent(t, event);
1529 /* Try to init modal numinput now, if possible. */
1530 if (!(handled || t->redraw) && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1531 handleNumInput(t->context, &(t->num), event))
1533 t->redraw |= TREDRAW_HARD;
1537 if (handled || t->redraw) {
1541 return OPERATOR_PASS_THROUGH;
1545 bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
1547 TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
1550 t->state = TRANS_RUNNING;
1552 /* avoid calculating PET */
1553 t->options = CTX_NO_PET;
1555 t->mode = TFM_DUMMY;
1557 initTransInfo(C, t, NULL, NULL);
1559 /* avoid doing connectivity lookups (when V3D_AROUND_LOCAL_ORIGINS is set) */
1560 t->around = V3D_AROUND_CENTER_BOUNDS;
1562 createTransData(C, t); // make TransData structs from selection
1564 t->around = centerMode; // override userdefined mode
1566 if (t->total == 0) {
1575 copy_v2_v2(cent2d, t->center2d);
1579 // Copy center from constraint center. Transform center can be local
1580 copy_v3_v3(cent3d, t->center_global);
1585 /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
1586 special_aftertrans_update(C, t);
1601 static void drawArrow(ArrowDirection d, short offset, short length, short size)
1611 glVertex2s(offset, 0);
1612 glVertex2s(offset + length, 0);
1613 glVertex2s(offset + length, 0);
1614 glVertex2s(offset + length - size, -size);
1615 glVertex2s(offset + length, 0);
1616 glVertex2s(offset + length - size, size);
1627 glVertex2s(0, offset);
1628 glVertex2s(0, offset + length);
1629 glVertex2s(0, offset + length);
1630 glVertex2s(-size, offset + length - size);
1631 glVertex2s(0, offset + length);
1632 glVertex2s(size, offset + length - size);
1638 static void drawArrowHead(ArrowDirection d, short size)
1647 glVertex2s(-size, -size);
1649 glVertex2s(-size, size);
1659 glVertex2s(-size, -size);
1661 glVertex2s(size, -size);
1667 static void drawArc(float size, float angle_start, float angle_end, int segments)
1669 float delta = (angle_end - angle_start) / segments;
1673 glBegin(GL_LINE_STRIP);
1675 for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
1676 glVertex2f(cosf(angle) * size, sinf(angle) * size);
1678 glVertex2f(cosf(angle_end) * size, sinf(angle_end) * size);
1683 static int helpline_poll(bContext *C)
1685 ARegion *ar = CTX_wm_region(C);
1687 if (ar && ar->regiontype == RGN_TYPE_WINDOW)
1692 static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
1694 TransInfo *t = (TransInfo *)customdata;
1696 if (t->helpline != HLP_NONE && !(t->flag & T_USES_MANIPULATOR)) {
1703 projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO);
1707 switch (t->helpline) {
1709 UI_ThemeColor(TH_VIEW_OVERLAY);
1714 glVertex2iv(t->mval);
1718 glTranslate2iv(mval);
1719 glRotatef(-RAD2DEGF(atan2f(cent[0] - t->mval[0], cent[1] - t->mval[1])), 0, 0, 1);
1723 drawArrow(UP, 5, 10, 5);
1724 drawArrow(DOWN, 5, 10, 5);
1727 UI_ThemeColor(TH_VIEW_OVERLAY);
1729 glTranslate2iv(mval);
1732 drawArrow(RIGHT, 5, 10, 5);
1733 drawArrow(LEFT, 5, 10, 5);
1736 UI_ThemeColor(TH_VIEW_OVERLAY);
1738 glTranslate2iv(mval);
1741 drawArrow(UP, 5, 10, 5);
1742 drawArrow(DOWN, 5, 10, 5);
1746 float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1];
1747 float angle = atan2f(dy, dx);
1748 float dist = hypotf(dx, dy);
1749 float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
1750 float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
1751 UI_ThemeColor(TH_VIEW_OVERLAY);
1756 glVertex2iv(t->mval);
1760 glTranslatef(cent[0] - t->mval[0] + mval[0], cent[1] - t->mval[1] + mval[1], 0);
1764 drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
1765 drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
1769 glTranslatef(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
1770 glRotatef(RAD2DEGF(angle - delta_angle), 0, 0, 1);
1772 drawArrowHead(DOWN, 5);
1776 glTranslatef(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
1777 glRotatef(RAD2DEGF(angle + delta_angle), 0, 0, 1);
1779 drawArrowHead(UP, 5);
1784 unsigned char col[3], col2[3];
1785 UI_GetThemeColor3ubv(TH_GRID, col);
1787 glTranslate2iv(mval);
1791 UI_make_axis_color(col, col2, 'X');
1792 glColor3ubv((GLubyte *)col2);
1794 drawArrow(RIGHT, 5, 10, 5);
1795 drawArrow(LEFT, 5, 10, 5);
1797 UI_make_axis_color(col, col2, 'Y');
1798 glColor3ubv((GLubyte *)col2);
1800 drawArrow(UP, 5, 10, 5);
1801 drawArrow(DOWN, 5, 10, 5);
1810 static void drawTransformView(const struct bContext *C, ARegion *UNUSED(ar), void *arg)
1817 drawPropCircle(C, t);
1820 /* edge slide, vert slide */
1825 /* just draw a little warning message in the top-right corner of the viewport to warn that autokeying is enabled */
1826 static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar)
1829 const char *printable = IFACE_("Auto Keying On");
1830 float printable_size[2];
1833 ED_region_visible_rect(ar, &rect);
1835 BLF_width_and_height_default(printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
1837 xco = (rect.xmax - U.widget_unit) - (int)printable_size[0];
1838 yco = (rect.ymax - U.widget_unit);
1840 /* warning text (to clarify meaning of overlays)
1841 * - original color was red to match the icon, but that clashes badly with a less nasty border
1843 UI_ThemeColorShade(TH_TEXT_HI, -50);
1844 #ifdef WITH_INTERNATIONAL
1845 BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1847 BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1850 /* autokey recording icon... */
1851 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1854 xco -= U.widget_unit;
1855 yco -= (int)printable_size[1] / 2;
1857 UI_icon_draw(xco, yco, ICON_REC);
1859 glDisable(GL_BLEND);
1862 static void drawTransformPixel(const struct bContext *UNUSED(C), ARegion *ar, void *arg)
1865 Scene *scene = t->scene;
1868 /* draw autokeyframing hint in the corner
1869 * - only draw if enabled (advanced users may be distracted/annoyed),
1870 * for objects that will be autokeyframed (no point ohterwise),
1871 * AND only for the active region (as showing all is too overwhelming)
1873 if ((U.autokey_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
1875 if (t->flag & (T_OBJECT | T_POSE)) {
1876 if (ob && autokeyframe_cfra_can_key(scene, &ob->id)) {
1877 drawAutoKeyWarning(t, ar);
1885 * \see #initTransform which reads values from the operator.
1887 void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
1889 ToolSettings *ts = CTX_data_tool_settings(C);
1890 int constraint_axis[3] = {0, 0, 0};
1891 int proportional = 0;
1894 // Save back mode in case we're in the generic operator
1895 if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
1896 RNA_property_enum_set(op->ptr, prop, t->mode);
1899 if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
1902 copy_v4_v4(values, (t->flag & T_AUTOVALUES) ? t->auto_values : t->values);
1904 if (RNA_property_array_check(prop)) {
1905 RNA_property_float_set_array(op->ptr, prop, values);
1908 RNA_property_float_set(op->ptr, prop, values[0]);
1912 /* convert flag to enum */
1913 switch (t->flag & T_PROP_EDIT_ALL) {
1915 proportional = PROP_EDIT_ON;
1917 case (T_PROP_EDIT | T_PROP_CONNECTED):
1918 proportional = PROP_EDIT_CONNECTED;
1920 case (T_PROP_EDIT | T_PROP_PROJECTED):
1921 proportional = PROP_EDIT_PROJECTED;
1924 proportional = PROP_EDIT_OFF;
1928 // If modal, save settings back in scene if not set as operator argument
1929 if ((t->flag & T_MODAL) || (op->flag & OP_IS_REPEAT)) {
1930 /* save settings if not set in operator */
1932 /* skip saving proportional edit if it was not actually used */
1933 if (!(t->options & CTX_NO_PET)) {
1934 if ((prop = RNA_struct_find_property(op->ptr, "proportional")) &&
1935 !RNA_property_is_set(op->ptr, prop))
1937 if (t->spacetype == SPACE_IPO)
1938 ts->proportional_fcurve = proportional;
1939 else if (t->spacetype == SPACE_ACTION)
1940 ts->proportional_action = proportional;
1942 ts->proportional = proportional;
1943 else if (t->options & CTX_MASK)
1944 ts->proportional_mask = (proportional != PROP_EDIT_OFF);
1946 ts->proportional_objects = (proportional != PROP_EDIT_OFF);
1949 if ((prop = RNA_struct_find_property(op->ptr, "proportional_size"))) {
1950 ts->proportional_size =
1951 RNA_property_is_set(op->ptr, prop) ? RNA_property_float_get(op->ptr, prop) : t->prop_size;
1954 if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
1955 !RNA_property_is_set(op->ptr, prop))
1957 ts->prop_mode = t->prop_mode;
1961 /* do we check for parameter? */
1962 if (t->modifiers & MOD_SNAP) {
1963 ts->snap_flag |= SCE_SNAP;
1966 ts->snap_flag &= ~SCE_SNAP;
1969 if (t->spacetype == SPACE_VIEW3D) {
1970 if ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) &&
1971 !RNA_property_is_set(op->ptr, prop))
1973 View3D *v3d = t->view;
1975 v3d->twmode = t->current_orientation;
1980 if ((prop = RNA_struct_find_property(op->ptr, "proportional"))) {
1981 RNA_property_enum_set(op->ptr, prop, proportional);
1982 RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
1983 RNA_float_set(op->ptr, "proportional_size", t->prop_size);
1986 if ((prop = RNA_struct_find_property(op->ptr, "axis"))) {
1987 RNA_property_float_set_array(op->ptr, prop, t->axis);
1990 if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
1991 RNA_property_boolean_set(op->ptr, prop, (t->flag & T_MIRROR) != 0);
1994 if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
1995 /* constraint orientation can be global, event if user selects something else
1996 * so use the orientation in the constraint if set
1998 if (t->con.mode & CON_APPLY) {
1999 RNA_enum_set(op->ptr, "constraint_orientation", t->con.orientation);
2002 RNA_enum_set(op->ptr, "constraint_orientation", t->current_orientation);
2005 if (t->con.mode & CON_APPLY) {
2006 if (t->con.mode & CON_AXIS0) {
2007 constraint_axis[0] = 1;
2009 if (t->con.mode & CON_AXIS1) {
2010 constraint_axis[1] = 1;
2012 if (t->con.mode & CON_AXIS2) {
2013 constraint_axis[2] = 1;
2017 RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
2021 const char *prop_id = NULL;
2022 if (t->mode == TFM_SHRINKFATTEN) {
2023 prop_id = "use_even_offset";
2026 if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) {
2028 RNA_property_boolean_set(op->ptr, prop, (t->flag & T_ALT_TRANSFORM) != 0);
2032 if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
2033 RNA_property_boolean_set(op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) != 0);
2038 * \note caller needs to free 't' on a 0 return
2039 * \warning \a event might be NULL (when tweaking from redo panel)
2040 * \see #saveTransform which writes these values back.
2042 bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
2049 /* added initialize, for external calls to set stuff in TransInfo, like undo string */
2051 t->state = TRANS_STARTING;
2053 if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) && RNA_property_is_set(op->ptr, prop)) {
2054 if (RNA_property_boolean_get(op->ptr, prop)) {
2055 options |= CTX_TEXTURE;
2059 if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) && RNA_property_is_set(op->ptr, prop)) {
2060 if (RNA_property_boolean_get(op->ptr, prop)) {
2061 options |= CTX_GPENCIL_STROKES;
2065 t->options = options;
2069 t->launch_event = event ? event->type : -1;
2071 if (t->launch_event == EVT_TWEAK_R) {
2072 t->launch_event = RIGHTMOUSE;
2074 else if (t->launch_event == EVT_TWEAK_L) {
2075 t->launch_event = LEFTMOUSE;
2078 // XXX Remove this when wm_operator_call_internal doesn't use window->eventstate (which can have type = 0)
2079 // For manipulator only, so assume LEFTMOUSE
2080 if (t->launch_event == 0) {
2081 t->launch_event = LEFTMOUSE;
2084 unit_m3(t->spacemtx);
2086 initTransInfo(C, t, op, event);
2087 initTransformOrientation(C, t);
2089 if (t->spacetype == SPACE_VIEW3D) {
2090 t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
2091 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2092 t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2093 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2095 else if (t->spacetype == SPACE_IMAGE) {
2096 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2097 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2098 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2100 else if (t->spacetype == SPACE_CLIP) {
2101 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2102 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2104 else if (t->spacetype == SPACE_NODE) {
2105 /*t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);*/
2106 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2107 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2109 else if (t->spacetype == SPACE_IPO) {
2110 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2111 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2112 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2114 else if (t->spacetype == SPACE_ACTION) {
2115 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2116 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2117 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2120 createTransData(C, t); // make TransData structs from selection
2122 if (t->total == 0) {
2128 /* keymap for shortcut header prints */
2129 t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
2131 /* Stupid code to have Ctrl-Click on manipulator work ok
2133 * do this only for translation/rotation/resize due to only this
2134 * moded are available from manipulator and doing such check could
2135 * lead to keymap conflicts for other modes (see #31584)
2137 if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
2140 for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) {
2141 if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
2142 if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) && event->ctrl) ||
2143 (ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
2144 (ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) && event->alt) ||
2145 ((kmi->type == OSKEY) && event->oskey) )
2147 t->modifiers |= MOD_SNAP_INVERT;
2155 initSnapping(t, op); // Initialize snapping data AFTER mode flags
2157 initSnapSpatial(t, t->snap_spatial);
2159 /* EVIL! posemode code can switch translation to rotate when 1 bone is selected. will be removed (ton) */
2160 /* EVIL2: we gave as argument also texture space context bit... was cleared */
2161 /* EVIL3: extend mode for animation editors also switches modes... but is best way to avoid duplicate code */
2164 calculatePropRatio(t);
2168 /* Initialize accurate transform to settings requested by keymap. */
2169 bool use_accurate = false;
2170 if ((prop = RNA_struct_find_property(op->ptr, "use_accurate")) && RNA_property_is_set(op->ptr, prop)) {
2171 if (RNA_property_boolean_get(op->ptr, prop)) {
2172 use_accurate = true;
2175 initMouseInput(t, &t->mouse, t->center2d, event->mval, use_accurate);
2179 case TFM_TRANSLATION:
2188 case TFM_SKIN_RESIZE:
2200 case TFM_SHRINKFATTEN:
2201 initShrinkFatten(t);
2206 case TFM_CURVE_SHRINKFATTEN:
2207 initCurveShrinkFatten(t);
2209 case TFM_MASK_SHRINKFATTEN:
2210 initMaskShrinkFatten(t);
2212 case TFM_GPENCIL_SHRINKFATTEN:
2213 initGPShrinkFatten(t);
2225 { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
2226 bArmature *arm = t->poseobj->data;
2227 if (arm->drawtype == ARM_ENVELOPE) {
2228 initBoneEnvelope(t);
2229 t->mode = TFM_BONE_ENVELOPE_DIST;
2236 case TFM_BONE_ENVELOPE:
2237 initBoneEnvelope(t);
2239 case TFM_BONE_ENVELOPE_DIST:
2240 initBoneEnvelope(t);
2241 t->mode = TFM_BONE_ENVELOPE_DIST;
2243 case TFM_EDGE_SLIDE:
2244 case TFM_VERT_SLIDE:
2246 const bool use_even = (op ? RNA_boolean_get(op->ptr, "use_even") : false);
2247 const bool flipped = (op ? RNA_boolean_get(op->ptr, "flipped") : false);
2248 const bool use_clamp = (op ? RNA_boolean_get(op->ptr, "use_clamp") : true);
2249 if (mode == TFM_EDGE_SLIDE) {
2250 const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true);
2251 initEdgeSlide_ex(t, use_double_side, use_even, flipped, use_clamp);
2254 initVertSlide_ex(t, use_even, flipped, use_clamp);
2261 case TFM_TIME_TRANSLATE:
2262 initTimeTranslate(t);
2264 case TFM_TIME_SLIDE:
2267 case TFM_TIME_SCALE:
2270 case TFM_TIME_DUPLICATE:
2271 /* same as TFM_TIME_EXTEND, but we need the mode info for later
2272 * so that duplicate-culling will work properly
2274 if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2277 initTimeTranslate(t);
2280 case TFM_TIME_EXTEND:
2281 /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
2282 * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
2283 * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
2284 * depending on which editor this was called from
2286 if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2289 initTimeTranslate(t);
2308 if (t->state == TRANS_CANCEL) {
2313 /* Transformation axis from operator */
2314 if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) {
2315 RNA_property_float_get_array(op->ptr, prop, t->axis);
2316 normalize_v3(t->axis);
2317 copy_v3_v3(t->axis_orig, t->axis);
2320 /* Constraint init from operator */
2321 if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) {
2322 int constraint_axis[3];
2324 RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
2326 if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
2327 t->con.mode |= CON_APPLY;
2329 if (constraint_axis[0]) {
2330 t->con.mode |= CON_AXIS0;
2332 if (constraint_axis[1]) {
2333 t->con.mode |= CON_AXIS1;
2335 if (constraint_axis[2]) {
2336 t->con.mode |= CON_AXIS2;
2339 setUserConstraint(t, t->current_orientation, t->con.mode, "%s");
2343 /* overwrite initial values if operator supplied a non-null vector
2345 * keep last so we can apply the constraints space.
2347 if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
2348 float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory */
2350 if (RNA_property_array_check(prop)) {
2351 RNA_float_get_array(op->ptr, "value", values);
2354 values[0] = RNA_float_get(op->ptr, "value");
2357 copy_v4_v4(t->values, values);
2358 copy_v4_v4(t->auto_values, values);
2359 t->flag |= T_AUTOVALUES;
2367 void transformApply(bContext *C, TransInfo *t)
2371 if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
2372 selectConstraint(t);
2374 t->transform(t, t->mval); // calls recalcData()
2375 viewRedrawForce(C, t);
2377 t->redraw = TREDRAW_NOTHING;
2379 else if (t->redraw & TREDRAW_SOFT) {
2380 viewRedrawForce(C, t);
2383 /* If auto confirm is on, break after one pass */
2384 if (t->options & CTX_AUTOCONFIRM) {
2385 t->state = TRANS_CONFIRM;
2391 static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
2395 if (t->redraw & TREDRAW_SOFT) {
2396 t->redraw |= TREDRAW_HARD;
2397 transformApply((bContext *)C, t);
2401 int transformEnd(bContext *C, TransInfo *t)
2403 int exit_code = OPERATOR_RUNNING_MODAL;
2407 if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
2408 /* handle restoring objects */
2409 if (t->state == TRANS_CANCEL) {
2410 /* exception, edge slide transformed UVs too */
2411 if (t->mode == TFM_EDGE_SLIDE) {
2412 doEdgeSlide(t, 0.0f);
2414 else if (t->mode == TFM_VERT_SLIDE) {
2415 doVertSlide(t, 0.0f);
2418 exit_code = OPERATOR_CANCELLED;
2419 restoreTransObjects(t); // calls recalcData()
2422 exit_code = OPERATOR_FINISHED;
2425 /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
2426 special_aftertrans_update(C, t);
2431 /* send events out for redraws */
2432 viewRedrawPost(C, t);
2434 viewRedrawForce(C, t);
2442 /* ************************** TRANSFORM LOCKS **************************** */
2444 static void protectedTransBits(short protectflag, float vec[3])
2446 if (protectflag & OB_LOCK_LOCX)
2448 if (protectflag & OB_LOCK_LOCY)
2450 if (protectflag & OB_LOCK_LOCZ)
2454 static void protectedSizeBits(short protectflag, float size[3])
2456 if (protectflag & OB_LOCK_SCALEX)
2458 if (protectflag & OB_LOCK_SCALEY)
2460 if (protectflag & OB_LOCK_SCALEZ)
2464 static void protectedRotateBits(short protectflag, float eul[3], const float oldeul[3])
2466 if (protectflag & OB_LOCK_ROTX)
2468 if (protectflag & OB_LOCK_ROTY)
2470 if (protectflag & OB_LOCK_ROTZ)
2475 /* this function only does the delta rotation */
2476 /* axis-angle is usually internally stored as quats... */
2477 static void protectedAxisAngleBits(short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle)
2479 /* check that protection flags are set */
2480 if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2483 if (protectflag & OB_LOCK_ROT4D) {
2484 /* axis-angle getting limited as 4D entities that they are... */
2485 if (protectflag & OB_LOCK_ROTW)
2487 if (protectflag & OB_LOCK_ROTX)
2488 axis[0] = oldAxis[0];
2489 if (protectflag & OB_LOCK_ROTY)
2490 axis[1] = oldAxis[1];
2491 if (protectflag & OB_LOCK_ROTZ)
2492 axis[2] = oldAxis[2];
2495 /* axis-angle get limited with euler... */
2496 float eul[3], oldeul[3];
2498 axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle);
2499 axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle);
2501 if (protectflag & OB_LOCK_ROTX)
2503 if (protectflag & OB_LOCK_ROTY)
2505 if (protectflag & OB_LOCK_ROTZ)
2508 eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT);
2510 /* when converting to axis-angle, we need a special exception for the case when there is no axis */
2511 if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) {
2512 /* for now, rotate around y-axis then (so that it simply becomes the roll) */
2518 /* this function only does the delta rotation */
2519 static void protectedQuaternionBits(short protectflag, float quat[4], const float oldquat[4])
2521 /* check that protection flags are set */
2522 if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2525 if (protectflag & OB_LOCK_ROT4D) {
2526 /* quaternions getting limited as 4D entities that they are... */
2527 if (protectflag & OB_LOCK_ROTW)
2528 quat[0] = oldquat[0];
2529 if (protectflag & OB_LOCK_ROTX)
2530 quat[1] = oldquat[1];
2531 if (protectflag & OB_LOCK_ROTY)
2532 quat[2] = oldquat[2];
2533 if (protectflag & OB_LOCK_ROTZ)
2534 quat[3] = oldquat[3];
2537 /* quaternions get limited with euler... (compatibility mode) */
2538 float eul[3], oldeul[3], nquat[4], noldquat[4];
2541 qlen = normalize_qt_qt(nquat, quat);
2542 normalize_qt_qt(noldquat, oldquat);
2544 quat_to_eul(eul, nquat);
2545 quat_to_eul(oldeul, noldquat);
2547 if (protectflag & OB_LOCK_ROTX)
2549 if (protectflag & OB_LOCK_ROTY)
2551 if (protectflag & OB_LOCK_ROTZ)
2554 eul_to_quat(quat, eul);
2556 /* restore original quat size */
2557 mul_qt_fl(quat, qlen);
2559 /* quaternions flip w sign to accumulate rotations correctly */
2560 if ((nquat[0] < 0.0f && quat[0] > 0.0f) ||
2561 (nquat[0] > 0.0f && quat[0] < 0.0f))
2563 mul_qt_fl(quat, -1.0f);
2568 /* ******************* TRANSFORM LIMITS ********************** */
2570 static void constraintTransLim(TransInfo *t, TransData *td)
2573 const bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_LOCLIMIT);
2574 const bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_DISTLIMIT);
2576 bConstraintOb cob = {NULL};
2578 float ctime = (float)(t->scene->r.cfra);
2580 /* Make a temporary bConstraintOb for using these limit constraints
2581 * - they only care that cob->matrix is correctly set ;-)
2582 * - current space should be local
2584 unit_m4(cob.matrix);
2585 copy_v3_v3(cob.matrix[3], td->loc);
2587 /* Evaluate valid constraints */
2588 for (con = td->con; con; con = con->next) {
2589 const bConstraintTypeInfo *cti = NULL;
2590 ListBase targets = {NULL, NULL};
2592 /* only consider constraint if enabled */
2593 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2594 if (con->enforce == 0.0f) continue;
2596 /* only use it if it's tagged for this purpose (and the right type) */
2597 if (con->type == CONSTRAINT_TYPE_LOCLIMIT) {
2598 bLocLimitConstraint *data = con->data;
2600 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2604 else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) {
2605 bDistLimitConstraint *data = con->data;
2607 if ((data->flag & LIMITDIST_TRANSFORM) == 0)
2613 /* do space conversions */
2614 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2615 /* just multiply by td->mtx (this should be ok) */
2616 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2618 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2619 /* skip... incompatable spacetype */
2623 /* get constraint targets if needed */
2624 BKE_constraint_targets_for_solving_get(con, &cob, &targets, ctime);
2627 cti->evaluate_constraint(con, &cob, &targets);
2629 /* convert spaces again */
2630 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2631 /* just multiply by td->smtx (this should be ok) */
2632 mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2635 /* free targets list */
2636 BLI_freelistN(&targets);
2640 /* copy results from cob->matrix */
2641 copy_v3_v3(td->loc, cob.matrix[3]);
2645 static void constraintob_from_transdata(bConstraintOb *cob, TransData *td)
2647 /* Make a temporary bConstraintOb for use by limit constraints
2648 * - they only care that cob->matrix is correctly set ;-)
2649 * - current space should be local
2651 memset(cob, 0, sizeof(bConstraintOb));
2653 if (td->ext->rotOrder == ROT_MODE_QUAT) {
2655 /* objects and bones do normalization first too, otherwise
2656 * we don't necessarily end up with a rotation matrix, and
2657 * then conversion back to quat gives a different result */
2659 normalize_qt_qt(quat, td->ext->quat);
2660 quat_to_mat4(cob->matrix, quat);
2662 else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2664 axis_angle_to_mat4(cob->matrix, td->ext->rotAxis, *td->ext->rotAngle);
2668 eulO_to_mat4(cob->matrix, td->ext->rot, td->ext->rotOrder);
2673 static void constraintRotLim(TransInfo *UNUSED(t), TransData *td)
2676 const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_ROTLIMIT);
2679 bool do_limit = false;
2681 /* Evaluate valid constraints */
2682 for (con = td->con; con; con = con->next) {
2683 /* only consider constraint if enabled */
2684 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2685 if (con->enforce == 0.0f) continue;
2687 /* we're only interested in Limit-Rotation constraints */
2688 if (con->type == CONSTRAINT_TYPE_ROTLIMIT) {
2689 bRotLimitConstraint *data = con->data;
2691 /* only use it if it's tagged for this purpose */
2692 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2695 /* skip incompatable spacetypes */
2696 if (!ELEM(con->ownspace, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL))
2699 /* only do conversion if necessary, to preserve quats and eulers */
2700 if (do_limit == false) {
2701 constraintob_from_transdata(&cob, td);
2705 /* do space conversions */
2706 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2707 /* just multiply by td->mtx (this should be ok) */
2708 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2712 cti->evaluate_constraint(con, &cob, NULL);
2714 /* convert spaces again */
2715 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2716 /* just multiply by td->smtx (this should be ok) */
2717 mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2723 /* copy results from cob->matrix */
2724 if (td->ext->rotOrder == ROT_MODE_QUAT) {
2726 mat4_to_quat(td->ext->quat, cob.matrix);
2728 else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2730 mat4_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, cob.matrix);
2734 mat4_to_eulO(td->ext->rot, td->ext->rotOrder, cob.matrix);
2740 static void constraintSizeLim(TransInfo *t, TransData *td)
2742 if (td->con && td->ext) {
2743 const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_SIZELIMIT);
2744 bConstraintOb cob = {NULL};
2746 float size_sign[3], size_abs[3];
2749 /* Make a temporary bConstraintOb for using these limit constraints
2750 * - they only care that cob->matrix is correctly set ;-)
2751 * - current space should be local
2753 if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2754 /* scale val and reset size */
2755 return; // TODO: fix this case
2758 /* Reset val if SINGLESIZE but using a constraint */
2759 if (td->flag & TD_SINGLESIZE)
2762 /* separate out sign to apply back later */
2763 for (i = 0; i < 3; i++) {
2764 size_sign[i] = signf(td->ext->size[i]);
2765 size_abs[i] = fabsf(td->ext->size[i]);
2768 size_to_mat4(cob.matrix, size_abs);
2771 /* Evaluate valid constraints */
2772 for (con = td->con; con; con = con->next) {
2773 /* only consider constraint if enabled */
2774 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2775 if (con->enforce == 0.0f) continue;
2777 /* we're only interested in Limit-Scale constraints */
2778 if (con->type == CONSTRAINT_TYPE_SIZELIMIT) {
2779 bSizeLimitConstraint *data = con->data;
2781 /* only use it if it's tagged for this purpose */
2782 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2785 /* do space conversions */
2786 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2787 /* just multiply by td->mtx (this should be ok) */
2788 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2790 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2791 /* skip... incompatible spacetype */
2796 cti->evaluate_constraint(con, &cob, NULL);
2798 /* convert spaces again */
2799 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2800 /* just multiply by td->smtx (this should be ok) */
2801 mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2806 /* copy results from cob->matrix */
2807 if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2808 /* scale val and reset size */
2809 return; // TODO: fix this case
2812 /* Reset val if SINGLESIZE but using a constraint */
2813 if (td->flag & TD_SINGLESIZE)
2816 /* extrace scale from matrix and apply back sign */
2817 mat4_to_size(td->ext->size, cob.matrix);
2818 mul_v3_v3(td->ext->size, size_sign);
2824 /* -------------------------------------------------------------------- */
2825 /* Transform (Bend) */
2827 /** \name Transform Bend
2830 struct BendCustomData {
2837 /* for applying the mouse distance */
2838 float warp_init_dist;
2841 static void initBend(TransInfo *t)
2843 const float mval_fl[2] = {UNPACK2(t->mval)};
2846 struct BendCustomData *data;
2849 t->transform = Bend;
2850 t->handleEvent = handleEventBend;
2852 setInputPostFct(&t->mouse, postInputRotation);
2853 initMouseInputMode(t, &t->mouse, INPUT_ANGLE_SPRING);
2858 t->snap[1] = DEG2RAD(5.0);
2859 t->snap[2] = DEG2RAD(1.0);
2861 copy_v3_fl(t->num.val_inc, t->snap[1]);
2862 t->num.unit_sys = t->scene->unit.system;
2863 t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS);
2864 t->num.unit_type[0] = B_UNIT_ROTATION;
2865 t->num.unit_type[1] = B_UNIT_LENGTH;
2867 t->flag |= T_NO_CONSTRAINT;
2869 //copy_v3_v3(t->center, ED_view3d_cursor3d_get(t->scene, t->view));
2870 if ((t->flag & T_OVERRIDE_CENTER) == 0) {