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_memarena.h"
57 #include "BKE_editmesh_bvh.h"
58 #include "BKE_context.h"
59 #include "BKE_constraint.h"
60 #include "BKE_particle.h"
63 #include "BKE_report.h"
66 #include "BIF_glutil.h"
69 #include "ED_keyframing.h"
70 #include "ED_screen.h"
71 #include "ED_space_api.h"
72 #include "ED_markers.h"
73 #include "ED_view3d.h"
81 #include "UI_view2d.h"
82 #include "UI_interface_icons.h"
83 #include "UI_resources.h"
85 #include "RNA_access.h"
88 #include "BLF_translation.h"
90 #include "transform.h"
92 /* Disabling, since when you type you know what you are doing, and being able to set it to zero is handy. */
93 // #define USE_NUM_NO_ZERO
95 #define MAX_INFO_LEN 256
97 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
98 static void doEdgeSlide(TransInfo *t, float perc);
99 static void doVertSlide(TransInfo *t, float perc);
101 static void drawEdgeSlide(const struct bContext *C, TransInfo *t);
102 static void drawVertSlide(const struct bContext *C, TransInfo *t);
103 static void len_v3_ensure(float v[3], const float length);
104 static void postInputRotation(TransInfo *t, float values[3]);
107 /* Transform Callbacks */
108 static void initBend(TransInfo *t);
109 static eRedrawFlag handleEventBend(TransInfo *t, const struct wmEvent *event);
110 static void Bend(TransInfo *t, const int mval[2]);
112 static void initShear(TransInfo *t);
113 static eRedrawFlag handleEventShear(TransInfo *t, const struct wmEvent *event);
114 static void applyShear(TransInfo *t, const int mval[2]);
116 static void initResize(TransInfo *t);
117 static void applyResize(TransInfo *t, const int mval[2]);
119 static void initSkinResize(TransInfo *t);
120 static void applySkinResize(TransInfo *t, const int mval[2]);
122 static void initTranslation(TransInfo *t);
123 static void applyTranslation(TransInfo *t, const int mval[2]);
125 static void initToSphere(TransInfo *t);
126 static void applyToSphere(TransInfo *t, const int mval[2]);
128 static void initRotation(TransInfo *t);
129 static void applyRotation(TransInfo *t, const int mval[2]);
131 static void initShrinkFatten(TransInfo *t);
132 static void applyShrinkFatten(TransInfo *t, const int mval[2]);
134 static void initTilt(TransInfo *t);
135 static void applyTilt(TransInfo *t, const int mval[2]);
137 static void initCurveShrinkFatten(TransInfo *t);
138 static void applyCurveShrinkFatten(TransInfo *t, const int mval[2]);
140 static void initMaskShrinkFatten(TransInfo *t);
141 static void applyMaskShrinkFatten(TransInfo *t, const int mval[2]);
143 static void initGPShrinkFatten(TransInfo *t);
144 static void applyGPShrinkFatten(TransInfo *t, const int mval[2]);
146 static void initTrackball(TransInfo *t);
147 static void applyTrackball(TransInfo *t, const int mval[2]);
149 static void initPushPull(TransInfo *t);
150 static void applyPushPull(TransInfo *t, const int mval[2]);
152 static void initBevelWeight(TransInfo *t);
153 static void applyBevelWeight(TransInfo *t, const int mval[2]);
155 static void initCrease(TransInfo *t);
156 static void applyCrease(TransInfo *t, const int mval[2]);
158 static void initBoneSize(TransInfo *t);
159 static void applyBoneSize(TransInfo *t, const int mval[2]);
161 static void initBoneEnvelope(TransInfo *t);
162 static void applyBoneEnvelope(TransInfo *t, const int mval[2]);
164 static void initBoneRoll(TransInfo *t);
165 static void applyBoneRoll(TransInfo *t, const int mval[2]);
167 static void initEdgeSlide(TransInfo *t);
168 static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event);
169 static void applyEdgeSlide(TransInfo *t, const int mval[2]);
171 static void initVertSlide(TransInfo *t);
172 static eRedrawFlag handleEventVertSlide(TransInfo *t, const struct wmEvent *event);
173 static void applyVertSlide(TransInfo *t, const int mval[2]);
175 static void initTimeTranslate(TransInfo *t);
176 static void applyTimeTranslate(TransInfo *t, const int mval[2]);
178 static void initTimeSlide(TransInfo *t);
179 static void applyTimeSlide(TransInfo *t, const int mval[2]);
181 static void initTimeScale(TransInfo *t);
182 static void applyTimeScale(TransInfo *t, const int mval[2]);
184 static void initBakeTime(TransInfo *t);
185 static void applyBakeTime(TransInfo *t, const int mval[2]);
187 static void initMirror(TransInfo *t);
188 static void applyMirror(TransInfo *t, const int mval[2]);
190 static void initAlign(TransInfo *t);
191 static void applyAlign(TransInfo *t, const int mval[2]);
193 static void initSeqSlide(TransInfo *t);
194 static void applySeqSlide(TransInfo *t, const int mval[2]);
195 /* end transform callbacks */
198 static bool transdata_check_local_center(TransInfo *t, short around)
200 return ((around == V3D_LOCAL) && (
201 (t->flag & (T_OBJECT | T_POSE)) ||
202 (t->obedit && ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) ||
203 (t->spacetype == SPACE_IPO) ||
204 (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))
208 bool transdata_check_local_islands(TransInfo *t, short around)
210 return ((around == V3D_LOCAL) && (
211 (t->obedit && ELEM(t->obedit->type, OB_MESH))));
214 /* ************************** SPACE DEPENDANT CODE **************************** */
216 void setTransformViewMatrices(TransInfo *t)
218 if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
219 RegionView3D *rv3d = t->ar->regiondata;
221 copy_m4_m4(t->viewmat, rv3d->viewmat);
222 copy_m4_m4(t->viewinv, rv3d->viewinv);
223 copy_m4_m4(t->persmat, rv3d->persmat);
224 copy_m4_m4(t->persinv, rv3d->persinv);
225 t->persp = rv3d->persp;
232 t->persp = RV3D_ORTHO;
235 calculateCenter2D(t);
238 static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
242 divx = BLI_rcti_size_x(&v2d->mask);
243 divy = BLI_rcti_size_y(&v2d->mask);
245 r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
246 r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
250 static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
255 divx = BLI_rcti_size_x(&v2d->mask);
256 divy = BLI_rcti_size_y(&v2d->mask);
258 mulx = BLI_rctf_size_x(&v2d->cur);
259 muly = BLI_rctf_size_y(&v2d->cur);
261 /* difference with convertViewVec2D */
262 /* clamp w/h, mask only */
263 if (mulx / divx < muly / divy) {
273 r_vec[0] = mulx * dx / divx;
274 r_vec[1] = muly * dy / divy;
278 void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy)
280 if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
281 if (t->options & CTX_PAINT_CURVE) {
286 const float mval_f[2] = {(float)dx, (float)dy};
287 ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac);
290 else if (t->spacetype == SPACE_IMAGE) {
293 if (t->options & CTX_MASK) {
294 convertViewVec2D_mask(t->view, r_vec, dx, dy);
295 ED_space_image_get_aspect(t->sa->spacedata.first, &aspx, &aspy);
297 else if (t->options & CTX_PAINT_CURVE) {
304 convertViewVec2D(t->view, r_vec, dx, dy);
305 ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
311 else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
312 convertViewVec2D(t->view, r_vec, dx, dy);
314 else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
315 convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
317 else if (t->spacetype == SPACE_CLIP) {
320 if (t->options & CTX_MASK) {
321 convertViewVec2D_mask(t->view, r_vec, dx, dy);
324 convertViewVec2D(t->view, r_vec, dx, dy);
327 if (t->options & CTX_MOVIECLIP) {
328 ED_space_clip_get_aspect_dimension_aware(t->sa->spacedata.first, &aspx, &aspy);
330 else if (t->options & CTX_MASK) {
331 /* TODO - NOT WORKING, this isnt so bad since its only display aspect */
332 ED_space_clip_get_aspect(t->sa->spacedata.first, &aspx, &aspy);
335 /* should never happen, quiet warnings */
344 printf("%s: called in an invalid context\n", __func__);
349 void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
351 if (t->spacetype == SPACE_VIEW3D) {
352 if (t->ar->regiontype == RGN_TYPE_WINDOW) {
353 if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
354 adr[0] = (int)2140000000.0f; /* this is what was done in 2.64, perhaps we can be smarter? */
355 adr[1] = (int)2140000000.0f;
359 else if (t->spacetype == SPACE_IMAGE) {
360 SpaceImage *sima = t->sa->spacedata.first;
362 if (t->options & CTX_MASK) {
366 ED_space_image_get_aspect(sima, &aspx, &aspy);
373 BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
375 ED_image_point_pos__reverse(sima, t->ar, v, v);
380 else if (t->options & CTX_PAINT_CURVE) {
385 float aspx, aspy, v[2];
387 ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
388 v[0] = vec[0] / aspx;
389 v[1] = vec[1] / aspy;
391 UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
394 else if (t->spacetype == SPACE_ACTION) {
397 SpaceAction *sact = t->sa->spacedata.first;
399 if (sact->flag & SACTION_DRAWTIME) {
400 //vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
402 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
407 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
413 else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
416 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
420 else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
423 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
427 else if (t->spacetype == SPACE_CLIP) {
428 SpaceClip *sc = t->sa->spacedata.first;
430 if (t->options & CTX_MASK) {
431 MovieClip *clip = ED_space_clip_get_clip(sc);
437 ED_space_clip_get_aspect(sc, &aspx, &aspy);
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) {
457 float v[2], aspx, aspy;
460 ED_space_clip_get_aspect_dimension_aware(t->sa->spacedata.first, &aspx, &aspy);
465 UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]);
471 else if (t->spacetype == SPACE_NODE) {
472 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &adr[0], &adr[1]);
475 void projectIntView(TransInfo *t, const float vec[3], int adr[2])
477 projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
480 void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
482 switch (t->spacetype) {
485 if (t->options & CTX_PAINT_CURVE) {
489 else if (t->ar->regiontype == RGN_TYPE_WINDOW) {
490 /* allow points behind the view [#33643] */
491 if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
492 /* XXX, 2.64 and prior did this, weak! */
493 adr[0] = t->ar->winx / 2.0f;
494 adr[1] = t->ar->winy / 2.0f;
503 projectIntView(t, vec, a);
510 void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
512 projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
515 void applyAspectRatio(TransInfo *t, float vec[2])
517 if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) && !(t->options & CTX_PAINT_CURVE)) {
518 SpaceImage *sima = t->sa->spacedata.first;
521 if ((sima->flag & SI_COORDFLOATS) == 0) {
523 ED_space_image_get_size(sima, &width, &height);
529 ED_space_image_get_uv_aspect(sima, &aspx, &aspy);
533 else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
534 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
535 SpaceClip *sc = t->sa->spacedata.first;
539 if (t->options & CTX_MOVIECLIP) {
540 ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy);
545 else if (t->options & CTX_MASK) {
546 ED_space_clip_get_aspect(sc, &aspx, &aspy);
555 void removeAspectRatio(TransInfo *t, float vec[2])
557 if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
558 SpaceImage *sima = t->sa->spacedata.first;
561 if ((sima->flag & SI_COORDFLOATS) == 0) {
563 ED_space_image_get_size(sima, &width, &height);
569 ED_space_image_get_uv_aspect(sima, &aspx, &aspy);
573 else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
574 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
575 SpaceClip *sc = t->sa->spacedata.first;
576 float aspx = 1.0f, aspy = 1.0f;
578 if (t->options & CTX_MOVIECLIP) {
579 ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy);
581 else if (t->options & CTX_MASK) {
582 ED_space_clip_get_aspect(sc, &aspx, &aspy);
591 static void viewRedrawForce(const bContext *C, TransInfo *t)
593 if (t->options & CTX_GPENCIL_STROKES) {
594 WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
596 else if (t->spacetype == SPACE_VIEW3D) {
597 if (t->options & CTX_PAINT_CURVE) {
598 wmWindow *window = CTX_wm_window(C);
599 WM_paint_cursor_tag_redraw(window, t->ar);
602 /* Do we need more refined tags? */
603 if (t->flag & T_POSE)
604 WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
606 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
608 /* for realtime animation record - send notifiers recognised by animation editors */
609 // XXX: is this notifier a lame duck?
610 if ((t->animtimer) && IS_AUTOKEY_ON(t->scene))
611 WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
615 else if (t->spacetype == SPACE_ACTION) {
616 //SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
617 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
619 else if (t->spacetype == SPACE_IPO) {
620 //SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
621 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
623 else if (t->spacetype == SPACE_NLA) {
624 WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
626 else if (t->spacetype == SPACE_NODE) {
627 //ED_area_tag_redraw(t->sa);
628 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
630 else if (t->spacetype == SPACE_SEQ) {
631 WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
633 else if (t->spacetype == SPACE_IMAGE) {
634 if (t->options & CTX_MASK) {
635 Mask *mask = CTX_data_edit_mask(C);
637 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
639 else if (t->options & CTX_PAINT_CURVE) {
640 wmWindow *window = CTX_wm_window(C);
641 WM_paint_cursor_tag_redraw(window, t->ar);
644 // XXX how to deal with lock?
645 SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
646 if (sima->lock) WM_event_add_notifier(C, NC_GEOM | ND_DATA, t->obedit->data);
647 else ED_area_tag_redraw(t->sa);
650 else if (t->spacetype == SPACE_CLIP) {
651 SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
653 if (ED_space_clip_check_show_trackedit(sc)) {
654 MovieClip *clip = ED_space_clip_get_clip(sc);
656 /* objects could be parented to tracking data, so send this for viewport refresh */
657 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
659 WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
661 else if (ED_space_clip_check_show_maskedit(sc)) {
662 Mask *mask = CTX_data_edit_mask(C);
664 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
669 static void viewRedrawPost(bContext *C, TransInfo *t)
671 ED_area_headerprint(t->sa, NULL);
673 if (t->spacetype == SPACE_VIEW3D) {
674 /* if autokeying is enabled, send notifiers that keyframes were added */
675 if (IS_AUTOKEY_ON(t->scene))
676 WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
678 /* redraw UV editor */
679 if (ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) &&
680 (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT))
682 WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
685 /* XXX temp, first hack to get auto-render in compositor work (ton) */
686 WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
690 #if 0 // TRANSFORM_FIX_ME
691 if (t->spacetype == SPACE_VIEW3D) {
692 allqueue(REDRAWBUTSOBJECT, 0);
693 allqueue(REDRAWVIEW3D, 0);
695 else if (t->spacetype == SPACE_IMAGE) {
696 allqueue(REDRAWIMAGE, 0);
697 allqueue(REDRAWVIEW3D, 0);
699 else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) {
700 allqueue(REDRAWVIEW3D, 0);
701 allqueue(REDRAWACTION, 0);
702 allqueue(REDRAWNLA, 0);
703 allqueue(REDRAWIPO, 0);
704 allqueue(REDRAWTIME, 0);
705 allqueue(REDRAWBUTSOBJECT, 0);
708 scrarea_queue_headredraw(curarea);
712 /* ************************** TRANSFORMATIONS **************************** */
714 static void view_editmove(unsigned short UNUSED(event))
716 #if 0 // TRANSFORM_FIX_ME
718 /* Regular: Zoom in */
719 /* Shift: Scroll up */
720 /* Ctrl: Scroll right */
721 /* Alt-Shift: Rotate up */
722 /* Alt-Ctrl: Rotate right */
724 /* only work in 3D window for now
725 * In the end, will have to send to event to a 2D window handler instead
727 if (Trans.flag & T_2D_EDIT)
733 if (G.qual & LR_SHIFTKEY) {
734 if (G.qual & LR_ALTKEY) {
735 G.qual &= ~LR_SHIFTKEY;
737 G.qual |= LR_SHIFTKEY;
743 else if (G.qual & LR_CTRLKEY) {
744 if (G.qual & LR_ALTKEY) {
745 G.qual &= ~LR_CTRLKEY;
747 G.qual |= LR_CTRLKEY;
753 else if (U.uiflag & USER_WHEELZOOMDIR)
754 persptoetsen(PADMINUS);
756 persptoetsen(PADPLUSKEY);
761 if (G.qual & LR_SHIFTKEY) {
762 if (G.qual & LR_ALTKEY) {
763 G.qual &= ~LR_SHIFTKEY;
765 G.qual |= LR_SHIFTKEY;
771 else if (G.qual & LR_CTRLKEY) {
772 if (G.qual & LR_ALTKEY) {
773 G.qual &= ~LR_CTRLKEY;
775 G.qual |= LR_CTRLKEY;
781 else if (U.uiflag & USER_WHEELZOOMDIR)
782 persptoetsen(PADPLUSKEY);
784 persptoetsen(PADMINUS);
791 setTransformViewMatrices(&Trans);
795 /* ************************************************* */
797 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
798 #define TFM_MODAL_CANCEL 1
799 #define TFM_MODAL_CONFIRM 2
800 #define TFM_MODAL_TRANSLATE 3
801 #define TFM_MODAL_ROTATE 4
802 #define TFM_MODAL_RESIZE 5
803 #define TFM_MODAL_SNAP_INV_ON 6
804 #define TFM_MODAL_SNAP_INV_OFF 7
805 #define TFM_MODAL_SNAP_TOGGLE 8
806 #define TFM_MODAL_AXIS_X 9
807 #define TFM_MODAL_AXIS_Y 10
808 #define TFM_MODAL_AXIS_Z 11
809 #define TFM_MODAL_PLANE_X 12
810 #define TFM_MODAL_PLANE_Y 13
811 #define TFM_MODAL_PLANE_Z 14
812 #define TFM_MODAL_CONS_OFF 15
813 #define TFM_MODAL_ADD_SNAP 16
814 #define TFM_MODAL_REMOVE_SNAP 17
815 /* 18 and 19 used by numinput, defined in transform.h
817 #define TFM_MODAL_PROPSIZE_UP 20
818 #define TFM_MODAL_PROPSIZE_DOWN 21
819 #define TFM_MODAL_AUTOIK_LEN_INC 22
820 #define TFM_MODAL_AUTOIK_LEN_DEC 23
822 #define TFM_MODAL_EDGESLIDE_UP 24
823 #define TFM_MODAL_EDGESLIDE_DOWN 25
825 /* for analog input, like trackpad */
826 #define TFM_MODAL_PROPSIZE 26
828 /* called in transform_ops.c, on each regeneration of keymaps */
829 wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
831 static EnumPropertyItem modal_items[] = {
832 {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
833 {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
834 {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Translate", ""},
835 {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
836 {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
837 {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Invert Snap On", ""},
838 {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Invert Snap Off", ""},
839 {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
840 {TFM_MODAL_AXIS_X, "AXIS_X", 0, "Orientation X axis", ""},
841 {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Orientation Y axis", ""},
842 {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Orientation Z axis", ""},
843 {TFM_MODAL_PLANE_X, "PLANE_X", 0, "Orientation X plane", ""},
844 {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Orientation Y plane", ""},
845 {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Orientation Z plane", ""},
846 {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Remove Constraints", ""},
847 {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
848 {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
849 {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
850 {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
851 {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
852 {TFM_MODAL_PROPSIZE_DOWN, "PROPORTIONAL_SIZE_DOWN", 0, "Decrease Proportional Influence", ""},
853 {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
854 {TFM_MODAL_AUTOIK_LEN_DEC, "AUTOIK_CHAIN_LEN_DOWN", 0, "Decrease Max AutoIK Chain Length", ""},
855 {TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
856 {TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
857 {TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
858 {0, NULL, 0, NULL, NULL}
861 wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
863 /* this function is called for each spacetype, only needs to add map once */
864 if (keymap && keymap->modal_items) return NULL;
866 keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
868 /* items for modal map */
869 WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL);
870 WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
871 WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
872 WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
874 WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE);
875 WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE);
876 WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, TFM_MODAL_RESIZE);
878 WM_modalkeymap_add_item(keymap, TABKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_SNAP_TOGGLE);
880 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
881 WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
883 WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
884 WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
886 WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, TFM_MODAL_ADD_SNAP);
887 WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, KM_ALT, 0, TFM_MODAL_REMOVE_SNAP);
889 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
890 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
891 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP);
892 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN);
893 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
894 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
895 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP);
896 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN);
897 WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, TFM_MODAL_PROPSIZE);
899 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_UP);
900 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_DOWN);
902 WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
903 WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
904 WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
905 WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
910 static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode)
912 if (!(t->flag & T_NO_CONSTRAINT)) {
913 int constraint_axis, constraint_plane;
914 int edit_2d = (t->flag & T_2D_EDIT);
915 const char *msg1 = "", *msg2 = "", *msg3 = "";
921 msg1 = IFACE_("along X");
922 msg2 = IFACE_("along %s X");
923 msg3 = IFACE_("locking %s X");
925 constraint_axis = CON_AXIS0;
928 msg1 = IFACE_("along Y");
929 msg2 = IFACE_("along %s Y");
930 msg3 = IFACE_("locking %s Y");
932 constraint_axis = CON_AXIS1;
935 msg1 = IFACE_("along Z");
936 msg2 = IFACE_("along %s Z");
937 msg3 = IFACE_("locking %s Z");
939 constraint_axis = CON_AXIS2;
945 constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
947 if (edit_2d && (key_type != ZKEY)) {
952 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg1);
957 if (t->con.orientation != V3D_MANIP_GLOBAL) {
961 short orientation = (t->current_orientation != V3D_MANIP_GLOBAL ?
962 t->current_orientation : V3D_MANIP_LOCAL);
963 if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
964 setUserConstraint(t, orientation, constraint_axis, msg2);
965 else if (t->modifiers & MOD_CONSTRAINT_PLANE)
966 setUserConstraint(t, orientation, constraint_plane, msg3);
970 if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
971 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg2);
972 else if (t->modifiers & MOD_CONSTRAINT_PLANE)
973 setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_plane, msg3);
976 t->redraw |= TREDRAW_HARD;
980 int transformEvent(TransInfo *t, const wmEvent *event)
982 char cmode = constraintModeToChar(t);
983 bool handled = false;
984 const int modifiers_prev = t->modifiers;
986 t->redraw |= handleMouseInput(t, &t->mouse, event);
988 /* Handle modal numinput events first, if already activated. */
989 if (((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
990 hasNumInput(&t->num) && handleNumInput(t->context, &(t->num), event))
992 t->redraw |= TREDRAW_HARD;
995 else if (event->type == MOUSEMOVE) {
996 if (t->modifiers & MOD_CONSTRAINT_SELECT)
997 t->con.mode |= CON_SELECT;
999 copy_v2_v2_int(t->mval, event->mval);
1001 // t->redraw |= TREDRAW_SOFT; /* Use this for soft redraw. Might cause flicker in object mode */
1002 t->redraw |= TREDRAW_HARD;
1004 if (t->state == TRANS_STARTING) {
1005 t->state = TRANS_RUNNING;
1008 applyMouseInput(t, &t->mouse, t->mval, t->values);
1010 // Snapping mouse move events
1011 t->redraw |= handleSnapping(t, event);
1014 /* handle modal keymap first */
1015 else if (event->type == EVT_MODAL_MAP) {
1016 switch (event->val) {
1017 case TFM_MODAL_CANCEL:
1018 t->state = TRANS_CANCEL;
1021 case TFM_MODAL_CONFIRM:
1022 t->state = TRANS_CONFIRM;
1025 case TFM_MODAL_TRANSLATE:
1026 /* only switch when... */
1027 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1029 resetTransRestrictions(t);
1030 restoreTransObjects(t);
1032 initSnapping(t, NULL); // need to reinit after mode change
1033 t->redraw |= TREDRAW_HARD;
1034 WM_event_add_mousemove(t->context);
1037 else if (t->mode == TFM_SEQ_SLIDE) {
1038 t->flag ^= T_ALT_TRANSFORM;
1039 t->redraw |= TREDRAW_HARD;
1043 if (t->obedit && t->obedit->type == OB_MESH) {
1044 if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) {
1046 resetTransRestrictions(t);
1047 restoreTransObjects(t);
1049 /* first try edge slide */
1051 /* if that fails, do vertex slide */
1052 if (t->state == TRANS_CANCEL) {
1053 t->state = TRANS_STARTING;
1056 /* vert slide can fail on unconnected vertices (rare but possible) */
1057 if (t->state == TRANS_CANCEL) {
1058 t->state = TRANS_STARTING;
1059 resetTransRestrictions(t);
1060 restoreTransObjects(t);
1063 initSnapping(t, NULL); // need to reinit after mode change
1064 t->redraw |= TREDRAW_HARD;
1066 WM_event_add_mousemove(t->context);
1069 else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
1070 if (t->mode == TFM_TRANSLATION) {
1071 restoreTransObjects(t);
1073 t->flag ^= T_ALT_TRANSFORM;
1074 t->redraw |= TREDRAW_HARD;
1080 case TFM_MODAL_ROTATE:
1081 /* only switch when... */
1082 if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
1083 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1085 resetTransRestrictions(t);
1087 if (t->mode == TFM_ROTATION) {
1088 restoreTransObjects(t);
1092 restoreTransObjects(t);
1095 initSnapping(t, NULL); // need to reinit after mode change
1096 t->redraw |= TREDRAW_HARD;
1101 case TFM_MODAL_RESIZE:
1102 /* only switch when... */
1103 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
1105 /* Scale isn't normally very useful after extrude along normals, see T39756 */
1106 if ((t->con.mode & CON_APPLY) && (t->con.orientation == V3D_MANIP_NORMAL)) {
1111 resetTransRestrictions(t);
1112 restoreTransObjects(t);
1114 initSnapping(t, NULL); // need to reinit after mode change
1115 t->redraw |= TREDRAW_HARD;
1118 else if (t->mode == TFM_SHRINKFATTEN) {
1119 t->flag ^= T_ALT_TRANSFORM;
1120 t->redraw |= TREDRAW_HARD;
1123 else if (t->mode == TFM_RESIZE) {
1124 if (t->options & CTX_MOVIECLIP) {
1125 restoreTransObjects(t);
1127 t->flag ^= T_ALT_TRANSFORM;
1128 t->redraw |= TREDRAW_HARD;
1134 case TFM_MODAL_SNAP_INV_ON:
1135 t->modifiers |= MOD_SNAP_INVERT;
1136 t->redraw |= TREDRAW_HARD;
1139 case TFM_MODAL_SNAP_INV_OFF:
1140 t->modifiers &= ~MOD_SNAP_INVERT;
1141 t->redraw |= TREDRAW_HARD;
1144 case TFM_MODAL_SNAP_TOGGLE:
1145 t->modifiers ^= MOD_SNAP;
1146 t->redraw |= TREDRAW_HARD;
1149 case TFM_MODAL_AXIS_X:
1150 if (!(t->flag & T_NO_CONSTRAINT)) {
1151 transform_event_xyz_constraint(t, XKEY, cmode);
1152 t->redraw |= TREDRAW_HARD;
1156 case TFM_MODAL_AXIS_Y:
1157 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1158 transform_event_xyz_constraint(t, YKEY, cmode);
1159 t->redraw |= TREDRAW_HARD;
1163 case TFM_MODAL_AXIS_Z:
1164 if ((t->flag & (T_NO_CONSTRAINT)) == 0) {
1165 transform_event_xyz_constraint(t, ZKEY, cmode);
1166 t->redraw |= TREDRAW_HARD;
1170 case TFM_MODAL_PLANE_X:
1171 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1176 setUserConstraint(t, t->current_orientation, (CON_AXIS1 | CON_AXIS2), IFACE_("locking %s X"));
1178 t->redraw |= TREDRAW_HARD;
1182 case TFM_MODAL_PLANE_Y:
1183 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1188 setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS2), IFACE_("locking %s Y"));
1190 t->redraw |= TREDRAW_HARD;
1194 case TFM_MODAL_PLANE_Z:
1195 if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1200 setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS1), IFACE_("locking %s Z"));
1202 t->redraw |= TREDRAW_HARD;
1206 case TFM_MODAL_CONS_OFF:
1207 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1209 t->redraw |= TREDRAW_HARD;
1213 case TFM_MODAL_ADD_SNAP:
1215 t->redraw |= TREDRAW_HARD;
1218 case TFM_MODAL_REMOVE_SNAP:
1220 t->redraw |= TREDRAW_HARD;
1223 case TFM_MODAL_PROPSIZE:
1224 /* MOUSEPAN usage... */
1225 if (t->flag & T_PROP_EDIT) {
1226 float fac = 1.0f + 0.005f *(event->y - event->prevy);
1227 t->prop_size *= fac;
1228 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1229 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1230 calculatePropRatio(t);
1231 t->redraw |= TREDRAW_HARD;
1235 case TFM_MODAL_PROPSIZE_UP:
1236 if (t->flag & T_PROP_EDIT) {
1237 t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1238 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1239 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1240 calculatePropRatio(t);
1241 t->redraw |= TREDRAW_HARD;
1245 case TFM_MODAL_PROPSIZE_DOWN:
1246 if (t->flag & T_PROP_EDIT) {
1247 t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1248 calculatePropRatio(t);
1249 t->redraw |= TREDRAW_HARD;
1253 case TFM_MODAL_EDGESLIDE_UP:
1254 case TFM_MODAL_EDGESLIDE_DOWN:
1255 t->redraw |= TREDRAW_HARD;
1258 case TFM_MODAL_AUTOIK_LEN_INC:
1259 if (t->flag & T_AUTOIK) {
1260 transform_autoik_update(t, 1);
1261 t->redraw |= TREDRAW_HARD;
1265 case TFM_MODAL_AUTOIK_LEN_DEC:
1266 if (t->flag & T_AUTOIK) {
1267 transform_autoik_update(t, -1);
1268 t->redraw |= TREDRAW_HARD;
1276 /* else do non-mapped events */
1277 else if (event->val == KM_PRESS) {
1278 switch (event->type) {
1280 t->state = TRANS_CANCEL;
1283 /* enforce redraw of transform when modifiers are used */
1286 t->modifiers |= MOD_CONSTRAINT_PLANE;
1287 t->redraw |= TREDRAW_HARD;
1292 t->state = TRANS_CONFIRM;
1297 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1298 /* exception for switching to dolly, or trackball, in camera view */
1299 if (t->flag & T_CAMERA) {
1300 if (t->mode == TFM_TRANSLATION)
1301 setLocalConstraint(t, (CON_AXIS2), IFACE_("along local Z"));
1302 else if (t->mode == TFM_ROTATION) {
1303 restoreTransObjects(t);
1308 t->modifiers |= MOD_CONSTRAINT_SELECT;
1309 if (t->con.mode & CON_APPLY) {
1314 initSelectConstraint(t, t->spacemtx);
1317 /* bit hackish... but it prevents mmb select to print the orientation from menu */
1319 strcpy(t->spacename, "global");
1321 initSelectConstraint(t, mati);
1323 postSelectConstraint(t);
1326 t->redraw |= TREDRAW_HARD;
1331 t->state = TRANS_CANCEL;
1336 t->state = TRANS_CONFIRM;
1340 /* only switch when... */
1341 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) {
1343 resetTransRestrictions(t);
1344 restoreTransObjects(t);
1346 initSnapping(t, NULL); // need to reinit after mode change
1347 t->redraw |= TREDRAW_HARD;
1352 /* only switch when... */
1353 if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL)) {
1355 resetTransRestrictions(t);
1356 restoreTransObjects(t);
1358 initSnapping(t, NULL); // need to reinit after mode change
1359 t->redraw |= TREDRAW_HARD;
1364 /* only switch when... */
1365 if (!(t->options & CTX_TEXTURE)) {
1366 if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION)) {
1368 resetTransRestrictions(t);
1370 if (t->mode == TFM_ROTATION) {
1371 restoreTransObjects(t);
1375 restoreTransObjects(t);
1378 initSnapping(t, NULL); // need to reinit after mode change
1379 t->redraw |= TREDRAW_HARD;
1386 if (!(t->options & CTX_NO_PET)) {
1387 t->flag ^= T_PROP_CONNECTED;
1388 sort_trans_data_dist(t);
1389 calculatePropRatio(t);
1390 t->redraw = TREDRAW_HARD;
1395 if (!(t->flag & T_NO_CONSTRAINT)) {
1397 t->redraw |= TREDRAW_HARD;
1405 if (!(t->flag & T_NO_CONSTRAINT)) {
1406 transform_event_xyz_constraint(t, event->type, cmode);
1411 if (t->flag & T_PROP_EDIT && event->shift) {
1412 t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1413 calculatePropRatio(t);
1414 t->redraw |= TREDRAW_HARD;
1419 if (event->alt && t->flag & T_PROP_EDIT) {
1420 t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1421 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1422 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1423 calculatePropRatio(t);
1424 t->redraw = TREDRAW_HARD;
1429 case WHEELDOWNMOUSE:
1430 if (t->flag & T_AUTOIK) {
1431 transform_autoik_update(t, 1);
1434 view_editmove(event->type);
1436 t->redraw = TREDRAW_HARD;
1440 if (event->alt && t->flag & T_PROP_EDIT) {
1441 t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1442 calculatePropRatio(t);
1443 t->redraw = TREDRAW_HARD;
1449 if (t->flag & T_AUTOIK) {
1450 transform_autoik_update(t, -1);
1453 view_editmove(event->type);
1455 t->redraw = TREDRAW_HARD;
1460 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1461 t->flag |= T_ALT_TRANSFORM;
1462 t->redraw |= TREDRAW_HARD;
1470 /* Snapping key events */
1471 t->redraw |= handleSnapping(t, event);
1473 else if (event->val == KM_RELEASE) {
1474 switch (event->type) {
1477 t->modifiers &= ~MOD_CONSTRAINT_PLANE;
1478 t->redraw |= TREDRAW_HARD;
1483 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1484 t->modifiers &= ~MOD_CONSTRAINT_SELECT;
1485 postSelectConstraint(t);
1486 t->redraw |= TREDRAW_HARD;
1492 if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1493 t->flag &= ~T_ALT_TRANSFORM;
1494 t->redraw |= TREDRAW_HARD;
1502 /* confirm transform if launch key is released after mouse move */
1503 if (t->flag & T_RELEASE_CONFIRM) {
1504 /* XXX Keyrepeat bug in Xorg messes this up, will test when fixed */
1505 if (event->type == t->launch_event && (t->launch_event == LEFTMOUSE || t->launch_event == RIGHTMOUSE)) {
1506 t->state = TRANS_CONFIRM;
1511 /* if we change snap options, get the unsnapped values back */
1512 if ((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) !=
1513 (modifiers_prev & (MOD_SNAP | MOD_SNAP_INVERT)))
1515 applyMouseInput(t, &t->mouse, t->mval, t->values);
1518 /* Per transform event, if present */
1519 if (t->handleEvent &&
1521 /* Needed for vertex slide, see [#38756] */
1522 (event->type == MOUSEMOVE)))
1524 t->redraw |= t->handleEvent(t, event);
1527 /* Try to init modal numinput now, if possible. */
1528 if (!(handled || t->redraw) && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1529 handleNumInput(t->context, &(t->num), event))
1531 t->redraw |= TREDRAW_HARD;
1535 if (handled || t->redraw) {
1539 return OPERATOR_PASS_THROUGH;
1543 bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
1545 TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
1548 t->state = TRANS_RUNNING;
1550 /* avoid calculating PET */
1551 t->options = CTX_NO_PET;
1553 t->mode = TFM_DUMMY;
1555 initTransInfo(C, t, NULL, NULL);
1557 /* avoid doing connectivity lookups (when V3D_LOCAL is set) */
1558 t->around = V3D_CENTER;
1560 createTransData(C, t); // make TransData structs from selection
1562 t->around = centerMode; // override userdefined mode
1564 if (t->total == 0) {
1573 copy_v2_v2(cent2d, t->center2d);
1577 // Copy center from constraint center. Transform center can be local
1578 copy_v3_v3(cent3d, t->con.center);
1583 /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
1584 special_aftertrans_update(C, t);
1599 static void drawArrow(ArrowDirection d, short offset, short length, short size)
1609 glVertex2s(offset, 0);
1610 glVertex2s(offset + length, 0);
1611 glVertex2s(offset + length, 0);
1612 glVertex2s(offset + length - size, -size);
1613 glVertex2s(offset + length, 0);
1614 glVertex2s(offset + length - size, size);
1625 glVertex2s(0, offset);
1626 glVertex2s(0, offset + length);
1627 glVertex2s(0, offset + length);
1628 glVertex2s(-size, offset + length - size);
1629 glVertex2s(0, offset + length);
1630 glVertex2s(size, offset + length - size);
1636 static void drawArrowHead(ArrowDirection d, short size)
1645 glVertex2s(-size, -size);
1647 glVertex2s(-size, size);
1657 glVertex2s(-size, -size);
1659 glVertex2s(size, -size);
1665 static void drawArc(float size, float angle_start, float angle_end, int segments)
1667 float delta = (angle_end - angle_start) / segments;
1671 glBegin(GL_LINE_STRIP);
1673 for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
1674 glVertex2f(cosf(angle) * size, sinf(angle) * size);
1676 glVertex2f(cosf(angle_end) * size, sinf(angle_end) * size);
1681 static int helpline_poll(bContext *C)
1683 ARegion *ar = CTX_wm_region(C);
1685 if (ar && ar->regiontype == RGN_TYPE_WINDOW)
1690 static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
1692 TransInfo *t = (TransInfo *)customdata;
1694 if (t->helpline != HLP_NONE && !(t->flag & T_USES_MANIPULATOR)) {
1695 float vecrot[3], cent[2];
1701 copy_v3_v3(vecrot, t->center);
1702 if (t->flag & T_EDIT) {
1703 Object *ob = t->obedit;
1704 if (ob) mul_m4_v3(ob->obmat, vecrot);
1706 else if (t->flag & T_POSE) {
1707 Object *ob = t->poseobj;
1708 if (ob) mul_m4_v3(ob->obmat, vecrot);
1711 projectFloatViewEx(t, vecrot, cent, V3D_PROJ_TEST_CLIP_ZERO);
1715 switch (t->helpline) {
1717 UI_ThemeColor(TH_VIEW_OVERLAY);
1720 glBegin(GL_LINE_STRIP);
1721 glVertex2iv(t->mval);
1725 glTranslatef(mval[0], mval[1], 0);
1726 glRotatef(-RAD2DEGF(atan2f(cent[0] - t->mval[0], cent[1] - t->mval[1])), 0, 0, 1);
1730 drawArrow(UP, 5, 10, 5);
1731 drawArrow(DOWN, 5, 10, 5);
1735 UI_ThemeColor(TH_VIEW_OVERLAY);
1737 glTranslatef(mval[0], mval[1], 0);
1740 drawArrow(RIGHT, 5, 10, 5);
1741 drawArrow(LEFT, 5, 10, 5);
1745 UI_ThemeColor(TH_VIEW_OVERLAY);
1747 glTranslatef(mval[0], mval[1], 0);
1750 drawArrow(UP, 5, 10, 5);
1751 drawArrow(DOWN, 5, 10, 5);
1756 float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1];
1757 float angle = atan2f(dy, dx);
1758 float dist = hypotf(dx, dy);
1759 float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
1760 float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
1761 UI_ThemeColor(TH_VIEW_OVERLAY);
1764 glBegin(GL_LINE_STRIP);
1765 glVertex2iv(t->mval);
1769 glTranslatef(cent[0] - t->mval[0] + mval[0], cent[1] - t->mval[1] + mval[1], 0);
1773 drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
1774 drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
1778 glTranslatef(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
1779 glRotatef(RAD2DEGF(angle - delta_angle), 0, 0, 1);
1781 drawArrowHead(DOWN, 5);
1785 glTranslatef(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
1786 glRotatef(RAD2DEGF(angle + delta_angle), 0, 0, 1);
1788 drawArrowHead(UP, 5);
1795 unsigned char col[3], col2[3];
1796 UI_GetThemeColor3ubv(TH_GRID, col);
1798 glTranslatef(mval[0], mval[1], 0);
1802 UI_make_axis_color(col, col2, 'X');
1803 glColor3ubv((GLubyte *)col2);
1805 drawArrow(RIGHT, 5, 10, 5);
1806 drawArrow(LEFT, 5, 10, 5);
1808 UI_make_axis_color(col, col2, 'Y');
1809 glColor3ubv((GLubyte *)col2);
1811 drawArrow(UP, 5, 10, 5);
1812 drawArrow(DOWN, 5, 10, 5);
1822 static void drawTransformView(const struct bContext *C, ARegion *UNUSED(ar), void *arg)
1827 drawPropCircle(C, t);
1830 /* edge slide, vert slide */
1831 drawEdgeSlide(C, t);
1832 drawVertSlide(C, t);
1835 /* just draw a little warning message in the top-right corner of the viewport to warn that autokeying is enabled */
1836 static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar)
1839 const char *printable = IFACE_("Auto Keying On");
1840 float printable_size[2];
1843 ED_region_visible_rect(ar, &rect);
1845 BLF_width_and_height_default(printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
1847 xco = (rect.xmax - U.widget_unit) - (int)printable_size[0];
1848 yco = (rect.ymax - U.widget_unit);
1850 /* warning text (to clarify meaning of overlays)
1851 * - original color was red to match the icon, but that clashes badly with a less nasty border
1853 UI_ThemeColorShade(TH_TEXT_HI, -50);
1854 #ifdef WITH_INTERNATIONAL
1855 BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1857 BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1860 /* autokey recording icon... */
1861 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1864 xco -= U.widget_unit;
1865 yco -= (int)printable_size[1] / 2;
1867 UI_icon_draw(xco, yco, ICON_REC);
1869 glDisable(GL_BLEND);
1872 static void drawTransformPixel(const struct bContext *UNUSED(C), ARegion *ar, void *arg)
1875 Scene *scene = t->scene;
1878 /* draw autokeyframing hint in the corner
1879 * - only draw if enabled (advanced users may be distracted/annoyed),
1880 * for objects that will be autokeyframed (no point ohterwise),
1881 * AND only for the active region (as showing all is too overwhelming)
1883 if ((U.autokey_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
1885 if (t->flag & (T_OBJECT | T_POSE)) {
1886 if (ob && autokeyframe_cfra_can_key(scene, &ob->id)) {
1887 drawAutoKeyWarning(t, ar);
1894 void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
1896 ToolSettings *ts = CTX_data_tool_settings(C);
1897 int constraint_axis[3] = {0, 0, 0};
1898 int proportional = 0;
1901 // Save back mode in case we're in the generic operator
1902 if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
1903 RNA_property_enum_set(op->ptr, prop, t->mode);
1906 if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
1907 const float *values = (t->flag & T_AUTOVALUES) ? t->auto_values : t->values;
1908 if (RNA_property_array_check(prop)) {
1909 RNA_property_float_set_array(op->ptr, prop, values);
1912 RNA_property_float_set(op->ptr, prop, values[0]);
1916 /* convert flag to enum */
1917 switch (t->flag & T_PROP_EDIT_ALL) {
1919 proportional = PROP_EDIT_ON;
1921 case (T_PROP_EDIT | T_PROP_CONNECTED):
1922 proportional = PROP_EDIT_CONNECTED;
1924 case (T_PROP_EDIT | T_PROP_PROJECTED):
1925 proportional = PROP_EDIT_PROJECTED;
1928 proportional = PROP_EDIT_OFF;
1932 // If modal, save settings back in scene if not set as operator argument
1933 if (t->flag & T_MODAL) {
1934 /* save settings if not set in operator */
1936 /* skip saving proportional edit if it was not actually used */
1937 if (!(t->options & CTX_NO_PET)) {
1938 if ((prop = RNA_struct_find_property(op->ptr, "proportional")) &&
1939 !RNA_property_is_set(op->ptr, prop))
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 !RNA_property_is_set(op->ptr, prop))
1952 ts->proportional_size = t->prop_size;
1955 if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
1956 !RNA_property_is_set(op->ptr, prop))
1958 ts->prop_mode = t->prop_mode;
1962 /* do we check for parameter? */
1963 if (t->modifiers & MOD_SNAP) {
1964 ts->snap_flag |= SCE_SNAP;
1967 ts->snap_flag &= ~SCE_SNAP;
1970 if (t->spacetype == SPACE_VIEW3D) {
1971 if ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) &&
1972 !RNA_property_is_set(op->ptr, prop))
1974 View3D *v3d = t->view;
1976 v3d->twmode = t->current_orientation;
1981 if ((prop = RNA_struct_find_property(op->ptr, "proportional"))) {
1982 RNA_property_enum_set(op->ptr, prop, proportional);
1983 RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
1984 RNA_float_set(op->ptr, "proportional_size", t->prop_size);
1987 if ((prop = RNA_struct_find_property(op->ptr, "axis"))) {
1988 RNA_property_float_set_array(op->ptr, prop, t->axis);
1991 if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
1992 RNA_property_boolean_set(op->ptr, prop, t->flag & T_MIRROR);
1995 if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
1996 /* constraint orientation can be global, event if user selects something else
1997 * so use the orientation in the constraint if set
1999 if (t->con.mode & CON_APPLY) {
2000 RNA_enum_set(op->ptr, "constraint_orientation", t->con.orientation);
2003 RNA_enum_set(op->ptr, "constraint_orientation", t->current_orientation);
2006 if (t->con.mode & CON_APPLY) {
2007 if (t->con.mode & CON_AXIS0) {
2008 constraint_axis[0] = 1;
2010 if (t->con.mode & CON_AXIS1) {
2011 constraint_axis[1] = 1;
2013 if (t->con.mode & CON_AXIS2) {
2014 constraint_axis[2] = 1;
2018 RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
2022 /* note: caller needs to free 't' on a 0 return */
2023 bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
2030 /* added initialize, for external calls to set stuff in TransInfo, like undo string */
2032 t->state = TRANS_STARTING;
2034 if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) && RNA_property_is_set(op->ptr, prop)) {
2035 if (RNA_property_boolean_get(op->ptr, prop)) {
2036 options |= CTX_TEXTURE;
2040 if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) && RNA_property_is_set(op->ptr, prop)) {
2041 if (RNA_property_boolean_get(op->ptr, prop)) {
2042 options |= CTX_GPENCIL_STROKES;
2046 t->options = options;
2050 t->launch_event = event ? event->type : -1;
2052 if (t->launch_event == EVT_TWEAK_R) {
2053 t->launch_event = RIGHTMOUSE;
2055 else if (t->launch_event == EVT_TWEAK_L) {
2056 t->launch_event = LEFTMOUSE;
2059 // XXX Remove this when wm_operator_call_internal doesn't use window->eventstate (which can have type = 0)
2060 // For manipulator only, so assume LEFTMOUSE
2061 if (t->launch_event == 0) {
2062 t->launch_event = LEFTMOUSE;
2065 initTransInfo(C, t, op, event);
2067 if (t->spacetype == SPACE_VIEW3D) {
2068 initTransformOrientation(C, t);
2070 t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
2071 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2072 t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2073 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2075 else if (t->spacetype == SPACE_IMAGE) {
2076 unit_m3(t->spacemtx);
2077 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2078 //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
2079 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2081 else if (t->spacetype == SPACE_CLIP) {
2082 unit_m3(t->spacemtx);
2083 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2084 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2086 else if (t->spacetype == SPACE_NODE) {
2087 unit_m3(t->spacemtx);
2088 /*t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);*/
2089 t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
2090 t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
2093 unit_m3(t->spacemtx);
2095 createTransData(C, t); // make TransData structs from selection
2097 if (t->total == 0) {
2103 /* keymap for shortcut header prints */
2104 t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
2106 /* Stupid code to have Ctrl-Click on manipulator work ok
2108 * do this only for translation/rotation/resize due to only this
2109 * moded are available from manipulator and doing such check could
2110 * lead to keymap conflicts for other modes (see #31584)
2112 if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
2115 for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) {
2116 if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
2117 if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) && event->ctrl) ||
2118 (ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
2119 (ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) && event->alt) ||
2120 ((kmi->type == OSKEY) && event->oskey) )
2122 t->modifiers |= MOD_SNAP_INVERT;
2130 initSnapping(t, op); // Initialize snapping data AFTER mode flags
2132 /* EVIL! posemode code can switch translation to rotate when 1 bone is selected. will be removed (ton) */
2133 /* EVIL2: we gave as argument also texture space context bit... was cleared */
2134 /* EVIL3: extend mode for animation editors also switches modes... but is best way to avoid duplicate code */
2137 calculatePropRatio(t);
2140 initMouseInput(t, &t->mouse, t->center2d, t->imval);
2143 case TFM_TRANSLATION:
2152 case TFM_SKIN_RESIZE:
2164 case TFM_SHRINKFATTEN:
2165 initShrinkFatten(t);
2170 case TFM_CURVE_SHRINKFATTEN:
2171 initCurveShrinkFatten(t);
2173 case TFM_MASK_SHRINKFATTEN:
2174 initMaskShrinkFatten(t);
2176 case TFM_GPENCIL_SHRINKFATTEN:
2177 initGPShrinkFatten(t);
2189 { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
2190 bArmature *arm = t->poseobj->data;
2191 if (arm->drawtype == ARM_ENVELOPE)
2192 initBoneEnvelope(t);
2197 case TFM_BONE_ENVELOPE:
2198 initBoneEnvelope(t);
2200 case TFM_EDGE_SLIDE:
2203 case TFM_VERT_SLIDE:
2209 case TFM_TIME_TRANSLATE:
2210 initTimeTranslate(t);
2212 case TFM_TIME_SLIDE:
2215 case TFM_TIME_SCALE:
2218 case TFM_TIME_DUPLICATE:
2219 /* same as TFM_TIME_EXTEND, but we need the mode info for later
2220 * so that duplicate-culling will work properly
2222 if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2225 initTimeTranslate(t);
2228 case TFM_TIME_EXTEND:
2229 /* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
2230 * Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
2231 * (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
2232 * depending on which editor this was called from
2234 if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2237 initTimeTranslate(t);
2256 if (t->state == TRANS_CANCEL) {
2262 /* overwrite initial values if operator supplied a non-null vector */
2263 if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
2264 float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory */
2266 if (RNA_property_array_check(prop)) {
2267 RNA_float_get_array(op->ptr, "value", values);
2270 values[0] = RNA_float_get(op->ptr, "value");
2273 copy_v4_v4(t->values, values);
2274 copy_v4_v4(t->auto_values, values);
2275 t->flag |= T_AUTOVALUES;
2278 /* Transformation axis from operator */
2279 if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) {
2280 RNA_property_float_get_array(op->ptr, prop, t->axis);
2281 normalize_v3(t->axis);
2282 copy_v3_v3(t->axis_orig, t->axis);
2285 /* Constraint init from operator */
2286 if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) {
2287 int constraint_axis[3];
2289 RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
2291 if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
2292 t->con.mode |= CON_APPLY;
2294 if (constraint_axis[0]) {
2295 t->con.mode |= CON_AXIS0;
2297 if (constraint_axis[1]) {
2298 t->con.mode |= CON_AXIS1;
2300 if (constraint_axis[2]) {
2301 t->con.mode |= CON_AXIS2;
2304 setUserConstraint(t, t->current_orientation, t->con.mode, "%s");
2313 void transformApply(bContext *C, TransInfo *t)
2317 if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
2318 selectConstraint(t);
2320 t->transform(t, t->mval); // calls recalcData()
2321 viewRedrawForce(C, t);
2323 t->redraw = TREDRAW_NOTHING;
2325 else if (t->redraw & TREDRAW_SOFT) {
2326 viewRedrawForce(C, t);
2329 /* If auto confirm is on, break after one pass */
2330 if (t->options & CTX_AUTOCONFIRM) {
2331 t->state = TRANS_CONFIRM;
2337 static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
2341 if (t->redraw & TREDRAW_SOFT) {
2342 t->redraw |= TREDRAW_HARD;
2343 transformApply((bContext *)C, t);
2347 int transformEnd(bContext *C, TransInfo *t)
2349 int exit_code = OPERATOR_RUNNING_MODAL;
2353 if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
2354 /* handle restoring objects */
2355 if (t->state == TRANS_CANCEL) {
2356 /* exception, edge slide transformed UVs too */
2357 if (t->mode == TFM_EDGE_SLIDE) {
2358 doEdgeSlide(t, 0.0f);
2360 else if (t->mode == TFM_VERT_SLIDE) {
2361 doVertSlide(t, 0.0f);
2364 exit_code = OPERATOR_CANCELLED;
2365 restoreTransObjects(t); // calls recalcData()
2368 exit_code = OPERATOR_FINISHED;
2371 /* aftertrans does insert keyframes, and clears base flags; doesn't read transdata */
2372 special_aftertrans_update(C, t);
2377 /* send events out for redraws */
2378 viewRedrawPost(C, t);
2380 viewRedrawForce(C, t);
2388 /* ************************** TRANSFORM LOCKS **************************** */
2390 static void protectedTransBits(short protectflag, float vec[3])
2392 if (protectflag & OB_LOCK_LOCX)
2394 if (protectflag & OB_LOCK_LOCY)
2396 if (protectflag & OB_LOCK_LOCZ)
2400 static void protectedSizeBits(short protectflag, float size[3])
2402 if (protectflag & OB_LOCK_SCALEX)
2404 if (protectflag & OB_LOCK_SCALEY)
2406 if (protectflag & OB_LOCK_SCALEZ)
2410 static void protectedRotateBits(short protectflag, float eul[3], const float oldeul[3])
2412 if (protectflag & OB_LOCK_ROTX)
2414 if (protectflag & OB_LOCK_ROTY)
2416 if (protectflag & OB_LOCK_ROTZ)
2421 /* this function only does the delta rotation */
2422 /* axis-angle is usually internally stored as quats... */
2423 static void protectedAxisAngleBits(short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle)
2425 /* check that protection flags are set */
2426 if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2429 if (protectflag & OB_LOCK_ROT4D) {
2430 /* axis-angle getting limited as 4D entities that they are... */
2431 if (protectflag & OB_LOCK_ROTW)
2433 if (protectflag & OB_LOCK_ROTX)
2434 axis[0] = oldAxis[0];
2435 if (protectflag & OB_LOCK_ROTY)
2436 axis[1] = oldAxis[1];
2437 if (protectflag & OB_LOCK_ROTZ)
2438 axis[2] = oldAxis[2];
2441 /* axis-angle get limited with euler... */
2442 float eul[3], oldeul[3];
2444 axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle);
2445 axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle);
2447 if (protectflag & OB_LOCK_ROTX)
2449 if (protectflag & OB_LOCK_ROTY)
2451 if (protectflag & OB_LOCK_ROTZ)
2454 eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT);
2456 /* when converting to axis-angle, we need a special exception for the case when there is no axis */
2457 if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) {
2458 /* for now, rotate around y-axis then (so that it simply becomes the roll) */
2464 /* this function only does the delta rotation */
2465 static void protectedQuaternionBits(short protectflag, float quat[4], const float oldquat[4])
2467 /* check that protection flags are set */
2468 if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2471 if (protectflag & OB_LOCK_ROT4D) {
2472 /* quaternions getting limited as 4D entities that they are... */
2473 if (protectflag & OB_LOCK_ROTW)
2474 quat[0] = oldquat[0];
2475 if (protectflag & OB_LOCK_ROTX)
2476 quat[1] = oldquat[1];
2477 if (protectflag & OB_LOCK_ROTY)
2478 quat[2] = oldquat[2];
2479 if (protectflag & OB_LOCK_ROTZ)
2480 quat[3] = oldquat[3];
2483 /* quaternions get limited with euler... (compatibility mode) */
2484 float eul[3], oldeul[3], nquat[4], noldquat[4];
2487 qlen = normalize_qt_qt(nquat, quat);
2488 normalize_qt_qt(noldquat, oldquat);
2490 quat_to_eul(eul, nquat);
2491 quat_to_eul(oldeul, noldquat);
2493 if (protectflag & OB_LOCK_ROTX)
2495 if (protectflag & OB_LOCK_ROTY)
2497 if (protectflag & OB_LOCK_ROTZ)
2500 eul_to_quat(quat, eul);
2502 /* restore original quat size */
2503 mul_qt_fl(quat, qlen);
2505 /* quaternions flip w sign to accumulate rotations correctly */
2506 if ((nquat[0] < 0.0f && quat[0] > 0.0f) ||
2507 (nquat[0] > 0.0f && quat[0] < 0.0f))
2509 mul_qt_fl(quat, -1.0f);
2514 /* ******************* TRANSFORM LIMITS ********************** */
2516 static void constraintTransLim(TransInfo *t, TransData *td)
2519 bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_LOCLIMIT);
2520 bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_DISTLIMIT);
2522 bConstraintOb cob = {NULL};
2524 float ctime = (float)(t->scene->r.cfra);
2526 /* Make a temporary bConstraintOb for using these limit constraints
2527 * - they only care that cob->matrix is correctly set ;-)
2528 * - current space should be local
2530 unit_m4(cob.matrix);
2531 copy_v3_v3(cob.matrix[3], td->loc);
2533 /* Evaluate valid constraints */
2534 for (con = td->con; con; con = con->next) {
2535 bConstraintTypeInfo *cti = NULL;
2536 ListBase targets = {NULL, NULL};
2538 /* only consider constraint if enabled */
2539 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2540 if (con->enforce == 0.0f) continue;
2542 /* only use it if it's tagged for this purpose (and the right type) */
2543 if (con->type == CONSTRAINT_TYPE_LOCLIMIT) {
2544 bLocLimitConstraint *data = con->data;
2546 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2550 else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) {
2551 bDistLimitConstraint *data = con->data;
2553 if ((data->flag & LIMITDIST_TRANSFORM) == 0)
2559 /* do space conversions */
2560 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2561 /* just multiply by td->mtx (this should be ok) */
2562 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2564 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2565 /* skip... incompatable spacetype */
2569 /* get constraint targets if needed */
2570 BKE_constraint_targets_for_solving_get(con, &cob, &targets, ctime);
2573 cti->evaluate_constraint(con, &cob, &targets);
2575 /* convert spaces again */
2576 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2577 /* just multiply by td->smtx (this should be ok) */
2578 mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2581 /* free targets list */
2582 BLI_freelistN(&targets);
2586 /* copy results from cob->matrix */
2587 copy_v3_v3(td->loc, cob.matrix[3]);
2591 static void constraintob_from_transdata(bConstraintOb *cob, TransData *td)
2593 /* Make a temporary bConstraintOb for use by limit constraints
2594 * - they only care that cob->matrix is correctly set ;-)
2595 * - current space should be local
2597 memset(cob, 0, sizeof(bConstraintOb));
2599 if (td->ext->rotOrder == ROT_MODE_QUAT) {
2601 /* objects and bones do normalization first too, otherwise
2602 * we don't necessarily end up with a rotation matrix, and
2603 * then conversion back to quat gives a different result */
2605 normalize_qt_qt(quat, td->ext->quat);
2606 quat_to_mat4(cob->matrix, quat);
2608 else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2610 axis_angle_to_mat4(cob->matrix, &td->ext->quat[1], td->ext->quat[0]);
2614 eulO_to_mat4(cob->matrix, td->ext->rot, td->ext->rotOrder);
2619 static void constraintRotLim(TransInfo *UNUSED(t), TransData *td)
2622 bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_ROTLIMIT);
2625 bool do_limit = false;
2627 /* Evaluate valid constraints */
2628 for (con = td->con; con; con = con->next) {
2629 /* only consider constraint if enabled */
2630 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2631 if (con->enforce == 0.0f) continue;
2633 /* we're only interested in Limit-Rotation constraints */
2634 if (con->type == CONSTRAINT_TYPE_ROTLIMIT) {
2635 bRotLimitConstraint *data = con->data;
2637 /* only use it if it's tagged for this purpose */
2638 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2641 /* skip incompatable spacetypes */
2642 if (!ELEM(con->ownspace, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL))
2645 /* only do conversion if necessary, to preserve quats and eulers */
2646 if (do_limit == false) {
2647 constraintob_from_transdata(&cob, td);
2651 /* do space conversions */
2652 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2653 /* just multiply by td->mtx (this should be ok) */
2654 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2658 cti->evaluate_constraint(con, &cob, NULL);
2660 /* convert spaces again */
2661 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2662 /* just multiply by td->smtx (this should be ok) */
2663 mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2669 /* copy results from cob->matrix */
2670 if (td->ext->rotOrder == ROT_MODE_QUAT) {
2672 mat4_to_quat(td->ext->quat, cob.matrix);
2674 else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2676 mat4_to_axis_angle(&td->ext->quat[1], &td->ext->quat[0], cob.matrix);
2680 mat4_to_eulO(td->ext->rot, td->ext->rotOrder, cob.matrix);
2686 static void constraintSizeLim(TransInfo *t, TransData *td)
2688 if (td->con && td->ext) {
2689 bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_SIZELIMIT);
2690 bConstraintOb cob = {NULL};
2692 float size_sign[3], size_abs[3];
2695 /* Make a temporary bConstraintOb for using these limit constraints
2696 * - they only care that cob->matrix is correctly set ;-)
2697 * - current space should be local
2699 if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2700 /* scale val and reset size */
2701 return; // TODO: fix this case
2704 /* Reset val if SINGLESIZE but using a constraint */
2705 if (td->flag & TD_SINGLESIZE)
2708 /* separate out sign to apply back later */
2709 for (i = 0; i < 3; i++) {
2710 size_sign[i] = signf(td->ext->size[i]);
2711 size_abs[i] = fabsf(td->ext->size[i]);
2714 size_to_mat4(cob.matrix, size_abs);
2717 /* Evaluate valid constraints */
2718 for (con = td->con; con; con = con->next) {
2719 /* only consider constraint if enabled */
2720 if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2721 if (con->enforce == 0.0f) continue;
2723 /* we're only interested in Limit-Scale constraints */
2724 if (con->type == CONSTRAINT_TYPE_SIZELIMIT) {
2725 bSizeLimitConstraint *data = con->data;
2727 /* only use it if it's tagged for this purpose */
2728 if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2731 /* do space conversions */
2732 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2733 /* just multiply by td->mtx (this should be ok) */
2734 mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2736 else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2737 /* skip... incompatible spacetype */
2742 cti->evaluate_constraint(con, &cob, NULL);
2744 /* convert spaces again */
2745 if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2746 /* just multiply by td->smtx (this should be ok) */
2747 mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2752 /* copy results from cob->matrix */
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 /* extrace scale from matrix and apply back sign */
2763 mat4_to_size(td->ext->size, cob.matrix);
2764 mul_v3_v3(td->ext->size, size_sign);
2770 /* -------------------------------------------------------------------- */
2771 /* Transform (Bend) */
2773 /** \name Transform Bend
2776 struct BendCustomData {
2783 /* for applying the mouse distance */
2784 float warp_init_dist;
2787 static void initBend(TransInfo *t)
2789 const float mval_fl[2] = {UNPACK2(t->mval)};