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
44
#include "MEM_guardedalloc.h"
46
#include "DNA_anim_types.h"
47
#include "DNA_armature_types.h"
48
#include "DNA_constraint_types.h"
49
#include "DNA_mesh_types.h"
50
#include "DNA_meshdata_types.h"
51
#include "DNA_mask_types.h"
52
#include "DNA_movieclip_types.h"
53
#include "DNA_scene_types.h" /* PET modes */
55
#include "BLI_utildefines.h"
58
#include "BLI_listbase.h"
59
#include "BLI_string.h"
60
#include "BLI_ghash.h"
61
#include "BLI_linklist.h"
62
#include "BLI_smallhash.h"
65
#include "BKE_bmesh.h"
66
#include "BKE_context.h"
67
#include "BKE_constraint.h"
68
#include "BKE_global.h"
69
#include "BKE_particle.h"
70
#include "BKE_pointcache.h"
75
#include "BIF_glutil.h"
78
#include "ED_keyframing.h"
79
#include "ED_screen.h"
80
#include "ED_space_api.h"
81
#include "ED_markers.h"
82
#include "ED_view3d.h"
90
#include "UI_view2d.h"
91
#include "UI_interface_icons.h"
92
#include "UI_resources.h"
94
#include "RNA_access.h"
97
#include "BLF_translation.h"
99
#include "transform.h"
101
static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg);
102
static int doEdgeSlide(TransInfo *t, float perc);
103
static int doVertSlide(TransInfo *t, float perc);
105
static void drawEdgeSlide(const struct bContext *C, TransInfo *t);
106
static void drawVertSlide(const struct bContext *C, TransInfo *t);
108
static bool transdata_check_local_center(TransInfo *t)
110
return ((t->around == V3D_LOCAL) && (
111
(t->flag & (T_OBJECT | T_POSE)) ||
112
(t->obedit && t->obedit->type == OB_MESH && (t->settings->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_FACE))) ||
113
(t->obedit && t->obedit->type == OB_ARMATURE) ||
114
(t->spacetype == SPACE_IPO))
118
/* ************************** SPACE DEPENDANT CODE **************************** */
120
void setTransformViewMatrices(TransInfo *t)
122
if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) {
123
RegionView3D *rv3d = t->ar->regiondata;
125
copy_m4_m4(t->viewmat, rv3d->viewmat);
126
copy_m4_m4(t->viewinv, rv3d->viewinv);
127
copy_m4_m4(t->persmat, rv3d->persmat);
128
copy_m4_m4(t->persinv, rv3d->persinv);
129
t->persp = rv3d->persp;
136
t->persp = RV3D_ORTHO;
139
calculateCenter2D(t);
142
static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
146
divx = BLI_rcti_size_x(&v2d->mask);
147
divy = BLI_rcti_size_y(&v2d->mask);
149
r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
150
r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
154
static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
159
divx = BLI_rcti_size_x(&v2d->mask);
160
divy = BLI_rcti_size_y(&v2d->mask);
162
mulx = BLI_rctf_size_x(&v2d->cur);
163
muly = BLI_rctf_size_y(&v2d->cur);
165
/* difference with convertViewVec2D */
166
/* clamp w/h, mask only */
167
if (mulx / divx < muly / divy) {
177
r_vec[0] = mulx * dx / divx;
178
r_vec[1] = muly * dy / divy;
182
void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy)
184
if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) {
185
const float mval_f[2] = {(float)dx, (float)dy};
186
ED_view3d_win_to_delta(t->ar, mval_f, r_vec);
188
else if (t->spacetype == SPACE_IMAGE) {
191
if (t->options & CTX_MASK) {
193
convertViewVec2D_mask(t->view, r_vec, dx, dy);
194
ED_space_image_get_aspect(t->sa->spacedata.first, &aspx, &aspy);
197
convertViewVec2D(t->view, r_vec, dx, dy);
198
ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
204
else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
205
convertViewVec2D(t->view, r_vec, dx, dy);
207
else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
208
convertViewVec2D(&t->ar->v2d, r_vec, dx, dy);
210
else if (t->spacetype == SPACE_CLIP) {
213
if (t->options & CTX_MASK) {
214
convertViewVec2D_mask(t->view, r_vec, dx, dy);
217
convertViewVec2D(t->view, r_vec, dx, dy);
220
if (t->options & CTX_MOVIECLIP) {
221
ED_space_clip_get_aspect_dimension_aware(t->sa->spacedata.first, &aspx, &aspy);
223
else if (t->options & CTX_MASK) {
224
/* TODO - NOT WORKING, this isnt so bad since its only display aspect */
225
ED_space_clip_get_aspect(t->sa->spacedata.first, &aspx, &aspy);
228
/* should never happen, quiet warnings */
237
printf("%s: called in an invalid context\n", __func__);
242
void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
244
if (t->spacetype == SPACE_VIEW3D) {
245
if (t->ar->regiontype == RGN_TYPE_WINDOW) {
246
if (ED_view3d_project_int_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
247
adr[0] = (int)2140000000.0f; /* this is what was done in 2.64, perhaps we can be smarter? */
248
adr[1] = (int)2140000000.0f;
252
else if (t->spacetype == SPACE_IMAGE) {
253
SpaceImage *sima = t->sa->spacedata.first;
255
if (t->options & CTX_MASK) {
256
/* not working quite right, TODO (see below too) */
260
ED_space_image_get_aspect(sima, &aspx, &aspy);
267
BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
272
ED_image_point_pos__reverse(sima, t->ar, v, v);
278
float aspx, aspy, v[2];
280
ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
281
v[0] = vec[0] / aspx;
282
v[1] = vec[1] / aspy;
284
UI_view2d_to_region_no_clip(t->view, v[0], v[1], adr, adr + 1);
287
else if (t->spacetype == SPACE_ACTION) {
290
SpaceAction *sact = t->sa->spacedata.first;
292
if (sact->flag & SACTION_DRAWTIME) {
293
//vec[0] = vec[0]/((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
295
UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], out, out + 1);
300
UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], out, out + 1);
306
else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) {
309
UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], out, out + 1);
313
else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work */
316
UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], out, out + 1);
320
else if (t->spacetype == SPACE_CLIP) {
321
SpaceClip *sc = t->sa->spacedata.first;
323
if (t->options & CTX_MASK) {
324
/* not working quite right, TODO (see above too) */
328
ED_space_clip_get_aspect(sc, &aspx, &aspy);
335
BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v);
340
ED_clip_point_stable_pos__reverse(sc, t->ar, v, v);
345
else if (t->options & CTX_MOVIECLIP) {
346
float v[2], aspx, aspy;
349
ED_space_clip_get_aspect_dimension_aware(t->sa->spacedata.first, &aspx, &aspy);
354
UI_view2d_to_region_no_clip(t->view, v[0], v[1], adr, adr + 1);
360
else if (t->spacetype == SPACE_NODE) {
361
UI_view2d_to_region_no_clip((View2D *)t->view, vec[0], vec[1], adr, adr + 1);
364
void projectIntView(TransInfo *t, const float vec[3], int adr[2])
366
projectIntViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
369
void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
371
switch (t->spacetype) {
374
if (t->ar->regiontype == RGN_TYPE_WINDOW) {
375
/* allow points behind the view [#33643] */
376
if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) {
377
/* XXX, 2.64 and prior did this, weak! */
378
adr[0] = t->ar->winx / 2.0f;
379
adr[1] = t->ar->winy / 2.0f;
391
projectIntView(t, vec, a);
400
void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
402
projectFloatViewEx(t, vec, adr, V3D_PROJ_TEST_NOP);
405
void applyAspectRatio(TransInfo *t, float vec[2])
407
if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
408
SpaceImage *sima = t->sa->spacedata.first;
411
if ((sima->flag & SI_COORDFLOATS) == 0) {
413
ED_space_image_get_size(sima, &width, &height);
419
ED_space_image_get_uv_aspect(sima, &aspx, &aspy);
423
else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
424
if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
425
SpaceClip *sc = t->sa->spacedata.first;
429
if (t->options & CTX_MOVIECLIP) {
430
ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy);
435
else if (t->options & CTX_MASK) {
436
ED_space_clip_get_aspect(sc, &aspx, &aspy);
445
void removeAspectRatio(TransInfo *t, float vec[2])
447
if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
448
SpaceImage *sima = t->sa->spacedata.first;
451
if ((sima->flag & SI_COORDFLOATS) == 0) {
453
ED_space_image_get_size(sima, &width, &height);
459
ED_space_image_get_uv_aspect(sima, &aspx, &aspy);
463
else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
464
if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
465
SpaceClip *sc = t->sa->spacedata.first;
466
float aspx = 1.0f, aspy = 1.0f;
468
if (t->options & CTX_MOVIECLIP) {
469
ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy);
471
else if (t->options & CTX_MASK) {
472
ED_space_clip_get_aspect(sc, &aspx, &aspy);
481
static void viewRedrawForce(const bContext *C, TransInfo *t)
483
if (t->spacetype == SPACE_VIEW3D) {
484
/* Do we need more refined tags? */
485
if (t->flag & T_POSE)
486
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
488
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
490
if (t->mode == TFM_EDGE_SLIDE && (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT))
491
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
493
/* for realtime animation record - send notifiers recognised by animation editors */
494
// XXX: is this notifier a lame duck?
495
if ((t->animtimer) && IS_AUTOKEY_ON(t->scene))
496
WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL);
499
else if (t->spacetype == SPACE_ACTION) {
500
//SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
501
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
503
else if (t->spacetype == SPACE_IPO) {
504
//SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
505
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
507
else if (t->spacetype == SPACE_NLA) {
508
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL);
510
else if (t->spacetype == SPACE_NODE) {
511
//ED_area_tag_redraw(t->sa);
512
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
514
else if (t->spacetype == SPACE_SEQ) {
515
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
517
else if (t->spacetype == SPACE_IMAGE) {
518
if (t->options & CTX_MASK) {
519
Mask *mask = CTX_data_edit_mask(C);
521
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
524
// XXX how to deal with lock?
525
SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first;
526
if (sima->lock) WM_event_add_notifier(C, NC_GEOM | ND_DATA, t->obedit->data);
527
else ED_area_tag_redraw(t->sa);
530
else if (t->spacetype == SPACE_CLIP) {
531
SpaceClip *sc = (SpaceClip *)t->sa->spacedata.first;
533
if (ED_space_clip_check_show_trackedit(sc)) {
534
MovieClip *clip = ED_space_clip_get_clip(sc);
536
/* objects could be parented to tracking data, so send this for viewport refresh */
537
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
539
WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip);
541
else if (ED_space_clip_check_show_maskedit(sc)) {
542
Mask *mask = CTX_data_edit_mask(C);
544
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
549
static void viewRedrawPost(bContext *C, TransInfo *t)
551
ED_area_headerprint(t->sa, NULL);
553
if (t->spacetype == SPACE_VIEW3D) {
554
/* if autokeying is enabled, send notifiers that keyframes were added */
555
if (IS_AUTOKEY_ON(t->scene))
556
WM_main_add_notifier(NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
558
/* redraw UV editor */
559
if (t->mode == TFM_EDGE_SLIDE && (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT))
560
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
562
/* XXX temp, first hack to get auto-render in compositor work (ton) */
563
WM_event_add_notifier(C, NC_SCENE | ND_TRANSFORM_DONE, CTX_data_scene(C));
567
#if 0 // TRANSFORM_FIX_ME
568
if (t->spacetype == SPACE_VIEW3D) {
569
allqueue(REDRAWBUTSOBJECT, 0);
570
allqueue(REDRAWVIEW3D, 0);
572
else if (t->spacetype == SPACE_IMAGE) {
573
allqueue(REDRAWIMAGE, 0);
574
allqueue(REDRAWVIEW3D, 0);
576
else if (ELEM3(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) {
577
allqueue(REDRAWVIEW3D, 0);
578
allqueue(REDRAWACTION, 0);
579
allqueue(REDRAWNLA, 0);
580
allqueue(REDRAWIPO, 0);
581
allqueue(REDRAWTIME, 0);
582
allqueue(REDRAWBUTSOBJECT, 0);
585
scrarea_queue_headredraw(curarea);
589
/* ************************** TRANSFORMATIONS **************************** */
591
void BIF_selectOrientation(void)
593
#if 0 // TRANSFORM_FIX_ME
595
char *str_menu = BIF_menustringTransformOrientation("Orientation");
596
val = pupmenu(str_menu);
605
static void view_editmove(unsigned short UNUSED(event))
607
#if 0 // TRANSFORM_FIX_ME
609
/* Regular: Zoom in */
610
/* Shift: Scroll up */
611
/* Ctrl: Scroll right */
612
/* Alt-Shift: Rotate up */
613
/* Alt-Ctrl: Rotate right */
615
/* only work in 3D window for now
616
* In the end, will have to send to event to a 2D window handler instead
618
if (Trans.flag & T_2D_EDIT)
624
if (G.qual & LR_SHIFTKEY) {
625
if (G.qual & LR_ALTKEY) {
626
G.qual &= ~LR_SHIFTKEY;
628
G.qual |= LR_SHIFTKEY;
634
else if (G.qual & LR_CTRLKEY) {
635
if (G.qual & LR_ALTKEY) {
636
G.qual &= ~LR_CTRLKEY;
638
G.qual |= LR_CTRLKEY;
644
else if (U.uiflag & USER_WHEELZOOMDIR)
645
persptoetsen(PADMINUS);
647
persptoetsen(PADPLUSKEY);
652
if (G.qual & LR_SHIFTKEY) {
653
if (G.qual & LR_ALTKEY) {
654
G.qual &= ~LR_SHIFTKEY;
656
G.qual |= LR_SHIFTKEY;
662
else if (G.qual & LR_CTRLKEY) {
663
if (G.qual & LR_ALTKEY) {
664
G.qual &= ~LR_CTRLKEY;
666
G.qual |= LR_CTRLKEY;
672
else if (U.uiflag & USER_WHEELZOOMDIR)
673
persptoetsen(PADPLUSKEY);
675
persptoetsen(PADMINUS);
682
setTransformViewMatrices(&Trans);
686
/* ************************************************* */
688
/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
689
#define TFM_MODAL_CANCEL 1
690
#define TFM_MODAL_CONFIRM 2
691
#define TFM_MODAL_TRANSLATE 3
692
#define TFM_MODAL_ROTATE 4
693
#define TFM_MODAL_RESIZE 5
694
#define TFM_MODAL_SNAP_INV_ON 6
695
#define TFM_MODAL_SNAP_INV_OFF 7
696
#define TFM_MODAL_SNAP_TOGGLE 8
697
#define TFM_MODAL_AXIS_X 9
698
#define TFM_MODAL_AXIS_Y 10
699
#define TFM_MODAL_AXIS_Z 11
700
#define TFM_MODAL_PLANE_X 12
701
#define TFM_MODAL_PLANE_Y 13
702
#define TFM_MODAL_PLANE_Z 14
703
#define TFM_MODAL_CONS_OFF 15
704
#define TFM_MODAL_ADD_SNAP 16
705
#define TFM_MODAL_REMOVE_SNAP 17
706
/* 18 and 19 used by numinput, defined in transform.h
708
#define TFM_MODAL_PROPSIZE_UP 20
709
#define TFM_MODAL_PROPSIZE_DOWN 21
710
#define TFM_MODAL_AUTOIK_LEN_INC 22
711
#define TFM_MODAL_AUTOIK_LEN_DEC 23
713
#define TFM_MODAL_EDGESLIDE_UP 24
714
#define TFM_MODAL_EDGESLIDE_DOWN 25
716
/* for analog input, like trackpad */
717
#define TFM_MODAL_PROPSIZE 26
719
/* called in transform_ops.c, on each regeneration of keymaps */
720
wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf)
722
static EnumPropertyItem modal_items[] = {
723
{TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
724
{TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
725
{TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Translate", ""},
726
{TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
727
{TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
728
{TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Invert Snap On", ""},
729
{TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Invert Snap Off", ""},
730
{TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
731
{TFM_MODAL_AXIS_X, "AXIS_X", 0, "Orientation X axis", ""},
732
{TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Orientation Y axis", ""},
733
{TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Orientation Z axis", ""},
734
{TFM_MODAL_PLANE_X, "PLANE_X", 0, "Orientation X plane", ""},
735
{TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Orientation Y plane", ""},
736
{TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Orientation Z plane", ""},
737
{TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Remove Constraints", ""},
738
{TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
739
{TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
740
{NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
741
{NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
742
{TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
743
{TFM_MODAL_PROPSIZE_DOWN, "PROPORTIONAL_SIZE_DOWN", 0, "Decrease Proportional Influence", ""},
744
{TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
745
{TFM_MODAL_AUTOIK_LEN_DEC, "AUTOIK_CHAIN_LEN_DOWN", 0, "Decrease Max AutoIK Chain Length", ""},
746
{TFM_MODAL_EDGESLIDE_UP, "EDGESLIDE_EDGE_NEXT", 0, "Select next Edge Slide Edge", ""},
747
{TFM_MODAL_EDGESLIDE_DOWN, "EDGESLIDE_PREV_NEXT", 0, "Select previous Edge Slide Edge", ""},
748
{TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
749
{0, NULL, 0, NULL, NULL}
752
wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Transform Modal Map");
754
/* this function is called for each spacetype, only needs to add map once */
755
if (keymap && keymap->modal_items) return NULL;
757
keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items);
759
/* items for modal map */
760
WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL);
761
WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
762
WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
763
WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM);
765
WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE);
766
WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE);
767
WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, TFM_MODAL_RESIZE);
769
WM_modalkeymap_add_item(keymap, TABKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_SNAP_TOGGLE);
771
WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
772
WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
774
WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_SNAP_INV_ON);
775
WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_SNAP_INV_OFF);
777
WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, TFM_MODAL_ADD_SNAP);
778
WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, KM_ALT, 0, TFM_MODAL_REMOVE_SNAP);
780
WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
781
WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
782
WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP);
783
WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN);
784
WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, TFM_MODAL_PROPSIZE);
786
WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_UP);
787
WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_DOWN);
789
WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
790
WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
791
WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_INC);
792
WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_AUTOIK_LEN_DEC);
797
static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode)
799
if (!(t->flag & T_NO_CONSTRAINT)) {
800
int constraint_axis, constraint_plane;
801
int edit_2d = (t->flag & T_2D_EDIT);
802
const char *msg1 = "", *msg2 = "", *msg3 = "";
808
msg1 = IFACE_("along X");
809
msg2 = IFACE_("along %s X");
810
msg3 = IFACE_("locking %s X");
812
constraint_axis = CON_AXIS0;
815
msg1 = IFACE_("along Y");
816
msg2 = IFACE_("along %s Y");
817
msg3 = IFACE_("locking %s Y");
819
constraint_axis = CON_AXIS1;
822
msg1 = IFACE_("along Z");
823
msg2 = IFACE_("along %s Z");
824
msg3 = IFACE_("locking %s Z");
826
constraint_axis = CON_AXIS2;
832
constraint_plane = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) & (~constraint_axis));
834
if (edit_2d && (key_type != ZKEY)) {
839
setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg1);
844
if (t->con.orientation != V3D_MANIP_GLOBAL) {
848
short orientation = (t->current_orientation != V3D_MANIP_GLOBAL ?
849
t->current_orientation : V3D_MANIP_LOCAL);
850
if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
851
setUserConstraint(t, orientation, constraint_axis, msg2);
852
else if (t->modifiers & MOD_CONSTRAINT_PLANE)
853
setUserConstraint(t, orientation, constraint_plane, msg3);
857
if (!(t->modifiers & MOD_CONSTRAINT_PLANE))
858
setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_axis, msg2);
859
else if (t->modifiers & MOD_CONSTRAINT_PLANE)
860
setUserConstraint(t, V3D_MANIP_GLOBAL, constraint_plane, msg3);
863
t->redraw |= TREDRAW_HARD;
867
int transformEvent(TransInfo *t, wmEvent *event)
869
float mati[3][3] = MAT3_UNITY;
870
char cmode = constraintModeToChar(t);
873
t->redraw |= handleMouseInput(t, &t->mouse, event);
875
if (event->type == MOUSEMOVE) {
876
if (t->modifiers & MOD_CONSTRAINT_SELECT)
877
t->con.mode |= CON_SELECT;
879
copy_v2_v2_int(t->mval, event->mval);
881
// t->redraw |= TREDRAW_SOFT; /* Use this for soft redraw. Might cause flicker in object mode */
882
t->redraw |= TREDRAW_HARD;
885
if (t->state == TRANS_STARTING) {
886
t->state = TRANS_RUNNING;
889
applyMouseInput(t, &t->mouse, t->mval, t->values);
891
// Snapping mouse move events
892
t->redraw |= handleSnapping(t, event);
895
/* handle modal keymap first */
896
if (event->type == EVT_MODAL_MAP) {
897
switch (event->val) {
898
case TFM_MODAL_CANCEL:
899
t->state = TRANS_CANCEL;
901
case TFM_MODAL_CONFIRM:
902
t->state = TRANS_CONFIRM;
904
case TFM_MODAL_TRANSLATE:
905
/* only switch when... */
906
if (ELEM5(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) {
907
if (t->mode == TFM_EDGE_SLIDE) {
908
freeEdgeSlideVerts(t);
910
else if (t->mode == TFM_VERT_SLIDE) {
911
freeVertSlideVerts(t);
913
resetTransRestrictions(t);
914
restoreTransObjects(t);
916
initSnapping(t, NULL); // need to reinit after mode change
917
t->redraw |= TREDRAW_HARD;
918
WM_event_add_mousemove(t->context);
920
else if (t->mode == TFM_SEQ_SLIDE) {
921
t->flag ^= T_ALT_TRANSFORM;
922
t->redraw |= TREDRAW_HARD;
925
if (t->obedit && t->obedit->type == OB_MESH) {
926
if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) {
927
resetTransRestrictions(t);
928
restoreTransObjects(t);
930
/* first try edge slide */
932
/* if that fails, do vertex slide */
933
if (t->state == TRANS_CANCEL) {
934
t->state = TRANS_STARTING;
937
/* vert slide can fail on unconnected vertices (rare but possible) */
938
if (t->state == TRANS_CANCEL) {
939
t->state = TRANS_STARTING;
940
resetTransRestrictions(t);
941
restoreTransObjects(t);
944
initSnapping(t, NULL); // need to reinit after mode change
945
t->redraw |= TREDRAW_HARD;
946
WM_event_add_mousemove(t->context);
949
else if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
950
if (t->mode == TFM_TRANSLATION) {
951
restoreTransObjects(t);
953
t->flag ^= T_ALT_TRANSFORM;
954
t->redraw |= TREDRAW_HARD;
959
case TFM_MODAL_ROTATE:
960
/* only switch when... */
961
if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) {
962
if (ELEM4(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION) ) {
964
resetTransRestrictions(t);
966
if (t->mode == TFM_ROTATION) {
967
restoreTransObjects(t);
971
restoreTransObjects(t);
974
initSnapping(t, NULL); // need to reinit after mode change
975
t->redraw |= TREDRAW_HARD;
979
case TFM_MODAL_RESIZE:
980
/* only switch when... */
981
if (ELEM3(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL) ) {
982
resetTransRestrictions(t);
983
restoreTransObjects(t);
985
initSnapping(t, NULL); // need to reinit after mode change
986
t->redraw |= TREDRAW_HARD;
988
else if (t->mode == TFM_SHRINKFATTEN) {
989
t->flag ^= T_ALT_TRANSFORM;
990
t->redraw |= TREDRAW_HARD;
992
else if (t->mode == TFM_RESIZE) {
993
if (t->options & CTX_MOVIECLIP) {
994
restoreTransObjects(t);
996
t->flag ^= T_ALT_TRANSFORM;
997
t->redraw |= TREDRAW_HARD;
1002
case TFM_MODAL_SNAP_INV_ON:
1003
t->modifiers |= MOD_SNAP_INVERT;
1004
t->redraw |= TREDRAW_HARD;
1006
case TFM_MODAL_SNAP_INV_OFF:
1007
t->modifiers &= ~MOD_SNAP_INVERT;
1008
t->redraw |= TREDRAW_HARD;
1010
case TFM_MODAL_SNAP_TOGGLE:
1011
t->modifiers ^= MOD_SNAP;
1012
t->redraw |= TREDRAW_HARD;
1014
case TFM_MODAL_AXIS_X:
1015
if ((t->flag & T_NO_CONSTRAINT) == 0) {
1020
if (t->flag & T_2D_EDIT) {
1021
setUserConstraint(t, V3D_MANIP_GLOBAL, (CON_AXIS0), IFACE_("along X"));
1024
setUserConstraint(t, t->current_orientation, (CON_AXIS0), IFACE_("along %s X"));
1027
t->redraw |= TREDRAW_HARD;
1030
case TFM_MODAL_AXIS_Y:
1031
if ((t->flag & T_NO_CONSTRAINT) == 0) {
1036
if (t->flag & T_2D_EDIT) {
1037
setUserConstraint(t, V3D_MANIP_GLOBAL, (CON_AXIS1), IFACE_("along Y"));
1040
setUserConstraint(t, t->current_orientation, (CON_AXIS1), IFACE_("along %s Y"));
1043
t->redraw |= TREDRAW_HARD;
1046
case TFM_MODAL_AXIS_Z:
1047
if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1052
setUserConstraint(t, t->current_orientation, (CON_AXIS2), IFACE_("along %s Z"));
1054
t->redraw |= TREDRAW_HARD;
1057
case TFM_MODAL_PLANE_X:
1058
if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1063
setUserConstraint(t, t->current_orientation, (CON_AXIS1 | CON_AXIS2), IFACE_("locking %s X"));
1065
t->redraw |= TREDRAW_HARD;
1068
case TFM_MODAL_PLANE_Y:
1069
if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1074
setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS2), IFACE_("locking %s Y"));
1076
t->redraw |= TREDRAW_HARD;
1079
case TFM_MODAL_PLANE_Z:
1080
if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) {
1085
setUserConstraint(t, t->current_orientation, (CON_AXIS0 | CON_AXIS1), IFACE_("locking %s Z"));
1087
t->redraw |= TREDRAW_HARD;
1090
case TFM_MODAL_CONS_OFF:
1091
if ((t->flag & T_NO_CONSTRAINT) == 0) {
1093
t->redraw |= TREDRAW_HARD;
1096
case TFM_MODAL_ADD_SNAP:
1098
t->redraw |= TREDRAW_HARD;
1100
case TFM_MODAL_REMOVE_SNAP:
1102
t->redraw |= TREDRAW_HARD;
1105
case TFM_MODAL_PROPSIZE:
1106
/* MOUSEPAN usage... */
1107
if (t->flag & T_PROP_EDIT) {
1108
float fac = 1.0f + 0.005f *(event->y - event->prevy);
1109
t->prop_size *= fac;
1110
if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1111
t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1112
calculatePropRatio(t);
1114
t->redraw |= TREDRAW_HARD;
1117
case TFM_MODAL_PROPSIZE_UP:
1118
if (t->flag & T_PROP_EDIT) {
1119
t->prop_size *= 1.1f;
1120
if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1121
t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1122
calculatePropRatio(t);
1124
t->redraw |= TREDRAW_HARD;
1126
case TFM_MODAL_PROPSIZE_DOWN:
1127
if (t->flag & T_PROP_EDIT) {
1128
t->prop_size *= 0.90909090f;
1129
calculatePropRatio(t);
1131
t->redraw |= TREDRAW_HARD;
1133
case TFM_MODAL_EDGESLIDE_UP:
1134
case TFM_MODAL_EDGESLIDE_DOWN:
1135
t->redraw |= TREDRAW_HARD;
1137
case TFM_MODAL_AUTOIK_LEN_INC:
1138
if (t->flag & T_AUTOIK)
1139
transform_autoik_update(t, 1);
1140
t->redraw |= TREDRAW_HARD;
1142
case TFM_MODAL_AUTOIK_LEN_DEC:
1143
if (t->flag & T_AUTOIK)
1144
transform_autoik_update(t, -1);
1145
t->redraw |= TREDRAW_HARD;
1152
// Modal numinput events
1153
t->redraw |= handleNumInput(&(t->num), event);
1155
/* else do non-mapped events */
1156
else if (event->val == KM_PRESS) {
1157
switch (event->type) {
1159
t->state = TRANS_CANCEL;
1161
/* enforce redraw of transform when modifiers are used */
1164
t->modifiers |= MOD_CONSTRAINT_PLANE;
1165
t->redraw |= TREDRAW_HARD;
1169
if ((t->spacetype == SPACE_VIEW3D) && event->alt) {
1170
#if 0 // TRANSFORM_FIX_ME
1173
getmouseco_sc(mval);
1174
BIF_selectOrientation();
1175
calc_manipulator_stats(curarea);
1176
copy_m3_m4(t->spacemtx, G.vd->twmat);
1177
warp_pointer(mval[0], mval[1]);
1181
t->state = TRANS_CONFIRM;
1186
if ((t->flag & T_NO_CONSTRAINT) == 0) {
1187
/* exception for switching to dolly, or trackball, in camera view */
1188
if (t->flag & T_CAMERA) {
1189
if (t->mode == TFM_TRANSLATION)
1190
setLocalConstraint(t, (CON_AXIS2), IFACE_("along local Z"));
1191
else if (t->mode == TFM_ROTATION) {
1192
restoreTransObjects(t);
1197
t->modifiers |= MOD_CONSTRAINT_SELECT;
1198
if (t->con.mode & CON_APPLY) {
1203
initSelectConstraint(t, t->spacemtx);
1206
/* bit hackish... but it prevents mmb select to print the orientation from menu */
1207
strcpy(t->spacename, "global");
1208
initSelectConstraint(t, mati);
1210
postSelectConstraint(t);
1213
t->redraw |= TREDRAW_HARD;
1217
t->state = TRANS_CANCEL;
1221
t->state = TRANS_CONFIRM;
1224
/* only switch when... */
1225
if (ELEM3(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL) ) {
1226
resetTransRestrictions(t);
1227
restoreTransObjects(t);
1229
initSnapping(t, NULL); // need to reinit after mode change
1230
t->redraw |= TREDRAW_HARD;
1234
/* only switch when... */
1235
if (ELEM3(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL) ) {
1236
resetTransRestrictions(t);
1237
restoreTransObjects(t);
1239
initSnapping(t, NULL); // need to reinit after mode change
1240
t->redraw |= TREDRAW_HARD;
1244
/* only switch when... */
1245
if (!(t->options & CTX_TEXTURE)) {
1246
if (ELEM4(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION) ) {
1248
resetTransRestrictions(t);
1250
if (t->mode == TFM_ROTATION) {
1251
restoreTransObjects(t);
1255
restoreTransObjects(t);
1258
initSnapping(t, NULL); // need to reinit after mode change
1259
t->redraw |= TREDRAW_HARD;
1265
t->flag ^= T_PROP_CONNECTED;
1266
sort_trans_data_dist(t);
1267
calculatePropRatio(t);
1272
t->redraw |= TREDRAW_HARD;
1278
transform_event_xyz_constraint(t, event->type, cmode);
1281
if (t->flag & T_PROP_EDIT && event->shift) {
1282
t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1283
calculatePropRatio(t);
1284
t->redraw |= TREDRAW_HARD;
1288
if (event->alt && t->flag & T_PROP_EDIT) {
1289
t->prop_size *= 1.1f;
1290
if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO)
1291
t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far);
1292
calculatePropRatio(t);
1297
case WHEELDOWNMOUSE:
1298
if (t->flag & T_AUTOIK) {
1299
transform_autoik_update(t, 1);
1301
else view_editmove(event->type);
1305
if (event->alt && t->flag & T_PROP_EDIT) {
1306
t->prop_size *= 0.90909090f;
1307
calculatePropRatio(t);
1313
if (t->flag & T_AUTOIK) {
1314
transform_autoik_update(t, -1);
1316
else view_editmove(event->type);
1321
if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1322
t->flag |= T_ALT_TRANSFORM;
1323
t->redraw |= TREDRAW_HARD;
1332
// Numerical input events
1333
t->redraw |= handleNumInput(&(t->num), event);
1335
// Snapping key events
1336
t->redraw |= handleSnapping(t, event);
1339
else if (event->val == KM_RELEASE) {
1340
switch (event->type) {
1343
t->modifiers &= ~MOD_CONSTRAINT_PLANE;
1344
t->redraw |= TREDRAW_HARD;
1348
if ((t->flag & T_NO_CONSTRAINT) == 0) {
1349
t->modifiers &= ~MOD_CONSTRAINT_SELECT;
1350
postSelectConstraint(t);
1351
t->redraw |= TREDRAW_HARD;
1356
// if (WM_modal_tweak_exit(event, t->event_type))
1357
//// if (t->options & CTX_TWEAK)
1358
// t->state = TRANS_CONFIRM;
1362
if (ELEM(t->spacetype, SPACE_SEQ, SPACE_VIEW3D)) {
1363
t->flag &= ~T_ALT_TRANSFORM;
1364
t->redraw |= TREDRAW_HARD;
1373
/* confirm transform if launch key is released after mouse move */
1374
if (t->flag & T_RELEASE_CONFIRM) {
1375
/* XXX Keyrepeat bug in Xorg fucks this up, will test when fixed */
1376
if (event->type == t->launch_event && (t->launch_event == LEFTMOUSE || t->launch_event == RIGHTMOUSE)) {
1377
t->state = TRANS_CONFIRM;
1384
// Per transform event, if present
1386
t->redraw |= t->handleEvent(t, event);
1388
if (handled || t->redraw) {
1392
return OPERATOR_PASS_THROUGH;
1396
int calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], int cent2d[2])
1398
TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data");
1401
t->state = TRANS_RUNNING;
1403
t->options = CTX_NONE;
1405
t->mode = TFM_DUMMY;
1407
initTransInfo(C, t, NULL, NULL); // internal data, mouse, vectors
1409
createTransData(C, t); // make TransData structs from selection
1411
t->around = centerMode; // override userdefined mode
1413
if (t->total == 0) {
1422
copy_v2_v2_int(cent2d, t->center2d);
1426
// Copy center from constraint center. Transform center can be local
1427
copy_v3_v3(cent3d, t->con.center);
1432
/* aftertrans does insert ipos and action channels, and clears base flags, doesnt read transdata */
1433
special_aftertrans_update(C, t);
1448
static void drawArrow(ArrowDirection d, short offset, short length, short size)
1457
glVertex2s(offset, 0);
1458
glVertex2s(offset + length, 0);
1459
glVertex2s(offset + length, 0);
1460
glVertex2s(offset + length - size, -size);
1461
glVertex2s(offset + length, 0);
1462
glVertex2s(offset + length - size, size);
1471
glVertex2s(0, offset);
1472
glVertex2s(0, offset + length);
1473
glVertex2s(0, offset + length);
1474
glVertex2s(-size, offset + length - size);
1475
glVertex2s(0, offset + length);
1476
glVertex2s(size, offset + length - size);
1482
static void drawArrowHead(ArrowDirection d, short size)
1490
glVertex2s(-size, -size);
1492
glVertex2s(-size, size);
1500
glVertex2s(-size, -size);
1502
glVertex2s(size, -size);
1508
static void drawArc(float size, float angle_start, float angle_end, int segments)
1510
float delta = (angle_end - angle_start) / segments;
1514
glBegin(GL_LINE_STRIP);
1516
for (angle = angle_start, a = 0; a < segments; angle += delta, a++) {
1517
glVertex2f(cosf(angle) * size, sinf(angle) * size);
1519
glVertex2f(cosf(angle_end) * size, sinf(angle_end) * size);
1524
static int helpline_poll(bContext *C)
1526
ARegion *ar = CTX_wm_region(C);
1528
if (ar && ar->regiontype == RGN_TYPE_WINDOW)
1533
static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata)
1535
TransInfo *t = (TransInfo *)customdata;
1537
if (t->helpline != HLP_NONE && !(t->flag & T_USES_MANIPULATOR)) {
1538
float vecrot[3], cent[2];
1544
copy_v3_v3(vecrot, t->center);
1545
if (t->flag & T_EDIT) {
1546
Object *ob = t->obedit;
1547
if (ob) mul_m4_v3(ob->obmat, vecrot);
1549
else if (t->flag & T_POSE) {
1550
Object *ob = t->poseobj;
1551
if (ob) mul_m4_v3(ob->obmat, vecrot);
1554
projectFloatViewEx(t, vecrot, cent, V3D_PROJ_TEST_CLIP_ZERO);
1558
switch (t->helpline) {
1560
UI_ThemeColor(TH_WIRE);
1563
glBegin(GL_LINE_STRIP);
1564
glVertex2iv(t->mval);
1568
glTranslatef(mval[0], mval[1], 0);
1569
glRotatef(-RAD2DEGF(atan2f(cent[0] - t->mval[0], cent[1] - t->mval[1])), 0, 0, 1);
1573
drawArrow(UP, 5, 10, 5);
1574
drawArrow(DOWN, 5, 10, 5);
1578
UI_ThemeColor(TH_WIRE);
1580
glTranslatef(mval[0], mval[1], 0);
1583
drawArrow(RIGHT, 5, 10, 5);
1584
drawArrow(LEFT, 5, 10, 5);
1588
UI_ThemeColor(TH_WIRE);
1590
glTranslatef(mval[0], mval[1], 0);
1593
drawArrow(UP, 5, 10, 5);
1594
drawArrow(DOWN, 5, 10, 5);
1599
float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1];
1600
float angle = atan2f(dy, dx);
1601
float dist = sqrtf(dx * dx + dy * dy);
1602
float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f);
1603
float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f);
1604
UI_ThemeColor(TH_WIRE);
1607
glBegin(GL_LINE_STRIP);
1608
glVertex2iv(t->mval);
1612
glTranslatef(cent[0] - t->mval[0] + mval[0], cent[1] - t->mval[1] + mval[1], 0);
1616
drawArc(dist, angle - delta_angle, angle - spacing_angle, 10);
1617
drawArc(dist, angle + spacing_angle, angle + delta_angle, 10);
1621
glTranslatef(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0);
1622
glRotatef(RAD2DEGF(angle - delta_angle), 0, 0, 1);
1624
drawArrowHead(DOWN, 5);
1628
glTranslatef(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0);
1629
glRotatef(RAD2DEGF(angle + delta_angle), 0, 0, 1);
1631
drawArrowHead(UP, 5);
1638
unsigned char col[3], col2[3];
1639
UI_GetThemeColor3ubv(TH_GRID, col);
1641
glTranslatef(mval[0], mval[1], 0);
1645
UI_make_axis_color(col, col2, 'X');
1646
glColor3ubv((GLubyte *)col2);
1648
drawArrow(RIGHT, 5, 10, 5);
1649
drawArrow(LEFT, 5, 10, 5);
1651
UI_make_axis_color(col, col2, 'Y');
1652
glColor3ubv((GLubyte *)col2);
1654
drawArrow(UP, 5, 10, 5);
1655
drawArrow(DOWN, 5, 10, 5);
1665
static void drawTransformView(const struct bContext *C, ARegion *UNUSED(ar), void *arg)
1670
drawPropCircle(C, t);
1673
/* edge slide, vert slide */
1674
drawEdgeSlide(C, t);
1675
drawVertSlide(C, t);
1678
/* just draw a little warning message in the top-right corner of the viewport to warn that autokeying is enabled */
1679
static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar)
1682
const char *printable = IFACE_("Auto Keying On");
1683
float printable_size[2];
1686
ED_region_visible_rect(ar, &rect);
1688
BLF_width_and_height_default(printable, &printable_size[0], &printable_size[1]);
1690
xco = rect.xmax - (int)printable_size[0] - 10;
1691
yco = rect.ymax - (int)printable_size[1] - 10;
1693
/* warning text (to clarify meaning of overlays)
1694
* - original color was red to match the icon, but that clashes badly with a less nasty border
1696
UI_ThemeColorShade(TH_TEXT_HI, -50);
1697
#ifdef WITH_INTERNATIONAL
1698
BLF_draw_default(xco, ar->winy - 17, 0.0f, printable, sizeof(printable));
1700
BLF_draw_default_ascii(xco, ar->winy - 17, 0.0f, printable, sizeof(printable));
1703
/* autokey recording icon... */
1704
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1707
xco -= (ICON_DEFAULT_WIDTH + 2);
1708
UI_icon_draw(xco, yco, ICON_REC);
1710
glDisable(GL_BLEND);
1713
static void drawTransformPixel(const struct bContext *UNUSED(C), ARegion *ar, void *arg)
1716
Scene *scene = t->scene;
1719
/* draw autokeyframing hint in the corner
1720
* - only draw if enabled (advanced users may be distracted/annoyed),
1721
* for objects that will be autokeyframed (no point ohterwise),
1722
* AND only for the active region (as showing all is too overwhelming)
1724
if ((U.autokey_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
1726
if (t->flag & (T_OBJECT | T_POSE)) {
1727
if (ob && autokeyframe_cfra_can_key(scene, &ob->id)) {
1728
drawAutoKeyWarning(t, ar);
1735
void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
1737
ToolSettings *ts = CTX_data_tool_settings(C);
1738
int constraint_axis[3] = {0, 0, 0};
1739
int proportional = 0;
1742
// Save back mode in case we're in the generic operator
1743
if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
1744
RNA_property_enum_set(op->ptr, prop, t->mode);
1747
if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
1748
float *values = (t->flag & T_AUTOVALUES) ? t->auto_values : t->values;
1749
if (RNA_property_array_check(prop)) {
1750
RNA_property_float_set_array(op->ptr, prop, values);
1753
RNA_property_float_set(op->ptr, prop, values[0]);
1757
/* convert flag to enum */
1758
switch (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
1759
case (T_PROP_EDIT | T_PROP_CONNECTED):
1760
proportional = PROP_EDIT_CONNECTED;
1763
proportional = PROP_EDIT_ON;
1766
proportional = PROP_EDIT_OFF;
1769
// If modal, save settings back in scene if not set as operator argument
1770
if (t->flag & T_MODAL) {
1772
/* save settings if not set in operator */
1773
if ((prop = RNA_struct_find_property(op->ptr, "proportional")) &&
1774
!RNA_property_is_set(op->ptr, prop))
1777
ts->proportional = proportional;
1778
else if (t->options & CTX_MASK)
1779
ts->proportional_mask = (proportional != PROP_EDIT_OFF);
1781
ts->proportional_objects = (proportional != PROP_EDIT_OFF);
1784
if ((prop = RNA_struct_find_property(op->ptr, "proportional_size")) &&
1785
!RNA_property_is_set(op->ptr, prop))
1787
ts->proportional_size = t->prop_size;
1790
if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
1791
!RNA_property_is_set(op->ptr, prop))
1793
ts->prop_mode = t->prop_mode;
1796
/* do we check for parameter? */
1797
if (t->modifiers & MOD_SNAP) {
1798
ts->snap_flag |= SCE_SNAP;
1801
ts->snap_flag &= ~SCE_SNAP;
1804
if (t->spacetype == SPACE_VIEW3D) {
1805
if ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) &&
1806
!RNA_property_is_set(op->ptr, prop))
1808
View3D *v3d = t->view;
1810
v3d->twmode = t->current_orientation;
1815
if (RNA_struct_find_property(op->ptr, "proportional")) {
1816
RNA_enum_set(op->ptr, "proportional", proportional);
1817
RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
1818
RNA_float_set(op->ptr, "proportional_size", t->prop_size);
1821
if ((prop = RNA_struct_find_property(op->ptr, "axis"))) {
1822
RNA_property_float_set_array(op->ptr, prop, t->axis);
1825
if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
1826
RNA_property_boolean_set(op->ptr, prop, t->flag & T_MIRROR);
1829
if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
1830
/* constraint orientation can be global, event if user selects something else
1831
* so use the orientation in the constraint if set
1833
if (t->con.mode & CON_APPLY) {
1834
RNA_enum_set(op->ptr, "constraint_orientation", t->con.orientation);
1837
RNA_enum_set(op->ptr, "constraint_orientation", t->current_orientation);
1840
if (t->con.mode & CON_APPLY) {
1841
if (t->con.mode & CON_AXIS0) {
1842
constraint_axis[0] = 1;
1844
if (t->con.mode & CON_AXIS1) {
1845
constraint_axis[1] = 1;
1847
if (t->con.mode & CON_AXIS2) {
1848
constraint_axis[2] = 1;
1852
RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
1856
/* note: caller needs to free 't' on a 0 return */
1857
int initTransform(bContext *C, TransInfo *t, wmOperator *op, wmEvent *event, int mode)
1864
/* added initialize, for external calls to set stuff in TransInfo, like undo string */
1866
t->state = TRANS_STARTING;
1868
if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) && RNA_property_is_set(op->ptr, prop)) {
1869
if (RNA_property_boolean_get(op->ptr, prop)) {
1870
options |= CTX_TEXTURE;
1874
t->options = options;
1878
t->launch_event = event ? event->type : -1;
1880
if (t->launch_event == EVT_TWEAK_R) {
1881
t->launch_event = RIGHTMOUSE;
1883
else if (t->launch_event == EVT_TWEAK_L) {
1884
t->launch_event = LEFTMOUSE;
1887
// XXX Remove this when wm_operator_call_internal doesn't use window->eventstate (which can have type = 0)
1888
// For manipulator only, so assume LEFTMOUSE
1889
if (t->launch_event == 0) {
1890
t->launch_event = LEFTMOUSE;
1893
if (!initTransInfo(C, t, op, event)) { /* internal data, mouse, vectors */
1897
if (t->spacetype == SPACE_VIEW3D) {
1898
//calc_manipulator_stats(curarea);
1899
initTransformOrientation(C, t);
1901
t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);
1902
t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
1903
t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
1904
t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
1906
else if (t->spacetype == SPACE_IMAGE) {
1907
unit_m3(t->spacemtx);
1908
t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
1909
//t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL);
1910
t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);
1912
else if (t->spacetype == SPACE_CLIP) {
1913
unit_m3(t->spacemtx);
1914
t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
1916
else if (t->spacetype == SPACE_NODE) {
1917
unit_m3(t->spacemtx);
1918
/*t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW);*/
1919
t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW);
1920
/*t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t);*/
1923
unit_m3(t->spacemtx);
1925
createTransData(C, t); // make TransData structs from selection
1927
if (t->total == 0) {
1932
/* Stupid code to have Ctrl-Click on manipulator work ok */
1934
/* do this only for translation/rotation/resize due to only this
1935
* moded are available from manipulator and doing such check could
1936
* lead to keymap conflicts for other modes (see #31584)
1938
if (ELEM3(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) {
1939
wmKeyMap *keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1942
for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
1943
if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
1944
if ((ELEM(kmi->type, LEFTCTRLKEY, RIGHTCTRLKEY) && event->ctrl) ||
1945
(ELEM(kmi->type, LEFTSHIFTKEY, RIGHTSHIFTKEY) && event->shift) ||
1946
(ELEM(kmi->type, LEFTALTKEY, RIGHTALTKEY) && event->alt) ||
1947
((kmi->type == OSKEY) && event->oskey) )
1949
t->modifiers |= MOD_SNAP_INVERT;
1958
t->keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1960
initSnapping(t, op); // Initialize snapping data AFTER mode flags
1962
/* EVIL! posemode code can switch translation to rotate when 1 bone is selected. will be removed (ton) */
1963
/* EVIL2: we gave as argument also texture space context bit... was cleared */
1964
/* EVIL3: extend mode for animation editors also switches modes... but is best way to avoid duplicate code */
1967
calculatePropRatio(t);
1970
initMouseInput(t, &t->mouse, t->center2d, t->imval);
1973
case TFM_TRANSLATION:
1982
case TFM_SKIN_RESIZE:
1994
case TFM_SHRINKFATTEN:
1995
initShrinkFatten(t);
2000
case TFM_CURVE_SHRINKFATTEN:
2001
initCurveShrinkFatten(t);
2003
case TFM_MASK_SHRINKFATTEN:
2004
initMaskShrinkFatten(t);
2016
{ /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */
2017
bArmature *arm = t->poseobj->data;
2018
if (arm->drawtype == ARM_ENVELOPE)
2019
initBoneEnvelope(t);
2024
case TFM_BONE_ENVELOPE:
2025
initBoneEnvelope(t);
2027
case TFM_EDGE_SLIDE:
2030
case TFM_VERT_SLIDE:
2036
case TFM_TIME_TRANSLATE:
2037
initTimeTranslate(t);
2039
case TFM_TIME_SLIDE:
2042
case TFM_TIME_SCALE:
2045
case TFM_TIME_DUPLICATE:
2046
/* same as TFM_TIME_EXTEND, but we need the mode info for later
2047
* so that duplicate-culling will work properly
2049
if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2052
initTimeTranslate(t);
2055
case TFM_TIME_EXTEND:
2056
/* now that transdata has been made, do like for TFM_TIME_TRANSLATE (for most Animation
2057
* Editors because they have only 1D transforms for time values) or TFM_TRANSLATION
2058
* (for Graph/NLA Editors only since they uses 'standard' transforms to get 2D movement)
2059
* depending on which editor this was called from
2061
if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA))
2064
initTimeTranslate(t);
2086
if (t->state == TRANS_CANCEL) {
2092
/* overwrite initial values if operator supplied a non-null vector */
2093
if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) {
2094
float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory */
2096
if (RNA_property_array_check(prop)) {
2097
RNA_float_get_array(op->ptr, "value", values);
2100
values[0] = RNA_float_get(op->ptr, "value");
2103
copy_v4_v4(t->values, values);
2104
copy_v4_v4(t->auto_values, values);
2105
t->flag |= T_AUTOVALUES;
2108
/* Transformation axis from operator */
2109
if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) {
2110
RNA_property_float_get_array(op->ptr, prop, t->axis);
2111
normalize_v3(t->axis);
2112
copy_v3_v3(t->axis_orig, t->axis);
2115
/* Constraint init from operator */
2116
if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) {
2117
int constraint_axis[3];
2119
RNA_property_boolean_get_array(op->ptr, prop, constraint_axis);
2121
if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) {
2122
t->con.mode |= CON_APPLY;
2124
if (constraint_axis[0]) {
2125
t->con.mode |= CON_AXIS0;
2127
if (constraint_axis[1]) {
2128
t->con.mode |= CON_AXIS1;
2130
if (constraint_axis[2]) {
2131
t->con.mode |= CON_AXIS2;
2134
setUserConstraint(t, t->current_orientation, t->con.mode, "%s");
2143
void transformApply(bContext *C, TransInfo *t)
2147
if ((t->redraw & TREDRAW_HARD) || (t->draw_handle_apply == NULL && (t->redraw & TREDRAW_SOFT))) {
2148
selectConstraint(t);
2150
t->transform(t, t->mval); // calls recalcData()
2151
viewRedrawForce(C, t);
2153
t->redraw = TREDRAW_NOTHING;
2155
else if (t->redraw & TREDRAW_SOFT) {
2156
viewRedrawForce(C, t);
2159
/* If auto confirm is on, break after one pass */
2160
if (t->options & CTX_AUTOCONFIRM) {
2161
t->state = TRANS_CONFIRM;
2167
static void drawTransformApply(const bContext *C, ARegion *UNUSED(ar), void *arg)
2171
if (t->redraw & TREDRAW_SOFT) {
2172
t->redraw |= TREDRAW_HARD;
2173
transformApply((bContext *)C, t);
2177
int transformEnd(bContext *C, TransInfo *t)
2179
int exit_code = OPERATOR_RUNNING_MODAL;
2183
if (t->state != TRANS_STARTING && t->state != TRANS_RUNNING) {
2184
/* handle restoring objects */
2185
if (t->state == TRANS_CANCEL) {
2186
/* exception, edge slide transformed UVs too */
2187
if (t->mode == TFM_EDGE_SLIDE)
2188
doEdgeSlide(t, 0.0f);
2190
exit_code = OPERATOR_CANCELLED;
2191
restoreTransObjects(t); // calls recalcData()
2194
exit_code = OPERATOR_FINISHED;
2197
/* aftertrans does insert keyframes, and clears base flags, doesnt read transdata */
2198
special_aftertrans_update(C, t);
2203
/* send events out for redraws */
2204
viewRedrawPost(C, t);
2206
/* Undo as last, certainly after special_trans_update! */
2208
if (t->state == TRANS_CANCEL) {
2209
// if (t->undostr) ED_undo_push(C, t->undostr);
2212
// if (t->undostr) ED_undo_push(C, t->undostr);
2213
// else ED_undo_push(C, transform_to_undostr(t));
2217
viewRedrawForce(C, t);
2225
/* ************************** TRANSFORM LOCKS **************************** */
2227
static void protectedTransBits(short protectflag, float *vec)
2229
if (protectflag & OB_LOCK_LOCX)
2231
if (protectflag & OB_LOCK_LOCY)
2233
if (protectflag & OB_LOCK_LOCZ)
2237
static void protectedSizeBits(short protectflag, float *size)
2239
if (protectflag & OB_LOCK_SCALEX)
2241
if (protectflag & OB_LOCK_SCALEY)
2243
if (protectflag & OB_LOCK_SCALEZ)
2247
static void protectedRotateBits(short protectflag, float *eul, float *oldeul)
2249
if (protectflag & OB_LOCK_ROTX)
2251
if (protectflag & OB_LOCK_ROTY)
2253
if (protectflag & OB_LOCK_ROTZ)
2258
/* this function only does the delta rotation */
2259
/* axis-angle is usually internally stored as quats... */
2260
static void protectedAxisAngleBits(short protectflag, float axis[3], float *angle, float oldAxis[3], float oldAngle)
2262
/* check that protection flags are set */
2263
if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2266
if (protectflag & OB_LOCK_ROT4D) {
2267
/* axis-angle getting limited as 4D entities that they are... */
2268
if (protectflag & OB_LOCK_ROTW)
2270
if (protectflag & OB_LOCK_ROTX)
2271
axis[0] = oldAxis[0];
2272
if (protectflag & OB_LOCK_ROTY)
2273
axis[1] = oldAxis[1];
2274
if (protectflag & OB_LOCK_ROTZ)
2275
axis[2] = oldAxis[2];
2278
/* axis-angle get limited with euler... */
2279
float eul[3], oldeul[3];
2281
axis_angle_to_eulO(eul, EULER_ORDER_DEFAULT, axis, *angle);
2282
axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, oldAxis, oldAngle);
2284
if (protectflag & OB_LOCK_ROTX)
2286
if (protectflag & OB_LOCK_ROTY)
2288
if (protectflag & OB_LOCK_ROTZ)
2291
eulO_to_axis_angle(axis, angle, eul, EULER_ORDER_DEFAULT);
2293
/* when converting to axis-angle, we need a special exception for the case when there is no axis */
2294
if (IS_EQF(axis[0], axis[1]) && IS_EQF(axis[1], axis[2])) {
2295
/* for now, rotate around y-axis then (so that it simply becomes the roll) */
2301
/* this function only does the delta rotation */
2302
static void protectedQuaternionBits(short protectflag, float *quat, float *oldquat)
2304
/* check that protection flags are set */
2305
if ((protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) == 0)
2308
if (protectflag & OB_LOCK_ROT4D) {
2309
/* quaternions getting limited as 4D entities that they are... */
2310
if (protectflag & OB_LOCK_ROTW)
2311
quat[0] = oldquat[0];
2312
if (protectflag & OB_LOCK_ROTX)
2313
quat[1] = oldquat[1];
2314
if (protectflag & OB_LOCK_ROTY)
2315
quat[2] = oldquat[2];
2316
if (protectflag & OB_LOCK_ROTZ)
2317
quat[3] = oldquat[3];
2320
/* quaternions get limited with euler... (compatibility mode) */
2321
float eul[3], oldeul[3], nquat[4], noldquat[4];
2324
qlen = normalize_qt_qt(nquat, quat);
2325
normalize_qt_qt(noldquat, oldquat);
2327
quat_to_eul(eul, nquat);
2328
quat_to_eul(oldeul, noldquat);
2330
if (protectflag & OB_LOCK_ROTX)
2332
if (protectflag & OB_LOCK_ROTY)
2334
if (protectflag & OB_LOCK_ROTZ)
2337
eul_to_quat(quat, eul);
2339
/* restore original quat size */
2340
mul_qt_fl(quat, qlen);
2342
/* quaternions flip w sign to accumulate rotations correctly */
2343
if ((nquat[0] < 0.0f && quat[0] > 0.0f) ||
2344
(nquat[0] > 0.0f && quat[0] < 0.0f))
2346
mul_qt_fl(quat, -1.0f);
2351
/* ******************* TRANSFORM LIMITS ********************** */
2353
static void constraintTransLim(TransInfo *t, TransData *td)
2356
bConstraintTypeInfo *ctiLoc = BKE_get_constraint_typeinfo(CONSTRAINT_TYPE_LOCLIMIT);
2357
bConstraintTypeInfo *ctiDist = BKE_get_constraint_typeinfo(CONSTRAINT_TYPE_DISTLIMIT);
2359
bConstraintOb cob = {NULL};
2361
float ctime = (float)(t->scene->r.cfra);
2363
/* Make a temporary bConstraintOb for using these limit constraints
2364
* - they only care that cob->matrix is correctly set ;-)
2365
* - current space should be local
2367
unit_m4(cob.matrix);
2368
copy_v3_v3(cob.matrix[3], td->loc);
2370
/* Evaluate valid constraints */
2371
for (con = td->con; con; con = con->next) {
2372
bConstraintTypeInfo *cti = NULL;
2373
ListBase targets = {NULL, NULL};
2375
/* only consider constraint if enabled */
2376
if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2377
if (con->enforce == 0.0f) continue;
2379
/* only use it if it's tagged for this purpose (and the right type) */
2380
if (con->type == CONSTRAINT_TYPE_LOCLIMIT) {
2381
bLocLimitConstraint *data = con->data;
2383
if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2387
else if (con->type == CONSTRAINT_TYPE_DISTLIMIT) {
2388
bDistLimitConstraint *data = con->data;
2390
if ((data->flag & LIMITDIST_TRANSFORM) == 0)
2396
/* do space conversions */
2397
if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2398
/* just multiply by td->mtx (this should be ok) */
2399
mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2401
else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2402
/* skip... incompatable spacetype */
2406
/* get constraint targets if needed */
2407
BKE_get_constraint_targets_for_solving(con, &cob, &targets, ctime);
2410
cti->evaluate_constraint(con, &cob, &targets);
2412
/* convert spaces again */
2413
if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2414
/* just multiply by td->smtx (this should be ok) */
2415
mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2418
/* free targets list */
2419
BLI_freelistN(&targets);
2423
/* copy results from cob->matrix */
2424
copy_v3_v3(td->loc, cob.matrix[3]);
2428
static void constraintob_from_transdata(bConstraintOb *cob, TransData *td)
2430
/* Make a temporary bConstraintOb for use by limit constraints
2431
* - they only care that cob->matrix is correctly set ;-)
2432
* - current space should be local
2434
memset(cob, 0, sizeof(bConstraintOb));
2436
if (td->ext->rotOrder == ROT_MODE_QUAT) {
2438
/* objects and bones do normalization first too, otherwise
2439
* we don't necessarily end up with a rotation matrix, and
2440
* then conversion back to quat gives a different result */
2442
normalize_qt_qt(quat, td->ext->quat);
2443
quat_to_mat4(cob->matrix, quat);
2445
else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2447
axis_angle_to_mat4(cob->matrix, &td->ext->quat[1], td->ext->quat[0]);
2451
eulO_to_mat4(cob->matrix, td->ext->rot, td->ext->rotOrder);
2456
static void constraintRotLim(TransInfo *UNUSED(t), TransData *td)
2459
bConstraintTypeInfo *cti = BKE_get_constraint_typeinfo(CONSTRAINT_TYPE_ROTLIMIT);
2462
int do_limit = FALSE;
2464
/* Evaluate valid constraints */
2465
for (con = td->con; con; con = con->next) {
2466
/* only consider constraint if enabled */
2467
if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2468
if (con->enforce == 0.0f) continue;
2470
/* we're only interested in Limit-Rotation constraints */
2471
if (con->type == CONSTRAINT_TYPE_ROTLIMIT) {
2472
bRotLimitConstraint *data = con->data;
2474
/* only use it if it's tagged for this purpose */
2475
if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2478
/* skip incompatable spacetypes */
2479
if (!ELEM(con->ownspace, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL))
2482
/* only do conversion if necessary, to preserve quats and eulers */
2483
if (do_limit == FALSE) {
2484
constraintob_from_transdata(&cob, td);
2488
/* do space conversions */
2489
if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2490
/* just multiply by td->mtx (this should be ok) */
2491
mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2495
cti->evaluate_constraint(con, &cob, NULL);
2497
/* convert spaces again */
2498
if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2499
/* just multiply by td->smtx (this should be ok) */
2500
mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2506
/* copy results from cob->matrix */
2507
if (td->ext->rotOrder == ROT_MODE_QUAT) {
2509
mat4_to_quat(td->ext->quat, cob.matrix);
2511
else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
2513
mat4_to_axis_angle(&td->ext->quat[1], &td->ext->quat[0], cob.matrix);
2517
mat4_to_eulO(td->ext->rot, td->ext->rotOrder, cob.matrix);
2523
static void constraintSizeLim(TransInfo *t, TransData *td)
2525
if (td->con && td->ext) {
2526
bConstraintTypeInfo *cti = BKE_get_constraint_typeinfo(CONSTRAINT_TYPE_SIZELIMIT);
2527
bConstraintOb cob = {NULL};
2529
float size_sign[3], size_abs[3];
2532
/* Make a temporary bConstraintOb for using these limit constraints
2533
* - they only care that cob->matrix is correctly set ;-)
2534
* - current space should be local
2536
if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2537
/* scale val and reset size */
2538
return; // TODO: fix this case
2541
/* Reset val if SINGLESIZE but using a constraint */
2542
if (td->flag & TD_SINGLESIZE)
2545
/* separate out sign to apply back later */
2546
for (i = 0; i < 3; i++) {
2547
size_sign[i] = signf(td->ext->size[i]);
2548
size_abs[i] = fabsf(td->ext->size[i]);
2551
size_to_mat4(cob.matrix, size_abs);
2554
/* Evaluate valid constraints */
2555
for (con = td->con; con; con = con->next) {
2556
/* only consider constraint if enabled */
2557
if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) continue;
2558
if (con->enforce == 0.0f) continue;
2560
/* we're only interested in Limit-Scale constraints */
2561
if (con->type == CONSTRAINT_TYPE_SIZELIMIT) {
2562
bSizeLimitConstraint *data = con->data;
2564
/* only use it if it's tagged for this purpose */
2565
if ((data->flag2 & LIMIT_TRANSFORM) == 0)
2568
/* do space conversions */
2569
if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2570
/* just multiply by td->mtx (this should be ok) */
2571
mul_m4_m3m4(cob.matrix, td->mtx, cob.matrix);
2573
else if (con->ownspace != CONSTRAINT_SPACE_LOCAL) {
2574
/* skip... incompatible spacetype */
2579
cti->evaluate_constraint(con, &cob, NULL);
2581
/* convert spaces again */
2582
if (con->ownspace == CONSTRAINT_SPACE_WORLD) {
2583
/* just multiply by td->smtx (this should be ok) */
2584
mul_m4_m3m4(cob.matrix, td->smtx, cob.matrix);
2589
/* copy results from cob->matrix */
2590
if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
2591
/* scale val and reset size */
2592
return; // TODO: fix this case
2595
/* Reset val if SINGLESIZE but using a constraint */
2596
if (td->flag & TD_SINGLESIZE)
2599
/* extrace scale from matrix and apply back sign */
2600
mat4_to_size(td->ext->size, cob.matrix);
2601
mul_v3_v3(td->ext->size, size_sign);
2606
/* ************************** WARP *************************** */
2608
static void postInputWarp(TransInfo *t, float values[3])
2610
mul_v3_fl(values, (float)(M_PI * 2));
2612
if (t->customData) { /* non-null value indicates reversed input */
2617
void initWarp(TransInfo *t)
2619
float max[3], min[3];
2623
t->transform = Warp;
2624
t->handleEvent = handleEventWarp;
2626
setInputPostFct(&t->mouse, postInputWarp);
2627
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO);
2632
t->snap[1] = 5.0f / 180.0f * (float)M_PI;
2633
t->snap[2] = 1.0f / 180.0f * (float)M_PI;
2635
t->num.increment = 1.0f;
2637
t->flag |= T_NO_CONSTRAINT;
2639
/* we need min/max in view space */
2640
for (i = 0; i < t->total; i++) {
2642
copy_v3_v3(center, t->data[i].center);
2643
mul_m3_v3(t->data[i].mtx, center);
2644
mul_m4_v3(t->viewmat, center);
2645
sub_v3_v3(center, t->viewmat[3]);
2647
minmax_v3v3_v3(min, max, center);
2650
copy_v3_v3(max, center);
2651
copy_v3_v3(min, center);
2655
mid_v3_v3v3(t->center, min, max);
2657
if (max[0] == min[0]) max[0] += 0.1f; /* not optimal, but flipping is better than invalid garbage (i.e. division by zero!) */
2658
t->val = (max[0] - min[0]) / 2.0f; /* t->val is X dimension projected boundbox */
2661
int handleEventWarp(TransInfo *t, wmEvent *event)
2665
if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
2666
// Use customData pointer to signal warp direction
2667
if (t->customData == NULL)
2668
t->customData = (void *)1;
2670
t->customData = NULL;
2678
int Warp(TransInfo *t, const int UNUSED(mval[2]))
2680
TransData *td = t->data;
2681
float vec[3], circumfac, dist, phi0, co, si, cursor[3], gcursor[3];
2686
curs = give_cursor(t->scene, t->view);
2688
* gcursor is the one used for helpline.
2689
* It has to be in the same space as the drawing loop
2690
* (that means it needs to be in the object's space when in edit mode and
2691
* in global space in object mode)
2693
* cursor is used for calculations.
2694
* It needs to be in view space, but we need to take object's offset
2695
* into account if in Edit mode.
2697
copy_v3_v3(cursor, curs);
2698
copy_v3_v3(gcursor, cursor);
2699
if (t->flag & T_EDIT) {
2700
sub_v3_v3(cursor, t->obedit->obmat[3]);
2701
sub_v3_v3(gcursor, t->obedit->obmat[3]);
2702
mul_m3_v3(t->data->smtx, gcursor);
2704
mul_m4_v3(t->viewmat, cursor);
2705
sub_v3_v3(cursor, t->viewmat[3]);
2707
/* amount of radians for warp */
2708
circumfac = t->values[0];
2710
snapGrid(t, &circumfac);
2711
applyNumInput(&t->num, &circumfac);
2713
/* header print for NumInput */
2714
if (hasNumInput(&t->num)) {
2715
char c[NUM_STR_REP_LEN];
2717
outputNumInput(&(t->num), c);
2719
sprintf(str, IFACE_("Warp: %s"), c);
2721
circumfac = DEG2RADF(circumfac);
2724
/* default header print */
2725
sprintf(str, IFACE_("Warp: %.3f"), RAD2DEGF(circumfac));
2728
t->values[0] = circumfac;
2730
circumfac /= 2; /* only need 180 on each side to make 360 */
2732
for (i = 0; i < t->total; i++, td++) {
2734
if (td->flag & TD_NOACTION)
2737
if (td->flag & TD_SKIP)
2740
/* translate point to center, rotate in such a way that outline==distance */
2741
copy_v3_v3(vec, td->iloc);
2742
mul_m3_v3(td->mtx, vec);
2743
mul_m4_v3(t->viewmat, vec);
2744
sub_v3_v3(vec, t->viewmat[3]);
2746
dist = vec[0] - cursor[0];
2748
/* t->val is X dimension projected boundbox */
2749
phi0 = (circumfac * dist / t->val);
2751
vec[1] = (vec[1] - cursor[1]);
2753
co = (float)cos(phi0);
2754
si = (float)sin(phi0);
2755
loc[0] = -si * vec[1] + cursor[0];
2756
loc[1] = co * vec[1] + cursor[1];
2759
mul_m4_v3(t->viewinv, loc);
2760
sub_v3_v3(loc, t->viewinv[3]);
2761
mul_m3_v3(td->smtx, loc);
2763
sub_v3_v3(loc, td->iloc);
2764
mul_v3_fl(loc, td->factor);
2765
add_v3_v3v3(td->loc, td->iloc, loc);
2770
ED_area_headerprint(t->sa, str);
2775
/* ************************** SHEAR *************************** */
2777
static void postInputShear(TransInfo *UNUSED(t), float values[3])
2779
mul_v3_fl(values, 0.05f);
2782
void initShear(TransInfo *t)
2784
t->mode = TFM_SHEAR;
2785
t->transform = Shear;
2786
t->handleEvent = handleEventShear;
2788
setInputPostFct(&t->mouse, postInputShear);
2789
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE);
2795
t->snap[2] = t->snap[1] * 0.1f;
2797
t->num.increment = 0.1f;
2799
t->flag |= T_NO_CONSTRAINT;
2802
int handleEventShear(TransInfo *t, wmEvent *event)
2806
if (event->type == MIDDLEMOUSE && event->val == KM_PRESS) {
2807
// Use customData pointer to signal Shear direction
2808
if (t->customData == NULL) {
2809
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
2810
t->customData = (void *)1;
2813
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE);
2814
t->customData = NULL;
2819
else if (event->type == XKEY && event->val == KM_PRESS) {
2820
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE);
2821
t->customData = NULL;
2825
else if (event->type == YKEY && event->val == KM_PRESS) {
2826
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
2827
t->customData = (void *)1;
2836
int Shear(TransInfo *t, const int UNUSED(mval[2]))
2838
TransData *td = t->data;
2840
float smat[3][3], tmat[3][3], totmat[3][3], persmat[3][3], persinv[3][3];
2845
copy_m3_m4(persmat, t->viewmat);
2846
invert_m3_m3(persinv, persmat);
2848
value = t->values[0];
2850
snapGrid(t, &value);
2852
applyNumInput(&t->num, &value);
2854
/* header print for NumInput */
2855
if (hasNumInput(&t->num)) {
2856
char c[NUM_STR_REP_LEN];
2858
outputNumInput(&(t->num), c);
2860
sprintf(str, IFACE_("Shear: %s %s"), c, t->proptext);
2863
/* default header print */
2864
sprintf(str, IFACE_("Shear: %.3f %s (Press X or Y to set shear axis)"), value, t->proptext);
2867
t->values[0] = value;
2871
// Custom data signals shear direction
2872
if (t->customData == NULL)
2877
mul_m3_m3m3(tmat, smat, persmat);
2878
mul_m3_m3m3(totmat, persinv, tmat);
2880
for (i = 0; i < t->total; i++, td++) {
2881
if (td->flag & TD_NOACTION)
2884
if (td->flag & TD_SKIP)
2889
mul_m3_m3m3(mat3, totmat, td->mtx);
2890
mul_m3_m3m3(tmat, td->smtx, mat3);
2893
copy_m3_m3(tmat, totmat);
2895
sub_v3_v3v3(vec, td->center, t->center);
2897
mul_m3_v3(tmat, vec);
2899
add_v3_v3(vec, t->center);
2900
sub_v3_v3(vec, td->center);
2902
mul_v3_fl(vec, td->factor);
2904
add_v3_v3v3(td->loc, td->iloc, vec);
2909
ED_area_headerprint(t->sa, str);
2914
/* ************************** RESIZE *************************** */
2916
void initResize(TransInfo *t)
2918
t->mode = TFM_RESIZE;
2919
t->transform = Resize;
2921
initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
2923
t->flag |= T_NULL_ONE;
2924
t->num.flag |= NUM_NULL_ONE;
2925
t->num.flag |= NUM_AFFECT_ALL;
2927
t->flag |= T_NO_ZERO;
2928
t->num.flag |= NUM_NO_ZERO;
2935
t->snap[2] = t->snap[1] * 0.1f;
2937
t->num.increment = t->snap[1];
2940
static void headerResize(TransInfo *t, float vec[3], char *str)
2942
char tvec[NUM_STR_REP_LEN * 3];
2944
if (hasNumInput(&t->num)) {
2945
outputNumInput(&(t->num), tvec);
2948
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", vec[0]);
2949
BLI_snprintf(&tvec[NUM_STR_REP_LEN], NUM_STR_REP_LEN, "%.4f", vec[1]);
2950
BLI_snprintf(&tvec[NUM_STR_REP_LEN * 2], NUM_STR_REP_LEN, "%.4f", vec[2]);
2953
if (t->con.mode & CON_APPLY) {
2954
switch (t->num.idx_max) {
2956
spos += sprintf(spos, IFACE_("Scale: %s%s %s"), &tvec[0], t->con.text, t->proptext);
2959
spos += sprintf(spos, IFACE_("Scale: %s : %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN],
2960
t->con.text, t->proptext);
2963
spos += sprintf(spos, IFACE_("Scale: %s : %s : %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN],
2964
&tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext);
2968
if (t->flag & T_2D_EDIT) {
2969
spos += sprintf(spos, IFACE_("Scale X: %s Y: %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN],
2970
t->con.text, t->proptext);
2973
spos += sprintf(spos, IFACE_("Scale X: %s Y: %s Z: %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN],
2974
&tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext);
2978
if (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
2979
spos += sprintf(spos, IFACE_(" Proportional size: %.2f"), t->prop_size);
2985
/* FLT_EPSILON is too small [#29633], 0.0000001f starts to flip */
2986
#define TX_FLIP_EPS 0.00001f
2987
BLI_INLINE int tx_sign(const float a)
2989
return (a < -TX_FLIP_EPS ? 1 : a > TX_FLIP_EPS ? 2 : 3);
2991
BLI_INLINE int tx_vec_sign_flip(const float a[3], const float b[3])
2993
return ((tx_sign(a[0]) & tx_sign(b[0])) == 0 ||
2994
(tx_sign(a[1]) & tx_sign(b[1])) == 0 ||
2995
(tx_sign(a[2]) & tx_sign(b[2])) == 0);
2998
/* smat is reference matrix, only scaled */
2999
static void TransMat3ToSize(float mat[3][3], float smat[3][3], float size[3])
3003
copy_v3_v3(vec, mat[0]);
3004
size[0] = normalize_v3(vec);
3005
copy_v3_v3(vec, mat[1]);
3006
size[1] = normalize_v3(vec);
3007
copy_v3_v3(vec, mat[2]);
3008
size[2] = normalize_v3(vec);
3010
/* first tried with dotproduct... but the sign flip is crucial */
3011
if (tx_vec_sign_flip(mat[0], smat[0]) ) size[0] = -size[0];
3012
if (tx_vec_sign_flip(mat[1], smat[1]) ) size[1] = -size[1];
3013
if (tx_vec_sign_flip(mat[2], smat[2]) ) size[2] = -size[2];
3017
static void ElementResize(TransInfo *t, TransData *td, float mat[3][3])
3019
float tmat[3][3], smat[3][3], center[3];
3022
if (t->flag & T_EDIT) {
3023
mul_m3_m3m3(smat, mat, td->mtx);
3024
mul_m3_m3m3(tmat, td->smtx, smat);
3027
copy_m3_m3(tmat, mat);
3030
if (t->con.applySize) {
3031
t->con.applySize(t, td, tmat);
3034
/* local constraint shouldn't alter center */
3035
if (transdata_check_local_center(t)) {
3036
copy_v3_v3(center, td->center);
3038
else if (t->options & CTX_MOVIECLIP) {
3039
copy_v3_v3(center, td->center);
3042
copy_v3_v3(center, t->center);
3048
if (t->flag & (T_OBJECT | T_TEXTURE | T_POSE)) {
3049
float obsizemat[3][3];
3050
/* Reorient the size mat to fit the oriented object. */
3051
mul_m3_m3m3(obsizemat, tmat, td->axismtx);
3052
/* print_m3("obsizemat", obsizemat); */
3053
TransMat3ToSize(obsizemat, td->axismtx, fsize);
3054
/* print_v3("fsize", fsize); */
3057
mat3_to_size(fsize, tmat);
3060
protectedSizeBits(td->protectflag, fsize);
3062
if ((t->flag & T_V3D_ALIGN) == 0) { /* align mode doesn't resize objects itself */
3063
if ((td->flag & TD_SINGLESIZE) && !(t->con.mode & CON_APPLY)) {
3064
/* scale val and reset size */
3065
*td->val = td->ival * (1 + (fsize[0] - 1) * td->factor);
3067
td->ext->size[0] = td->ext->isize[0];
3068
td->ext->size[1] = td->ext->isize[1];
3069
td->ext->size[2] = td->ext->isize[2];
3072
/* Reset val if SINGLESIZE but using a constraint */
3073
if (td->flag & TD_SINGLESIZE)
3074
*td->val = td->ival;
3076
td->ext->size[0] = td->ext->isize[0] * (1 + (fsize[0] - 1) * td->factor);
3077
td->ext->size[1] = td->ext->isize[1] * (1 + (fsize[1] - 1) * td->factor);
3078
td->ext->size[2] = td->ext->isize[2] * (1 + (fsize[2] - 1) * td->factor);
3082
constraintSizeLim(t, td);
3085
/* For individual element center, Editmode need to use iloc */
3086
if (t->flag & T_POINTS)
3087
sub_v3_v3v3(vec, td->iloc, center);
3089
sub_v3_v3v3(vec, td->center, center);
3091
mul_m3_v3(tmat, vec);
3093
add_v3_v3(vec, center);
3094
if (t->flag & T_POINTS)
3095
sub_v3_v3(vec, td->iloc);
3097
sub_v3_v3(vec, td->center);
3099
mul_v3_fl(vec, td->factor);
3101
if (t->flag & (T_OBJECT | T_POSE)) {
3102
mul_m3_v3(td->smtx, vec);
3105
protectedTransBits(td->protectflag, vec);
3106
add_v3_v3v3(td->loc, td->iloc, vec);
3108
constraintTransLim(t, td);
3111
int Resize(TransInfo *t, const int mval[2])
3114
float size[3], mat[3][3];
3119
/* for manipulator, center handle, the scaling can't be done relative to center */
3120
if ((t->flag & T_USES_MANIPULATOR) && t->con.mode == 0) {
3121
ratio = 1.0f - ((t->imval[0] - mval[0]) + (t->imval[1] - mval[1])) / 100.0f;
3124
ratio = t->values[0];
3127
size[0] = size[1] = size[2] = ratio;
3131
if (hasNumInput(&t->num)) {
3132
applyNumInput(&t->num, size);
3133
constraintNumInput(t, size);
3136
applySnapping(t, size);
3138
if (t->flag & T_AUTOVALUES) {
3139
copy_v3_v3(size, t->auto_values);
3142
copy_v3_v3(t->values, size);
3144
size_to_mat3(mat, size);
3146
if (t->con.applySize) {
3147
t->con.applySize(t, NULL, mat);
3150
copy_m3_m3(t->mat, mat); // used in manipulator
3152
headerResize(t, size, str);
3154
for (i = 0, td = t->data; i < t->total; i++, td++) {
3155
if (td->flag & TD_NOACTION)
3158
if (td->flag & TD_SKIP)
3161
ElementResize(t, td, mat);
3164
/* evil hack - redo resize if cliping needed */
3165
if (t->flag & T_CLIP_UV && clipUVTransform(t, size, 1)) {
3166
size_to_mat3(mat, size);
3168
if (t->con.applySize)
3169
t->con.applySize(t, NULL, mat);
3171
for (i = 0, td = t->data; i < t->total; i++, td++)
3172
ElementResize(t, td, mat);
3174
/* In proportional edit it can happen that */
3175
/* vertices in the radius of the brush end */
3176
/* outside the clipping area */
3178
if (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
3185
ED_area_headerprint(t->sa, str);
3190
/* ************************** SKIN *************************** */
3192
void initSkinResize(TransInfo *t)
3194
t->mode = TFM_SKIN_RESIZE;
3195
t->transform = SkinResize;
3197
initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
3199
t->flag |= T_NULL_ONE;
3200
t->num.flag |= NUM_NULL_ONE;
3201
t->num.flag |= NUM_AFFECT_ALL;
3203
t->flag |= T_NO_ZERO;
3204
t->num.flag |= NUM_NO_ZERO;
3211
t->snap[2] = t->snap[1] * 0.1f;
3213
t->num.increment = t->snap[1];
3216
int SkinResize(TransInfo *t, const int UNUSED(mval[2]))
3219
float size[3], mat[3][3];
3224
ratio = t->values[0];
3225
size[0] = size[1] = size[2] = ratio;
3229
if (hasNumInput(&t->num)) {
3230
applyNumInput(&t->num, size);
3231
constraintNumInput(t, size);
3234
applySnapping(t, size);
3236
if (t->flag & T_AUTOVALUES) {
3237
copy_v3_v3(size, t->auto_values);
3240
copy_v3_v3(t->values, size);
3242
size_to_mat3(mat, size);
3244
headerResize(t, size, str);
3246
for (i = 0, td = t->data; i < t->total; i++, td++) {
3247
float tmat[3][3], smat[3][3];
3250
if (td->flag & TD_NOACTION)
3253
if (td->flag & TD_SKIP)
3256
if (t->flag & T_EDIT) {
3257
mul_m3_m3m3(smat, mat, td->mtx);
3258
mul_m3_m3m3(tmat, td->smtx, smat);
3261
copy_m3_m3(tmat, mat);
3264
if (t->con.applySize) {
3265
t->con.applySize(t, NULL, tmat);
3268
mat3_to_size(fsize, tmat);
3269
td->val[0] = td->ext->isize[0] * (1 + (fsize[0] - 1) * td->factor);
3270
td->val[1] = td->ext->isize[1] * (1 + (fsize[1] - 1) * td->factor);
3275
ED_area_headerprint(t->sa, str);
3280
/* ************************** TOSPHERE *************************** */
3282
void initToSphere(TransInfo *t)
3284
TransData *td = t->data;
3287
t->mode = TFM_TOSPHERE;
3288
t->transform = ToSphere;
3290
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_RATIO);
3296
t->snap[2] = t->snap[1] * 0.1f;
3298
t->num.increment = t->snap[1];
3300
t->num.flag |= NUM_NULL_ONE | NUM_NO_NEGATIVE;
3301
t->flag |= T_NO_CONSTRAINT;
3303
// Calculate average radius
3304
for (i = 0; i < t->total; i++, td++) {
3305
t->val += len_v3v3(t->center, td->iloc);
3308
t->val /= (float)t->total;
3311
int ToSphere(TransInfo *t, const int UNUSED(mval[2]))
3314
float ratio, radius;
3317
TransData *td = t->data;
3319
ratio = t->values[0];
3321
snapGrid(t, &ratio);
3323
applyNumInput(&t->num, &ratio);
3330
t->values[0] = ratio;
3332
/* header print for NumInput */
3333
if (hasNumInput(&t->num)) {
3334
char c[NUM_STR_REP_LEN];
3336
outputNumInput(&(t->num), c);
3338
sprintf(str, IFACE_("To Sphere: %s %s"), c, t->proptext);
3341
/* default header print */
3342
sprintf(str, IFACE_("To Sphere: %.4f %s"), ratio, t->proptext);
3346
for (i = 0; i < t->total; i++, td++) {
3348
if (td->flag & TD_NOACTION)
3351
if (td->flag & TD_SKIP)
3354
sub_v3_v3v3(vec, td->iloc, t->center);
3356
radius = normalize_v3(vec);
3358
tratio = ratio * td->factor;
3360
mul_v3_fl(vec, radius * (1.0f - tratio) + t->val * tratio);
3362
add_v3_v3v3(td->loc, t->center, vec);
3368
ED_area_headerprint(t->sa, str);
3373
/* ************************** ROTATION *************************** */
3376
static void postInputRotation(TransInfo *t, float values[3])
3378
if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
3379
t->con.applyRot(t, NULL, t->axis, values);
3383
void initRotation(TransInfo *t)
3385
t->mode = TFM_ROTATION;
3386
t->transform = Rotation;
3388
setInputPostFct(&t->mouse, postInputRotation);
3389
initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
3394
t->snap[1] = (float)((5.0 / 180) * M_PI);
3395
t->snap[2] = t->snap[1] * 0.2f;
3397
t->num.increment = 1.0f;
3399
if (t->flag & T_2D_EDIT)
3400
t->flag |= T_NO_CONSTRAINT;
3402
negate_v3_v3(t->axis, t->viewinv[2]);
3403
normalize_v3(t->axis);
3405
copy_v3_v3(t->axis_orig, t->axis);
3408
static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around)
3410
float vec[3], totmat[3][3], smat[3][3];
3411
float eul[3], fmat[3][3], quat[4];
3412
const float *center;
3414
/* local constraint shouldn't alter center */
3415
if (transdata_check_local_center(t) ||
3416
((around == V3D_LOCAL) && (t->options & CTX_MOVIECLIP)))
3418
center = td->center;
3424
if (t->flag & T_POINTS) {
3425
mul_m3_m3m3(totmat, mat, td->mtx);
3426
mul_m3_m3m3(smat, td->smtx, totmat);
3428
sub_v3_v3v3(vec, td->iloc, center);
3429
mul_m3_v3(smat, vec);
3431
add_v3_v3v3(td->loc, vec, center);
3433
sub_v3_v3v3(vec, td->loc, td->iloc);
3434
protectedTransBits(td->protectflag, vec);
3435
add_v3_v3v3(td->loc, td->iloc, vec);
3438
if (td->flag & TD_USEQUAT) {
3439
mul_serie_m3(fmat, td->mtx, mat, td->smtx, NULL, NULL, NULL, NULL, NULL);
3440
mat3_to_quat(quat, fmat); // Actual transform
3442
if (td->ext->quat) {
3443
mul_qt_qtqt(td->ext->quat, quat, td->ext->iquat);
3445
/* is there a reason not to have this here? -jahka */
3446
protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat);
3453
* This is some VERY ugly special case to deal with pose mode.
3455
* The problem is that mtx and smtx include each bone orientation.
3457
* That is needed to rotate each bone properly, HOWEVER, to calculate
3458
* the translation component, we only need the actual armature object's
3459
* matrix (and inverse). That is not all though. Once the proper translation
3460
* has been computed, it has to be converted back into the bone's space.
3462
else if (t->flag & T_POSE) {
3463
float pmtx[3][3], imtx[3][3];
3465
// Extract and invert armature object matrix
3466
copy_m3_m4(pmtx, t->poseobj->obmat);
3467
invert_m3_m3(imtx, pmtx);
3469
if ((td->flag & TD_NO_LOC) == 0) {
3470
sub_v3_v3v3(vec, td->center, center);
3472
mul_m3_v3(pmtx, vec); // To Global space
3473
mul_m3_v3(mat, vec); // Applying rotation
3474
mul_m3_v3(imtx, vec); // To Local space
3476
add_v3_v3(vec, center);
3477
/* vec now is the location where the object has to be */
3479
sub_v3_v3v3(vec, vec, td->center); // Translation needed from the initial location
3481
/* special exception, see TD_PBONE_LOCAL_MTX definition comments */
3482
if (td->flag & TD_PBONE_LOCAL_MTX_P) {
3485
else if (td->flag & TD_PBONE_LOCAL_MTX_C) {
3486
mul_m3_v3(pmtx, vec); // To Global space
3487
mul_m3_v3(td->ext->l_smtx, vec); // To Pose space (Local Location)
3490
mul_m3_v3(pmtx, vec); // To Global space
3491
mul_m3_v3(td->smtx, vec); // To Pose space
3494
protectedTransBits(td->protectflag, vec);
3496
add_v3_v3v3(td->loc, td->iloc, vec);
3498
constraintTransLim(t, td);
3502
/* MORE HACK: as in some cases the matrix to apply location and rot/scale is not the same,
3503
* and ElementRotation() might be called in Translation context (with align snapping),
3504
* we need to be sure to actually use the *rotation* matrix here...
3505
* So no other way than storing it in some dedicated members of td->ext! */
3506
if ((t->flag & T_V3D_ALIGN) == 0) { /* align mode doesn't rotate objects itself */
3507
/* euler or quaternion/axis-angle? */
3508
if (td->ext->rotOrder == ROT_MODE_QUAT) {
3509
mul_serie_m3(fmat, td->ext->r_mtx, mat, td->ext->r_smtx, NULL, NULL, NULL, NULL, NULL);
3511
mat3_to_quat(quat, fmat); /* Actual transform */
3513
mul_qt_qtqt(td->ext->quat, quat, td->ext->iquat);
3514
/* this function works on end result */
3515
protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat);
3518
else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
3519
/* calculate effect based on quats */
3520
float iquat[4], tquat[4];
3522
axis_angle_to_quat(iquat, td->ext->irotAxis, td->ext->irotAngle);
3524
mul_serie_m3(fmat, td->ext->r_mtx, mat, td->ext->r_smtx, NULL, NULL, NULL, NULL, NULL);
3525
mat3_to_quat(quat, fmat); /* Actual transform */
3526
mul_qt_qtqt(tquat, quat, iquat);
3528
quat_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, tquat);
3530
/* this function works on end result */
3531
protectedAxisAngleBits(td->protectflag, td->ext->rotAxis, td->ext->rotAngle, td->ext->irotAxis, td->ext->irotAngle);
3536
mul_m3_m3m3(totmat, mat, td->ext->r_mtx);
3537
mul_m3_m3m3(smat, td->ext->r_smtx, totmat);
3539
/* calculate the total rotatation in eulers */
3540
copy_v3_v3(eul, td->ext->irot);
3541
eulO_to_mat3(eulmat, eul, td->ext->rotOrder);
3543
/* mat = transform, obmat = bone rotation */
3544
mul_m3_m3m3(fmat, smat, eulmat);
3546
mat3_to_compatible_eulO(eul, td->ext->rot, td->ext->rotOrder, fmat);
3548
/* and apply (to end result only) */
3549
protectedRotateBits(td->protectflag, eul, td->ext->irot);
3550
copy_v3_v3(td->ext->rot, eul);
3553
constraintRotLim(t, td);
3557
if ((td->flag & TD_NO_LOC) == 0) {
3559
sub_v3_v3v3(vec, td->center, center);
3560
mul_m3_v3(mat, vec);
3561
add_v3_v3(vec, center);
3562
/* vec now is the location where the object has to be */
3563
sub_v3_v3(vec, td->center);
3564
mul_m3_v3(td->smtx, vec);
3566
protectedTransBits(td->protectflag, vec);
3568
add_v3_v3v3(td->loc, td->iloc, vec);
3572
constraintTransLim(t, td);
3575
if ((t->flag & T_V3D_ALIGN) == 0) { // align mode doesn't rotate objects itself
3576
/* euler or quaternion? */
3577
if ((td->ext->rotOrder == ROT_MODE_QUAT) || (td->flag & TD_USEQUAT)) {
3578
mul_serie_m3(fmat, td->mtx, mat, td->smtx, NULL, NULL, NULL, NULL, NULL);
3579
mat3_to_quat(quat, fmat); // Actual transform
3581
mul_qt_qtqt(td->ext->quat, quat, td->ext->iquat);
3582
/* this function works on end result */
3583
protectedQuaternionBits(td->protectflag, td->ext->quat, td->ext->iquat);
3585
else if (td->ext->rotOrder == ROT_MODE_AXISANGLE) {
3586
/* calculate effect based on quats */
3587
float iquat[4], tquat[4];
3589
axis_angle_to_quat(iquat, td->ext->irotAxis, td->ext->irotAngle);
3591
mul_serie_m3(fmat, td->mtx, mat, td->smtx, NULL, NULL, NULL, NULL, NULL);
3592
mat3_to_quat(quat, fmat); // Actual transform
3593
mul_qt_qtqt(tquat, quat, iquat);
3595
quat_to_axis_angle(td->ext->rotAxis, td->ext->rotAngle, tquat);
3597
/* this function works on end result */
3598
protectedAxisAngleBits(td->protectflag, td->ext->rotAxis, td->ext->rotAngle, td->ext->irotAxis, td->ext->irotAngle);
3603
mul_m3_m3m3(totmat, mat, td->mtx);
3604
mul_m3_m3m3(smat, td->smtx, totmat);
3606
/* calculate the total rotatation in eulers */
3607
add_v3_v3v3(eul, td->ext->irot, td->ext->drot); /* we have to correct for delta rot */
3608
eulO_to_mat3(obmat, eul, td->ext->rotOrder);
3609
/* mat = transform, obmat = object rotation */
3610
mul_m3_m3m3(fmat, smat, obmat);
3612
mat3_to_compatible_eulO(eul, td->ext->rot, td->ext->rotOrder, fmat);
3614
/* correct back for delta rot */
3615
sub_v3_v3v3(eul, eul, td->ext->drot);
3618
protectedRotateBits(td->protectflag, eul, td->ext->irot);
3619
copy_v3_v3(td->ext->rot, eul);
3622
constraintRotLim(t, td);
3627
static void applyRotation(TransInfo *t, float angle, float axis[3])
3629
TransData *td = t->data;
3633
vec_rot_to_mat3(mat, axis, angle);
3635
for (i = 0; i < t->total; i++, td++) {
3637
if (td->flag & TD_NOACTION)
3640
if (td->flag & TD_SKIP)
3643
if (t->con.applyRot) {
3644
t->con.applyRot(t, td, axis, NULL);
3645
vec_rot_to_mat3(mat, axis, angle * td->factor);
3647
else if (t->flag & T_PROP_EDIT) {
3648
vec_rot_to_mat3(mat, axis, angle * td->factor);
3651
ElementRotation(t, td, mat, t->around);
3655
int Rotation(TransInfo *t, const int UNUSED(mval[2]))
3657
char str[128], *spos = str;
3661
final = t->values[0];
3663
snapGrid(t, &final);
3665
if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
3666
t->con.applyRot(t, NULL, t->axis, NULL);
3669
/* reset axis if constraint is not set */
3670
copy_v3_v3(t->axis, t->axis_orig);
3673
applySnapping(t, &final);
3675
if (hasNumInput(&t->num)) {
3676
char c[NUM_STR_REP_LEN];
3678
applyNumInput(&t->num, &final);
3680
outputNumInput(&(t->num), c);
3682
spos += sprintf(spos, IFACE_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext);
3684
/* Clamp between -180 and 180 */
3685
final = angle_wrap_rad(DEG2RADF(final));
3688
spos += sprintf(spos, IFACE_("Rot: %.2f%s %s"), RAD2DEGF(final), t->con.text, t->proptext);
3691
if (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
3692
spos += sprintf(spos, IFACE_(" Proportional size: %.2f"), t->prop_size);
3696
t->values[0] = final;
3698
applyRotation(t, final, t->axis);
3702
ED_area_headerprint(t->sa, str);
3708
/* ************************** TRACKBALL *************************** */
3710
void initTrackball(TransInfo *t)
3712
t->mode = TFM_TRACKBALL;
3713
t->transform = Trackball;
3715
initMouseInputMode(t, &t->mouse, INPUT_TRACKBALL);
3720
t->snap[1] = (float)((5.0 / 180) * M_PI);
3721
t->snap[2] = t->snap[1] * 0.2f;
3723
t->num.increment = 1.0f;
3725
t->flag |= T_NO_CONSTRAINT;
3728
static void applyTrackball(TransInfo *t, float axis1[3], float axis2[3], float angles[2])
3730
TransData *td = t->data;
3731
float mat[3][3], smat[3][3], totmat[3][3];
3734
vec_rot_to_mat3(smat, axis1, angles[0]);
3735
vec_rot_to_mat3(totmat, axis2, angles[1]);
3737
mul_m3_m3m3(mat, smat, totmat);
3739
for (i = 0; i < t->total; i++, td++) {
3740
if (td->flag & TD_NOACTION)
3743
if (td->flag & TD_SKIP)
3746
if (t->flag & T_PROP_EDIT) {
3747
vec_rot_to_mat3(smat, axis1, td->factor * angles[0]);
3748
vec_rot_to_mat3(totmat, axis2, td->factor * angles[1]);
3750
mul_m3_m3m3(mat, smat, totmat);
3753
ElementRotation(t, td, mat, t->around);
3757
int Trackball(TransInfo *t, const int UNUSED(mval[2]))
3759
char str[128], *spos = str;
3760
float axis1[3], axis2[3];
3761
float mat[3][3], totmat[3][3], smat[3][3];
3764
copy_v3_v3(axis1, t->persinv[0]);
3765
copy_v3_v3(axis2, t->persinv[1]);
3766
normalize_v3(axis1);
3767
normalize_v3(axis2);
3769
phi[0] = t->values[0];
3770
phi[1] = t->values[1];
3774
if (hasNumInput(&t->num)) {
3775
char c[NUM_STR_REP_LEN * 2];
3777
applyNumInput(&t->num, phi);
3779
outputNumInput(&(t->num), c);
3781
spos += sprintf(spos, IFACE_("Trackball: %s %s %s"), &c[0], &c[NUM_STR_REP_LEN], t->proptext);
3783
phi[0] = DEG2RADF(phi[0]);
3784
phi[1] = DEG2RADF(phi[1]);
3787
spos += sprintf(spos, IFACE_("Trackball: %.2f %.2f %s"), RAD2DEGF(phi[0]), RAD2DEGF(phi[1]), t->proptext);
3790
if (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
3791
spos += sprintf(spos, IFACE_(" Proportional size: %.2f"), t->prop_size);
3795
vec_rot_to_mat3(smat, axis1, phi[0]);
3796
vec_rot_to_mat3(totmat, axis2, phi[1]);
3798
mul_m3_m3m3(mat, smat, totmat);
3801
//copy_m3_m3(t->mat, mat); // used in manipulator
3803
applyTrackball(t, axis1, axis2, phi);
3807
ED_area_headerprint(t->sa, str);
3812
/* ************************** TRANSLATION *************************** */
3814
void initTranslation(TransInfo *t)
3816
if (t->spacetype == SPACE_ACTION) {
3817
/* this space uses time translate */
3818
t->state = TRANS_CANCEL;
3821
t->mode = TFM_TRANSLATION;
3822
t->transform = Translation;
3824
initMouseInputMode(t, &t->mouse, INPUT_VECTOR);
3826
t->idx_max = (t->flag & T_2D_EDIT) ? 1 : 2;
3828
t->num.idx_max = t->idx_max;
3830
if (t->spacetype == SPACE_VIEW3D) {
3831
RegionView3D *rv3d = t->ar->regiondata;
3835
t->snap[1] = rv3d->gridview * 1.0f;
3836
t->snap[2] = t->snap[1] * 0.1f;
3839
else if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_CLIP)) {
3841
t->snap[1] = 0.125f;
3842
t->snap[2] = 0.0625f;
3844
else if (t->spacetype == SPACE_NODE) {
3846
t->snap[1] = 125.0f;
3851
t->snap[1] = t->snap[2] = 1.0f;
3854
t->num.increment = t->snap[1];
3857
static void headerTranslation(TransInfo *t, float vec[3], char *str)
3860
char tvec[NUM_STR_REP_LEN * 3];
3861
char distvec[NUM_STR_REP_LEN];
3862
char autoik[NUM_STR_REP_LEN];
3865
if (hasNumInput(&t->num)) {
3866
outputNumInput(&(t->num), tvec);
3867
dist = len_v3(t->num.val);
3872
copy_v3_v3(dvec, vec);
3873
applyAspectRatio(t, dvec);
3876
if (!(t->flag & T_2D_EDIT) && t->scene->unit.system) {
3877
int i, do_split = t->scene->unit.flag & USER_UNIT_OPT_SPLIT ? 1 : 0;
3879
for (i = 0; i < 3; i++) {
3880
bUnit_AsString(&tvec[i * NUM_STR_REP_LEN], NUM_STR_REP_LEN, dvec[i] * t->scene->unit.scale_length,
3881
4, t->scene->unit.system, B_UNIT_LENGTH, do_split, 1);
3885
sprintf(&tvec[0], "%.4f", dvec[0]);
3886
sprintf(&tvec[NUM_STR_REP_LEN], "%.4f", dvec[1]);
3887
sprintf(&tvec[NUM_STR_REP_LEN * 2], "%.4f", dvec[2]);
3891
if (!(t->flag & T_2D_EDIT) && t->scene->unit.system)
3892
bUnit_AsString(distvec, sizeof(distvec), dist * t->scene->unit.scale_length, 4, t->scene->unit.system, B_UNIT_LENGTH, t->scene->unit.flag & USER_UNIT_OPT_SPLIT, 0);
3893
else if (dist > 1e10f || dist < -1e10f) /* prevent string buffer overflow */
3894
sprintf(distvec, "%.4e", dist);
3896
sprintf(distvec, "%.4f", dist);
3898
if (t->flag & T_AUTOIK) {
3899
short chainlen = t->settings->autoik_chainlen;
3902
sprintf(autoik, IFACE_("AutoIK-Len: %d"), chainlen);
3909
if (t->con.mode & CON_APPLY) {
3910
switch (t->num.idx_max) {
3912
spos += sprintf(spos, "D: %s (%s)%s %s %s", &tvec[0], distvec, t->con.text, t->proptext, &autoik[0]);
3915
spos += sprintf(spos, "D: %s D: %s (%s)%s %s %s", &tvec[0], &tvec[NUM_STR_REP_LEN],
3916
distvec, t->con.text, t->proptext, &autoik[0]);
3919
spos += sprintf(spos, "D: %s D: %s D: %s (%s)%s %s %s", &tvec[0], &tvec[NUM_STR_REP_LEN],
3920
&tvec[NUM_STR_REP_LEN * 2], distvec, t->con.text, t->proptext, &autoik[0]);
3924
if (t->flag & T_2D_EDIT) {
3925
spos += sprintf(spos, "Dx: %s Dy: %s (%s)%s %s", &tvec[0], &tvec[NUM_STR_REP_LEN],
3926
distvec, t->con.text, t->proptext);
3929
spos += sprintf(spos, "Dx: %s Dy: %s Dz: %s (%s)%s %s %s", &tvec[0], &tvec[NUM_STR_REP_LEN],
3930
&tvec[NUM_STR_REP_LEN * 2], distvec, t->con.text, t->proptext, &autoik[0]);
3934
if (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
3935
spos += sprintf(spos, IFACE_(" Proportional size: %.2f"), t->prop_size);
3940
static void applyTranslation(TransInfo *t, float vec[3])
3942
TransData *td = t->data;
3946
for (i = 0; i < t->total; i++, td++) {
3947
if (td->flag & TD_NOACTION)
3950
if (td->flag & TD_SKIP)
3953
/* handle snapping rotation before doing the translation */
3954
if (usingSnappingNormal(t)) {
3955
if (validSnappingNormal(t)) {
3956
float *original_normal;
3962
/* In pose mode, we want to align normals with Y axis of bones... */
3963
if (t->flag & T_POSE)
3964
original_normal = td->axismtx[1];
3966
original_normal = td->axismtx[2];
3968
cross_v3_v3v3(axis, original_normal, t->tsnap.snapNormal);
3969
angle = saacos(dot_v3v3(original_normal, t->tsnap.snapNormal));
3971
axis_angle_to_quat(quat, axis, angle);
3973
quat_to_mat3(mat, quat);
3975
ElementRotation(t, td, mat, V3D_LOCAL);
3982
ElementRotation(t, td, mat, V3D_LOCAL);
3986
if (t->con.applyVec) {
3988
t->con.applyVec(t, td, vec, tvec, pvec);
3991
copy_v3_v3(tvec, vec);
3994
mul_m3_v3(td->smtx, tvec);
3995
mul_v3_fl(tvec, td->factor);
3997
protectedTransBits(td->protectflag, tvec);
4000
add_v3_v3v3(td->loc, td->iloc, tvec);
4002
constraintTransLim(t, td);
4006
/* uses t->vec to store actual translation in */
4007
int Translation(TransInfo *t, const int UNUSED(mval[2]))
4011
if (t->con.mode & CON_APPLY) {
4012
float pvec[3] = {0.0f, 0.0f, 0.0f};
4014
if (hasNumInput(&t->num)) {
4015
removeAspectRatio(t, t->values);
4017
applySnapping(t, t->values);
4018
t->con.applyVec(t, NULL, t->values, tvec, pvec);
4019
copy_v3_v3(t->values, tvec);
4020
headerTranslation(t, pvec, str);
4023
snapGrid(t, t->values);
4024
applyNumInput(&t->num, t->values);
4025
if (hasNumInput(&t->num)) {
4026
removeAspectRatio(t, t->values);
4028
applySnapping(t, t->values);
4029
headerTranslation(t, t->values, str);
4032
applyTranslation(t, t->values);
4034
/* evil hack - redo translation if clipping needed */
4035
if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values, 0)) {
4036
applyTranslation(t, t->values);
4038
/* In proportional edit it can happen that */
4039
/* vertices in the radius of the brush end */
4040
/* outside the clipping area */
4042
if (t->flag & (T_PROP_EDIT | T_PROP_CONNECTED)) {
4049
ED_area_headerprint(t->sa, str);
4054
/* ************************** SHRINK/FATTEN *************************** */
4056
void initShrinkFatten(TransInfo *t)
4058
// If not in mesh edit mode, fallback to Resize
4059
if (t->obedit == NULL || t->obedit->type != OB_MESH) {
4063
t->mode = TFM_SHRINKFATTEN;
4064
t->transform = ShrinkFatten;
4066
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
4072
t->snap[2] = t->snap[1] * 0.1f;
4074
t->num.increment = t->snap[1];
4076
t->flag |= T_NO_CONSTRAINT;
4081
int ShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
4087
TransData *td = t->data;
4089
distance = -t->values[0];
4091
snapGrid(t, &distance);
4093
applyNumInput(&t->num, &distance);
4095
/* header print for NumInput */
4097
str_p += BLI_snprintf(str_p, sizeof(str), IFACE_("Shrink/Fatten:"));
4098
if (hasNumInput(&t->num)) {
4099
char c[NUM_STR_REP_LEN];
4100
outputNumInput(&(t->num), c);
4101
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), " %s", c);
4104
/* default header print */
4105
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), " %.4f", distance);
4108
if (t->proptext[0]) {
4109
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), " %s", t->proptext);
4111
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), ", (");
4114
wmKeyMapItem *kmi = WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_RESIZE);
4116
str_p += WM_keymap_item_to_string(kmi, str_p, sizeof(str) - (str_p - str));
4119
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), IFACE_(" or Alt) Even Thickness %s"),
4120
(t->flag & T_ALT_TRANSFORM) ? IFACE_("ON") : IFACE_("OFF"));
4121
/* done with header string */
4124
t->values[0] = -distance;
4126
for (i = 0; i < t->total; i++, td++) {
4127
float tdistance; /* temp dist */
4128
if (td->flag & TD_NOACTION)
4131
if (td->flag & TD_SKIP)
4134
/* get the final offset */
4135
tdistance = distance * td->factor;
4136
if (td->ext && (t->flag & T_ALT_TRANSFORM)) {
4137
tdistance *= td->ext->isize[0]; /* shell factor */
4140
madd_v3_v3v3fl(td->loc, td->iloc, td->axismtx[2], tdistance);
4145
ED_area_headerprint(t->sa, str);
4150
/* ************************** TILT *************************** */
4152
void initTilt(TransInfo *t)
4155
t->transform = Tilt;
4157
initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
4162
t->snap[1] = (float)((5.0 / 180) * M_PI);
4163
t->snap[2] = t->snap[1] * 0.2f;
4165
t->num.increment = t->snap[1];
4167
t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
4172
int Tilt(TransInfo *t, const int UNUSED(mval[2]))
4174
TransData *td = t->data;
4180
final = t->values[0];
4182
snapGrid(t, &final);
4184
if (hasNumInput(&t->num)) {
4185
char c[NUM_STR_REP_LEN];
4187
applyNumInput(&t->num, &final);
4189
outputNumInput(&(t->num), c);
4191
sprintf(str, IFACE_("Tilt: %s° %s"), &c[0], t->proptext);
4193
final = DEG2RADF(final);
4195
/* XXX For some reason, this seems needed for this op, else RNA prop is not updated... :/ */
4196
t->values[0] = final;
4199
sprintf(str, IFACE_("Tilt: %.2f° %s"), RAD2DEGF(final), t->proptext);
4202
for (i = 0; i < t->total; i++, td++) {
4203
if (td->flag & TD_NOACTION)
4206
if (td->flag & TD_SKIP)
4210
*td->val = td->ival + final * td->factor;
4216
ED_area_headerprint(t->sa, str);
4222
/* ******************** Curve Shrink/Fatten *************** */
4224
void initCurveShrinkFatten(TransInfo *t)
4226
t->mode = TFM_CURVE_SHRINKFATTEN;
4227
t->transform = CurveShrinkFatten;
4229
initMouseInputMode(t, &t->mouse, INPUT_SPRING);
4235
t->snap[2] = t->snap[1] * 0.1f;
4237
t->num.increment = t->snap[1];
4239
t->flag |= T_NO_ZERO;
4240
t->num.flag |= NUM_NO_ZERO;
4242
t->flag |= T_NO_CONSTRAINT;
4245
int CurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
4247
TransData *td = t->data;
4252
ratio = t->values[0];
4254
snapGrid(t, &ratio);
4256
applyNumInput(&t->num, &ratio);
4258
/* header print for NumInput */
4259
if (hasNumInput(&t->num)) {
4260
char c[NUM_STR_REP_LEN];
4262
outputNumInput(&(t->num), c);
4263
sprintf(str, IFACE_("Shrink/Fatten: %s"), c);
4266
sprintf(str, IFACE_("Shrink/Fatten: %3f"), ratio);
4269
for (i = 0; i < t->total; i++, td++) {
4270
if (td->flag & TD_NOACTION)
4273
if (td->flag & TD_SKIP)
4277
*td->val = td->ival * ratio;
4279
*td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
4280
if (*td->val <= 0.0f) *td->val = 0.001f;
4286
ED_area_headerprint(t->sa, str);
4292
void initMaskShrinkFatten(TransInfo *t)
4294
t->mode = TFM_MASK_SHRINKFATTEN;
4295
t->transform = MaskShrinkFatten;
4297
initMouseInputMode(t, &t->mouse, INPUT_SPRING);
4303
t->snap[2] = t->snap[1] * 0.1f;
4305
t->num.increment = t->snap[1];
4307
t->flag |= T_NO_ZERO;
4308
t->num.flag |= NUM_NO_ZERO;
4310
t->flag |= T_NO_CONSTRAINT;
4313
int MaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
4317
int i, initial_feather = FALSE;
4320
ratio = t->values[0];
4322
snapGrid(t, &ratio);
4324
applyNumInput(&t->num, &ratio);
4326
/* header print for NumInput */
4327
if (hasNumInput(&t->num)) {
4328
char c[NUM_STR_REP_LEN];
4330
outputNumInput(&(t->num), c);
4331
sprintf(str, IFACE_("Feather Shrink/Fatten: %s"), c);
4334
sprintf(str, IFACE_("Feather Shrink/Fatten: %3f"), ratio);
4337
/* detect if no points have feather yet */
4339
initial_feather = TRUE;
4341
for (td = t->data, i = 0; i < t->total; i++, td++) {
4342
if (td->flag & TD_NOACTION)
4345
if (td->flag & TD_SKIP)
4348
if (td->ival >= 0.001f)
4349
initial_feather = FALSE;
4353
/* apply shrink/fatten */
4354
for (td = t->data, i = 0; i < t->total; i++, td++) {
4355
if (td->flag & TD_NOACTION)
4358
if (td->flag & TD_SKIP)
4362
if (initial_feather)
4363
*td->val = td->ival + (ratio - 1.0f) * 0.01f;
4365
*td->val = td->ival * ratio;
4368
*td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
4369
if (*td->val <= 0.0f) *td->val = 0.001f;
4375
ED_area_headerprint(t->sa, str);
4380
/* ************************** PUSH/PULL *************************** */
4382
void initPushPull(TransInfo *t)
4384
t->mode = TFM_PUSHPULL;
4385
t->transform = PushPull;
4387
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
4393
t->snap[2] = t->snap[1] * 0.1f;
4395
t->num.increment = t->snap[1];
4399
int PushPull(TransInfo *t, const int UNUSED(mval[2]))
4401
float vec[3], axis[3];
4405
TransData *td = t->data;
4407
distance = t->values[0];
4409
snapGrid(t, &distance);
4411
applyNumInput(&t->num, &distance);
4413
/* header print for NumInput */
4414
if (hasNumInput(&t->num)) {
4415
char c[NUM_STR_REP_LEN];
4417
outputNumInput(&(t->num), c);
4419
sprintf(str, IFACE_("Push/Pull: %s%s %s"), c, t->con.text, t->proptext);
4422
/* default header print */
4423
sprintf(str, IFACE_("Push/Pull: %.4f%s %s"), distance, t->con.text, t->proptext);
4426
t->values[0] = distance;
4428
if (t->con.applyRot && t->con.mode & CON_APPLY) {
4429
t->con.applyRot(t, NULL, axis, NULL);
4432
for (i = 0; i < t->total; i++, td++) {
4433
if (td->flag & TD_NOACTION)
4436
if (td->flag & TD_SKIP)
4439
sub_v3_v3v3(vec, t->center, td->center);
4440
if (t->con.applyRot && t->con.mode & CON_APPLY) {
4441
t->con.applyRot(t, td, axis, NULL);
4442
if (isLockConstraint(t)) {
4444
project_v3_v3v3(dvec, vec, axis);
4445
sub_v3_v3(vec, dvec);
4448
project_v3_v3v3(vec, vec, axis);
4452
mul_v3_fl(vec, distance);
4453
mul_v3_fl(vec, td->factor);
4455
add_v3_v3v3(td->loc, td->iloc, vec);
4460
ED_area_headerprint(t->sa, str);
4465
/* ************************** BEVEL **************************** */
4467
void initBevel(TransInfo *t)
4469
t->transform = Bevel;
4470
t->handleEvent = handleEventBevel;
4472
initMouseInputMode(t, &t->mouse, INPUT_HORIZONTAL_ABSOLUTE);
4474
t->mode = TFM_BEVEL;
4475
t->flag |= T_NO_CONSTRAINT;
4476
t->num.flag |= NUM_NO_NEGATIVE;
4482
t->snap[2] = t->snap[1] * 0.1f;
4484
t->num.increment = t->snap[1];
4486
/* DON'T KNOW WHY THIS IS NEEDED */
4487
if (G.editBMesh->imval[0] == 0 && G.editBMesh->imval[1] == 0) {
4488
/* save the initial mouse co */
4489
G.editBMesh->imval[0] = t->imval[0];
4490
G.editBMesh->imval[1] = t->imval[1];
4493
/* restore the mouse co from a previous call to initTransform() */
4494
t->imval[0] = G.editBMesh->imval[0];
4495
t->imval[1] = G.editBMesh->imval[1];
4499
int handleEventBevel(TransInfo *t, wmEvent *event)
4501
if (event->val == KM_PRESS) {
4502
if (!G.editBMesh) return 0;
4504
switch (event->type) {
4506
G.editBMesh->options ^= BME_BEVEL_VERT;
4507
t->state = TRANS_CANCEL;
4510
// G.editBMesh->options ^= BME_BEVEL_RES;
4511
// G.editBMesh->res += 1;
4512
// if (G.editBMesh->res > 4) {
4513
// G.editBMesh->res = 4;
4515
// t->state = TRANS_CANCEL;
4518
// G.editBMesh->options ^= BME_BEVEL_RES;
4519
// G.editBMesh->res -= 1;
4520
// if (G.editBMesh->res < 0) {
4521
// G.editBMesh->res = 0;
4523
// t->state = TRANS_CANCEL;
4532
int Bevel(TransInfo *t, const int UNUSED(mval[2]))
4538
TransData *td = t->data;
4540
mode = (G.editBMesh->options & BME_BEVEL_VERT) ? IFACE_("verts only") : IFACE_("normal");
4541
distance = t->values[0] / 4; /* 4 just seemed a nice value to me, nothing special */
4543
distance = fabs(distance);
4545
snapGrid(t, &distance);
4547
applyNumInput(&t->num, &distance);
4549
/* header print for NumInput */
4550
if (hasNumInput(&t->num)) {
4551
char c[NUM_STR_REP_LEN];
4553
outputNumInput(&(t->num), c);
4555
sprintf(str, IFACE_("Bevel - Dist: %s, Mode: %s (MMB to toggle))"), c, mode);
4558
/* default header print */
4559
sprintf(str, IFACE_("Bevel - Dist: %.4f, Mode: %s (MMB to toggle))"), distance, mode);
4562
if (distance < 0) distance = -distance;
4563
for (i = 0; i < t->total; i++, td++) {
4564
if (td->axismtx[1][0] > 0 && distance > td->axismtx[1][0]) {
4565
d = td->axismtx[1][0];
4570
madd_v3_v3v3fl(td->loc, td->center, td->axismtx[0], (*td->val) * d);
4575
ED_area_headerprint(t->sa, str);
4580
/* ************************** BEVEL WEIGHT *************************** */
4582
void initBevelWeight(TransInfo *t)
4584
t->mode = TFM_BWEIGHT;
4585
t->transform = BevelWeight;
4587
initMouseInputMode(t, &t->mouse, INPUT_SPRING);
4593
t->snap[2] = t->snap[1] * 0.1f;
4595
t->num.increment = t->snap[1];
4597
t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
4600
int BevelWeight(TransInfo *t, const int UNUSED(mval[2]))
4602
TransData *td = t->data;
4607
weight = t->values[0];
4610
if (weight > 1.0f) weight = 1.0f;
4612
snapGrid(t, &weight);
4614
applyNumInput(&t->num, &weight);
4616
/* header print for NumInput */
4617
if (hasNumInput(&t->num)) {
4618
char c[NUM_STR_REP_LEN];
4620
outputNumInput(&(t->num), c);
4623
sprintf(str, IFACE_("Bevel Weight: +%s %s"), c, t->proptext);
4625
sprintf(str, IFACE_("Bevel Weight: %s %s"), c, t->proptext);
4628
/* default header print */
4630
sprintf(str, IFACE_("Bevel Weight: +%.3f %s"), weight, t->proptext);
4632
sprintf(str, IFACE_("Bevel Weight: %.3f %s"), weight, t->proptext);
4635
for (i = 0; i < t->total; i++, td++) {
4636
if (td->flag & TD_NOACTION)
4640
*td->val = td->ival + weight * td->factor;
4641
if (*td->val < 0.0f) *td->val = 0.0f;
4642
if (*td->val > 1.0f) *td->val = 1.0f;
4648
ED_area_headerprint(t->sa, str);
4653
/* ************************** CREASE *************************** */
4655
void initCrease(TransInfo *t)
4657
t->mode = TFM_CREASE;
4658
t->transform = Crease;
4660
initMouseInputMode(t, &t->mouse, INPUT_SPRING);
4666
t->snap[2] = t->snap[1] * 0.1f;
4668
t->num.increment = t->snap[1];
4670
t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
4673
int Crease(TransInfo *t, const int UNUSED(mval[2]))
4675
TransData *td = t->data;
4680
crease = t->values[0];
4683
if (crease > 1.0f) crease = 1.0f;
4685
snapGrid(t, &crease);
4687
applyNumInput(&t->num, &crease);
4689
/* header print for NumInput */
4690
if (hasNumInput(&t->num)) {
4691
char c[NUM_STR_REP_LEN];
4693
outputNumInput(&(t->num), c);
4696
sprintf(str, IFACE_("Crease: +%s %s"), c, t->proptext);
4698
sprintf(str, IFACE_("Crease: %s %s"), c, t->proptext);
4701
/* default header print */
4703
sprintf(str, IFACE_("Crease: +%.3f %s"), crease, t->proptext);
4705
sprintf(str, IFACE_("Crease: %.3f %s"), crease, t->proptext);
4708
for (i = 0; i < t->total; i++, td++) {
4709
if (td->flag & TD_NOACTION)
4712
if (td->flag & TD_SKIP)
4716
*td->val = td->ival + crease * td->factor;
4717
if (*td->val < 0.0f) *td->val = 0.0f;
4718
if (*td->val > 1.0f) *td->val = 1.0f;
4724
ED_area_headerprint(t->sa, str);
4729
/* ******************** EditBone (B-bone) width scaling *************** */
4731
void initBoneSize(TransInfo *t)
4733
t->mode = TFM_BONESIZE;
4734
t->transform = BoneSize;
4736
initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
4740
t->num.flag |= NUM_NULL_ONE;
4741
t->num.flag |= NUM_AFFECT_ALL;
4744
t->snap[2] = t->snap[1] * 0.1f;
4746
t->num.increment = t->snap[1];
4749
static void headerBoneSize(TransInfo *t, float vec[3], char *str)
4751
char tvec[NUM_STR_REP_LEN * 3];
4752
if (hasNumInput(&t->num)) {
4753
outputNumInput(&(t->num), tvec);
4756
sprintf(&tvec[0], "%.4f", vec[0]);
4757
sprintf(&tvec[NUM_STR_REP_LEN], "%.4f", vec[1]);
4758
sprintf(&tvec[NUM_STR_REP_LEN * 2], "%.4f", vec[2]);
4761
/* hmm... perhaps the y-axis values don't need to be shown? */
4762
if (t->con.mode & CON_APPLY) {
4763
if (t->num.idx_max == 0)
4764
sprintf(str, IFACE_("ScaleB: %s%s %s"), &tvec[0], t->con.text, t->proptext);
4766
sprintf(str, IFACE_("ScaleB: %s : %s : %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN],
4767
&tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext);
4770
sprintf(str, IFACE_("ScaleB X: %s Y: %s Z: %s%s %s"), &tvec[0], &tvec[NUM_STR_REP_LEN],
4771
&tvec[NUM_STR_REP_LEN * 2], t->con.text, t->proptext);
4775
static void ElementBoneSize(TransInfo *t, TransData *td, float mat[3][3])
4777
float tmat[3][3], smat[3][3], oldy;
4778
float sizemat[3][3];
4780
mul_m3_m3m3(smat, mat, td->mtx);
4781
mul_m3_m3m3(tmat, td->smtx, smat);
4783
if (t->con.applySize) {
4784
t->con.applySize(t, td, tmat);
4787
/* we've tucked the scale in loc */
4789
size_to_mat3(sizemat, td->iloc);
4790
mul_m3_m3m3(tmat, tmat, sizemat);
4791
mat3_to_size(td->loc, tmat);
4795
int BoneSize(TransInfo *t, const int mval[2])
4797
TransData *td = t->data;
4798
float size[3], mat[3][3];
4803
// TRANSFORM_FIX_ME MOVE TO MOUSE INPUT
4804
/* for manipulator, center handle, the scaling can't be done relative to center */
4805
if ((t->flag & T_USES_MANIPULATOR) && t->con.mode == 0) {
4806
ratio = 1.0f - ((t->imval[0] - mval[0]) + (t->imval[1] - mval[1])) / 100.0f;
4809
ratio = t->values[0];
4812
size[0] = size[1] = size[2] = ratio;
4816
if (hasNumInput(&t->num)) {
4817
applyNumInput(&t->num, size);
4818
constraintNumInput(t, size);
4821
size_to_mat3(mat, size);
4823
if (t->con.applySize) {
4824
t->con.applySize(t, NULL, mat);
4827
copy_m3_m3(t->mat, mat); // used in manipulator
4829
headerBoneSize(t, size, str);
4831
for (i = 0; i < t->total; i++, td++) {
4832
if (td->flag & TD_NOACTION)
4835
if (td->flag & TD_SKIP)
4838
ElementBoneSize(t, td, mat);
4843
ED_area_headerprint(t->sa, str);
4849
/* ******************** EditBone envelope *************** */
4851
void initBoneEnvelope(TransInfo *t)
4853
t->mode = TFM_BONE_ENVELOPE;
4854
t->transform = BoneEnvelope;
4856
initMouseInputMode(t, &t->mouse, INPUT_SPRING);
4862
t->snap[2] = t->snap[1] * 0.1f;
4864
t->num.increment = t->snap[1];
4866
t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
4869
int BoneEnvelope(TransInfo *t, const int UNUSED(mval[2]))
4871
TransData *td = t->data;
4876
ratio = t->values[0];
4878
snapGrid(t, &ratio);
4880
applyNumInput(&t->num, &ratio);
4882
/* header print for NumInput */
4883
if (hasNumInput(&t->num)) {
4884
char c[NUM_STR_REP_LEN];
4886
outputNumInput(&(t->num), c);
4887
sprintf(str, IFACE_("Envelope: %s"), c);
4890
sprintf(str, IFACE_("Envelope: %3f"), ratio);
4893
for (i = 0; i < t->total; i++, td++) {
4894
if (td->flag & TD_NOACTION)
4897
if (td->flag & TD_SKIP)
4901
/* if the old/original value was 0.0f, then just use ratio */
4903
*td->val = td->ival * ratio;
4911
ED_area_headerprint(t->sa, str);
4916
/* ******************** Edge Slide *************** */
4917
static BMEdge *get_other_edge(BMVert *v, BMEdge *e)
4922
BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
4923
if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT) && e_iter != e) {
4931
static void len_v3_ensure(float v[3], const float length)
4934
mul_v3_fl(v, length);
4938
* Find the closest point on the ngon on the opposite side.
4939
* used to set the edge slide distance for ngons.
4941
static bool bm_loop_calc_opposite_co(BMLoop *l_tmp,
4942
const float plane_no[3],
4945
/* skip adjacent edges */
4946
BMLoop *l_first = l_tmp->next;
4947
BMLoop *l_last = l_tmp->prev;
4949
float dist = FLT_MAX;
4954
if (isect_line_plane_v3(tvec,
4955
l_iter->v->co, l_iter->next->v->co,
4956
l_tmp->v->co, plane_no, false))
4958
const float fac = line_point_factor_v3(tvec, l_iter->v->co, l_iter->next->v->co);
4959
/* allow some overlap to avoid missing the intersection because of float precision */
4960
if ((fac > -FLT_EPSILON) && (fac < 1.0f + FLT_EPSILON)) {
4961
/* likelyhood of multiple intersections per ngon is quite low,
4962
* it would have to loop back on its self, but better support it
4963
* so check for the closest opposite edge */
4964
const float tdist = len_v3v3(l_tmp->v->co, tvec);
4966
copy_v3_v3(r_co, tvec);
4971
} while ((l_iter = l_iter->next) != l_last);
4973
return (dist != FLT_MAX);
4977
* Given 2 edges and a loop, step over the loops
4978
* and calculate a direction to slide along.
4980
* \param r_slide_vec the direction to slide,
4981
* the length of the vector defines the slide distance.
4983
static BMLoop *get_next_loop(BMVert *v, BMLoop *l,
4984
BMEdge *e_prev, BMEdge *e_next, float r_slide_vec[3])
4987
float vec_accum[3] = {0.0f, 0.0f, 0.0f};
4988
float vec_accum_len = 0.0f;
4991
BLI_assert(BM_edge_share_vert(e_prev, e_next) == v);
4995
l = BM_face_other_edge_loop(l->f, l->e, v);
4996
if (l->radial_next == l)
4999
if (l->e == e_next) {
5001
len_v3_ensure(vec_accum, vec_accum_len / (float)i);
5004
/* When there is no edge to slide along,
5005
* we must slide along the vector defined by the face we're attach to */
5006
BMLoop *l_tmp = BM_face_vert_share_loop(l_first->f, v);
5008
BLI_assert(ELEM(l_tmp->e, e_prev, e_next) && ELEM(l_tmp->prev->e, e_prev, e_next));
5010
if (l_tmp->f->len == 4) {
5011
/* we could use code below, but in this case
5012
* sliding diagonally across the quad works well */
5013
sub_v3_v3v3(vec_accum, l_tmp->next->next->v->co, v->co);
5017
BM_loop_calc_face_direction(l_tmp, tdir);
5018
cross_v3_v3v3(vec_accum, l_tmp->f->no, tdir);
5020
/* rough guess, we can do better! */
5021
len_v3_ensure(vec_accum, (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f);
5023
/* be clever, check the opposite ngon edge to slide into.
5024
* this gives best results */
5029
if (bm_loop_calc_opposite_co(l_tmp, tdir, tvec)) {
5030
dist = len_v3v3(l_tmp->v->co, tvec);
5033
dist = (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f;
5036
len_v3_ensure(vec_accum, dist);
5042
copy_v3_v3(r_slide_vec, vec_accum);
5046
/* accumulate the normalized edge vector,
5047
* normalize so some edges don't skew the result */
5049
sub_v3_v3v3(tvec, BM_edge_other_vert(l->e, v)->co, v->co);
5050
vec_accum_len += normalize_v3(tvec);
5051
add_v3_v3(vec_accum, tvec);
5055
if (BM_face_other_edge_loop(l->f, l->e, v)->e == e_next) {
5057
len_v3_ensure(vec_accum, vec_accum_len / (float)i);
5060
copy_v3_v3(r_slide_vec, vec_accum);
5061
return BM_face_other_edge_loop(l->f, l->e, v);
5064
BLI_assert(l != l->radial_next);
5066
} while (l != l_first);
5069
len_v3_ensure(vec_accum, vec_accum_len / (float)i);
5072
copy_v3_v3(r_slide_vec, vec_accum);
5077
static void calcNonProportionalEdgeSlide(TransInfo *t, EdgeSlideData *sld, const float mval[2])
5079
TransDataEdgeSlideVert *sv = sld->sv;
5081
if (sld->totsv > 0) {
5082
ARegion *ar = t->ar;
5083
RegionView3D *rv3d = NULL;
5084
float projectMat[4][4];
5090
float min_dist = FLT_MAX;
5092
if (t->spacetype == SPACE_VIEW3D) {
5093
/* background mode support */
5094
rv3d = t->ar ? t->ar->regiondata : NULL;
5098
/* ok, let's try to survive this */
5099
unit_m4(projectMat);
5102
ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat);
5105
for (i = 0; i < sld->totsv; i++, sv++) {
5107
sv->edge_len = len_v3v3(sv->upvec, sv->downvec);
5109
ED_view3d_project_float_v2_m4(ar, sv->v->co, v_proj, projectMat);
5110
dist = len_squared_v2v2(mval, v_proj);
5111
if (dist < min_dist) {
5113
sld->curr_sv_index = i;
5118
sld->curr_sv_index = 0;
5122
static int createEdgeSlideVerts(TransInfo *t)
5124
BMEditMesh *em = BMEdit_FromObject(t->obedit);
5129
TransDataEdgeSlideVert *sv_array;
5133
EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
5135
RegionView3D *rv3d = NULL;
5136
ARegion *ar = t->ar;
5137
float projectMat[4][4];
5138
float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
5139
float start[3] = {0.0f, 0.0f, 0.0f}, end[3] = {0.0f, 0.0f, 0.0f};
5140
float vec[3], vec2[3] /*, lastvec[3], size, dis=0.0, z */ /* UNUSED */;
5141
float dir[3], maxdist, (*loop_dir)[3], *loop_maxdist;
5142
int numsel, i, j, loop_nr, l_nr;
5145
if (t->spacetype == SPACE_VIEW3D) {
5146
/* background mode support */
5147
v3d = t->sa ? t->sa->spacedata.first : NULL;
5148
rv3d = t->ar ? t->ar->regiondata : NULL;
5151
use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
5153
if (use_btree_disp) {
5154
btree = BMBVH_NewBVH(em, BMBVH_RESPECT_HIDDEN, NULL, NULL);
5160
sld->is_proportional = TRUE;
5161
sld->curr_sv_index = 0;
5162
sld->flipped_vtx = FALSE;
5165
/* ok, let's try to survive this */
5166
unit_m4(projectMat);
5169
ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat);
5172
BLI_smallhash_init(&sld->vhash);
5173
BLI_smallhash_init(&sld->origfaces);
5174
BLI_smallhash_init(&table);
5176
/*ensure valid selection*/
5177
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
5178
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
5181
BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
5182
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
5183
/* BMESH_TODO: this is probably very evil,
5184
* set v->e to a selected edge*/
5191
if (numsel == 0 || numsel > 2) {
5194
BMBVH_FreeBVH(btree);
5195
return 0; /* invalid edge selection */
5200
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
5201
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
5202
if (!BM_edge_is_manifold(e)) {
5205
BMBVH_FreeBVH(btree);
5206
return 0; /* can only handle exactly 2 faces around each edge */
5212
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
5213
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
5214
BM_elem_flag_enable(v, BM_ELEM_TAG);
5215
BLI_smallhash_insert(&table, (uintptr_t)v, SET_INT_IN_POINTER(j));
5219
BM_elem_flag_disable(v, BM_ELEM_TAG);
5226
BMBVH_FreeBVH(btree);
5231
sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array");
5235
BMLoop *l, *l1, *l2;
5239
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
5240
if (BM_elem_flag_test(v, BM_ELEM_TAG))
5253
/*walk along the edge loop*/
5259
e = get_other_edge(v, e);
5267
if (!BM_elem_flag_test(BM_edge_other_vert(e, v), BM_ELEM_TAG))
5270
v = BM_edge_other_vert(e, v);
5271
} while (e != v_first->e);
5273
BM_elem_flag_disable(v, BM_ELEM_TAG);
5277
l2 = e->l->radial_next;
5279
l = BM_face_other_edge_loop(l1->f, l1->e, v);
5280
sub_v3_v3v3(vec, BM_edge_other_vert(l->e, v)->co, v->co);
5283
l = BM_face_other_edge_loop(l2->f, l2->e, v);
5284
sub_v3_v3v3(vec2, BM_edge_other_vert(l->e, v)->co, v->co);
5290
/*iterate over the loop*/
5293
TransDataEdgeSlideVert *sv;
5295
/* XXX, 'sv' will initialize multiple times, this is suspicious. see [#34024] */
5296
BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false);
5297
sv = sv_array + GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v));
5300
sv->loop_nr = loop_nr;
5302
copy_v3_v3(sv->upvec, vec);
5304
copy_v3_v3(sv->downvec, vec2);
5306
l = BM_face_other_edge_loop(l1->f, l1->e, v);
5307
sv->up = BM_edge_other_vert(l->e, v);
5310
l = BM_face_other_edge_loop(l2->f, l2->e, v);
5311
sv->down = BM_edge_other_vert(l->e, v);
5314
v2 = v, v = BM_edge_other_vert(e, v);
5317
e = get_other_edge(v, e);
5319
BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false);
5320
sv = sv_array + GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v));
5323
sv->loop_nr = loop_nr;
5325
l = BM_face_other_edge_loop(l1->f, l1->e, v);
5326
sv->up = BM_edge_other_vert(l->e, v);
5327
sub_v3_v3v3(sv->upvec, BM_edge_other_vert(l->e, v)->co, v->co);
5330
l = BM_face_other_edge_loop(l2->f, l2->e, v);
5331
sv->down = BM_edge_other_vert(l->e, v);
5332
sub_v3_v3v3(sv->downvec, BM_edge_other_vert(l->e, v)->co, v->co);
5335
BM_elem_flag_disable(v, BM_ELEM_TAG);
5336
BM_elem_flag_disable(v2, BM_ELEM_TAG);
5341
l1 = get_next_loop(v, l1, e1, e, vec);
5342
l2 = l2 ? get_next_loop(v, l2, e1, e, vec2) : NULL;
5344
if (UNLIKELY(l1 == NULL && l2 != NULL)) {
5347
swap_v3_v3(vec, vec2);
5350
BM_elem_flag_disable(v, BM_ELEM_TAG);
5351
BM_elem_flag_disable(v2, BM_ELEM_TAG);
5352
} while (e != v_first->e && l1);
5357
/* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
5360
sld->totsv = sv_tot;
5362
/* find mouse vectors, the global one, and one per loop in case we have
5363
* multiple loops selected, in case they are oriented different */
5367
loop_dir = MEM_callocN(sizeof(float) * 3 * loop_nr, "sv loop_dir");
5368
loop_maxdist = MEM_callocN(sizeof(float) * loop_nr, "sv loop_maxdist");
5369
for (j = 0; j < loop_nr; j++)
5370
loop_maxdist[j] = -1.0f;
5372
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
5373
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
5378
/* search cross edges for visible edge to the mouse cursor,
5379
* then use the shared vertex to calculate screen vector*/
5380
for (i = 0; i < 2; i++) {
5381
v = i ? e->v1 : e->v2;
5382
BM_ITER_ELEM (e2, &iter2, v, BM_EDGES_OF_VERT) {
5383
if (BM_elem_flag_test(e2, BM_ELEM_SELECT))
5386
/* This test is only relevant if object is not wire-drawn! See [#32068]. */
5387
if (use_btree_disp && !BMBVH_EdgeVisible(btree, e2, ar, v3d, t->obedit)) {
5391
BLI_assert(BLI_smallhash_haskey(&table, (uintptr_t)v) != false);
5392
j = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&table, (uintptr_t)v));
5394
if (sv_array[j].down) {
5395
ED_view3d_project_float_v3_m4(ar, sv_array[j].down->co, vec1, projectMat);
5398
add_v3_v3v3(vec1, v->co, sv_array[j].downvec);
5399
ED_view3d_project_float_v3_m4(ar, vec1, vec1, projectMat);
5402
if (sv_array[j].up) {
5403
ED_view3d_project_float_v3_m4(ar, sv_array[j].up->co, vec2, projectMat);
5406
add_v3_v3v3(vec2, v->co, sv_array[j].upvec);
5407
ED_view3d_project_float_v3_m4(ar, vec2, vec2, projectMat);
5410
/* global direction */
5411
d = dist_to_line_segment_v2(mval, vec1, vec2);
5412
if (maxdist == -1.0f || d < maxdist) {
5414
sub_v3_v3v3(dir, vec1, vec2);
5417
/* per loop direction */
5418
l_nr = sv_array[j].loop_nr;
5419
if (loop_maxdist[l_nr] == -1.0f || d < loop_maxdist[l_nr]) {
5420
loop_maxdist[l_nr] = d;
5421
sub_v3_v3v3(loop_dir[l_nr], vec1, vec2);
5428
bmesh_edit_begin(bm, BMO_OP_FLAG_UNTAN_MULTIRES);
5430
/*create copies of faces for customdata projection*/
5432
for (i = 0; i < sld->totsv; i++, sv_array++) {
5433
BMIter fiter, liter;
5437
BM_ITER_ELEM (f, &fiter, sv_array->v, BM_FACES_OF_VERT) {
5439
if (!BLI_smallhash_haskey(&sld->origfaces, (uintptr_t)f)) {
5440
BMFace *copyf = BM_face_copy(bm, f, TRUE, TRUE);
5442
BM_face_select_set(bm, copyf, FALSE);
5443
BM_elem_flag_enable(copyf, BM_ELEM_HIDDEN);
5444
BM_ITER_ELEM (l, &liter, copyf, BM_LOOPS_OF_FACE) {
5445
BM_vert_select_set(bm, l->v, FALSE);
5446
BM_elem_flag_enable(l->v, BM_ELEM_HIDDEN);
5447
BM_edge_select_set(bm, l->e, FALSE);
5448
BM_elem_flag_enable(l->e, BM_ELEM_HIDDEN);
5451
BLI_smallhash_insert(&sld->origfaces, (uintptr_t)f, copyf);
5455
BLI_smallhash_insert(&sld->vhash, (uintptr_t)sv_array->v, sv_array);
5457
/* switch up/down if loop direction is different from global direction */
5458
l_nr = sv_array->loop_nr;
5459
if (dot_v3v3(loop_dir[l_nr], dir) < 0.0f) {
5460
swap_v3_v3(sv_array->upvec, sv_array->downvec);
5461
SWAP(BMVert, sv_array->vup, sv_array->vdown);
5462
SWAP(BMVert *, sv_array->up, sv_array->down);
5467
calcNonProportionalEdgeSlide(t, sld, mval);
5469
sld->origfaces_init = true;
5475
/*dir holds a vector along edge loop*/
5476
copy_v3_v3(end, dir);
5477
mul_v3_fl(end, 0.5f);
5479
sld->start[0] = t->mval[0] + start[0];
5480
sld->start[1] = t->mval[1] + start[1];
5482
sld->end[0] = t->mval[0] + end[0];
5483
sld->end[1] = t->mval[1] + end[1];
5487
t->customData = sld;
5489
BLI_smallhash_release(&table);
5491
BMBVH_FreeBVH(btree);
5493
MEM_freeN(loop_dir);
5494
MEM_freeN(loop_maxdist);
5496
/* arrays are dirty from copying faces: EDBM_index_arrays_free */
5497
EDBM_update_generic(em, FALSE, TRUE);
5502
void projectEdgeSlideData(TransInfo *t, bool is_final)
5504
EdgeSlideData *sld = t->customData;
5505
TransDataEdgeSlideVert *sv;
5506
BMEditMesh *em = sld->em;
5513
if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT))
5516
/* don't do this at all for non-basis shape keys, too easy to
5517
* accidentally break uv maps or vertex colors then */
5518
if (em->bm->shapenr > 1)
5521
BLI_smallhash_init(&visit);
5523
for (i = 0, sv = sld->sv; i < sld->totsv; sv++, i++) {
5527
/* BMESH_TODO, this interpolates between vertex/loops which are not moved
5528
* (are only apart of a face attached to a slide vert), couldn't we iterate BM_LOOPS_OF_VERT
5529
* here and only interpolate those? */
5530
BM_ITER_ELEM (f, &fiter, sv->v, BM_FACES_OF_VERT) {
5534
BMFace *f_copy; /* the copy of 'f' */
5535
BMFace *f_copy_flip; /* the copy of 'f' or detect if we need to flip to the shorter side. */
5537
char is_sel, is_hide;
5540
if (BLI_smallhash_haskey(&visit, (uintptr_t)f))
5543
BLI_smallhash_insert(&visit, (uintptr_t)f, NULL);
5545
/* the face attributes of the copied face will get
5546
* copied over, so its necessary to save the selection
5547
* and hidden state*/
5548
is_sel = BM_elem_flag_test(f, BM_ELEM_SELECT);
5549
is_hide = BM_elem_flag_test(f, BM_ELEM_HIDDEN);
5551
f_copy = BLI_smallhash_lookup(&sld->origfaces, (uintptr_t)f);
5553
/* project onto copied projection face */
5554
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
5555
/* only affected verts will get interpolated */
5556
char affected = FALSE;
5557
f_copy_flip = f_copy;
5559
if (BM_elem_flag_test(l->e, BM_ELEM_SELECT) || BM_elem_flag_test(l->prev->e, BM_ELEM_SELECT)) {
5560
/* the loop is attached of the selected edges that are sliding */
5561
BMLoop *l_ed_sel = l;
5563
if (!BM_elem_flag_test(l->e, BM_ELEM_SELECT))
5564
l_ed_sel = l_ed_sel->prev;
5566
if (sld->perc < 0.0f) {
5567
if (BM_vert_in_face(l_ed_sel->radial_next->f, sv->down)) {
5568
f_copy_flip = BLI_smallhash_lookup(&sld->origfaces, (uintptr_t)l_ed_sel->radial_next->f);
5571
else if (sld->perc > 0.0f) {
5572
if (BM_vert_in_face(l_ed_sel->radial_next->f, sv->up)) {
5573
f_copy_flip = BLI_smallhash_lookup(&sld->origfaces, (uintptr_t)l_ed_sel->radial_next->f);
5577
BLI_assert(f_copy_flip != NULL);
5579
continue; /* shouldn't happen, but protection */
5585
/* the loop is attached to only one vertex and not a selected edge,
5586
* this means we have to find a selected edges face going in the right direction
5587
* to copy from else we get bad distortion see: [#31080] */
5591
BM_ITER_ELEM (e_sel, &eiter, l->v, BM_EDGES_OF_VERT) {
5592
if (BM_elem_flag_test(e_sel, BM_ELEM_SELECT)) {
5598
/* warning if the UV's are not contiguous, this will copy from the _wrong_ UVs
5599
* in fact whenever the face being copied is not 'f_copy' this can happen,
5600
* we could be a lot smarter about this but would need to deal with every UV channel or
5601
* add a way to mask out lauers when calling #BM_loop_interp_from_face() */
5602
if (sld->perc < 0.0f) {
5603
if (BM_vert_in_face(e_sel->l->f, sv->down)) {
5604
f_copy_flip = BLI_smallhash_lookup(&sld->origfaces, (uintptr_t)e_sel->l->f);
5606
else if (BM_vert_in_face(e_sel->l->radial_next->f, sv->down)) {
5607
f_copy_flip = BLI_smallhash_lookup(&sld->origfaces, (uintptr_t)e_sel->l->radial_next->f);
5611
else if (sld->perc > 0.0f) {
5612
if (BM_vert_in_face(e_sel->l->f, sv->up)) {
5613
f_copy_flip = BLI_smallhash_lookup(&sld->origfaces, (uintptr_t)e_sel->l->f);
5615
else if (BM_vert_in_face(e_sel->l->radial_next->f, sv->up)) {
5616
f_copy_flip = BLI_smallhash_lookup(&sld->origfaces, (uintptr_t)e_sel->l->radial_next->f);
5628
/* only loop data, no vertex data since that contains shape keys,
5629
* and we do not want to mess up other shape keys */
5630
BM_loop_interp_from_face(em->bm, l, f_copy_flip, FALSE, FALSE);
5633
BM_loop_interp_multires(em->bm, l, f_copy_flip);
5634
if (f_copy != f_copy_flip) {
5635
BM_loop_interp_multires(em->bm, l, f_copy);
5640
/* make sure face-attributes are correct (e.g. MTexPoly) */
5641
BM_elem_attrs_copy(em->bm, em->bm, f_copy, f);
5643
/* restore selection and hidden flags */
5644
BM_face_select_set(em->bm, f, is_sel);
5646
/* this check is a workaround for bug, see note - [#30735],
5647
* without this edge can be hidden and selected */
5648
BM_elem_hide_set(em->bm, f, is_hide);
5653
BLI_smallhash_release(&visit);
5656
void freeEdgeSlideTempFaces(EdgeSlideData *sld)
5658
if (sld->origfaces_init) {
5659
SmallHashIter hiter;
5662
copyf = BLI_smallhash_iternew(&sld->origfaces, &hiter, NULL);
5663
for (; copyf; copyf = BLI_smallhash_iternext(&hiter, NULL)) {
5664
BM_face_verts_kill(sld->em->bm, copyf);
5667
BLI_smallhash_release(&sld->origfaces);
5669
sld->origfaces_init = false;
5671
/* arrays are dirty from removing faces: EDBM_index_arrays_free */
5672
EDBM_update_generic(sld->em, FALSE, TRUE);
5677
void freeEdgeSlideVerts(TransInfo *t)
5679
EdgeSlideData *sld = t->customData;
5681
#if 0 /*BMESH_TODO*/
5682
if (me->drawflag & ME_DRAWEXTRA_EDGELEN) {
5683
TransDataEdgeSlideVert *sv;
5684
LinkNode *look = sld->vertlist;
5685
GHash *vertgh = sld->vhash;
5687
sv = BLI_ghash_lookup(vertgh, (EditVert *)look->link);
5689
sv->up->f &= !SELECT;
5690
sv->down->f &= !SELECT;
5700
freeEdgeSlideTempFaces(sld);
5702
bmesh_edit_end(sld->em->bm, BMO_OP_FLAG_UNTAN_MULTIRES);
5704
BLI_smallhash_release(&sld->vhash);
5709
t->customData = NULL;
5714
void initEdgeSlide(TransInfo *t)
5718
t->mode = TFM_EDGE_SLIDE;
5719
t->transform = EdgeSlide;
5720
t->handleEvent = handleEventEdgeSlide;
5722
if (!createEdgeSlideVerts(t)) {
5723
t->state = TRANS_CANCEL;
5727
sld = t->customData;
5732
t->customFree = freeEdgeSlideVerts;
5734
/* set custom point first if you want value to be initialized by init */
5735
setCustomPoints(t, &t->mouse, sld->end, sld->start);
5736
initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO_FLIP);
5742
t->snap[2] = t->snap[1] * 0.1f;
5744
t->num.increment = t->snap[1];
5746
t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
5749
int handleEventEdgeSlide(struct TransInfo *t, struct wmEvent *event)
5751
if (t->mode == TFM_EDGE_SLIDE) {
5752
EdgeSlideData *sld = t->customData;
5755
switch (event->type) {
5757
if (event->val == KM_PRESS) {
5758
sld->is_proportional = !sld->is_proportional;
5764
if (event->val == KM_PRESS) {
5765
if (sld->is_proportional == FALSE) {
5766
sld->flipped_vtx = !sld->flipped_vtx;
5774
switch (event->val) {
5775
case TFM_MODAL_EDGESLIDE_DOWN:
5777
sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv;
5780
case TFM_MODAL_EDGESLIDE_UP:
5782
sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv;
5795
void drawEdgeSlide(const struct bContext *C, TransInfo *t)
5797
if (t->mode == TFM_EDGE_SLIDE) {
5798
EdgeSlideData *sld = (EdgeSlideData *)t->customData;
5800
if (sld && sld->is_proportional == FALSE) {
5801
View3D *v3d = CTX_wm_view3d(C);
5805
TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
5806
const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
5807
const float guide_size = ctrl_size - 0.5f;
5808
const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
5809
const int alpha_shade = -30;
5811
add_v3_v3v3(v1, curr_sv->origvert.co, curr_sv->upvec);
5812
add_v3_v3v3(v2, curr_sv->origvert.co, curr_sv->downvec);
5814
interp_v = (sld->perc + 1.0f) / 2.0f;
5815
interp_v3_v3v3(marker, v2, v1, interp_v);
5817
if (v3d && v3d->zbuf)
5818
glDisable(GL_DEPTH_TEST);
5821
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5823
glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT | GL_POINT_BIT);
5826
glMultMatrixf(t->obedit->obmat);
5828
glLineWidth(line_size);
5829
UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
5831
glVertex3fv(curr_sv->up->co);
5832
glVertex3fv(curr_sv->origvert.co);
5833
glVertex3fv(curr_sv->down->co);
5834
glVertex3fv(curr_sv->origvert.co);
5838
UI_ThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade);
5839
glPointSize(ctrl_size);
5840
if (sld->flipped_vtx) {
5841
bglBegin(GL_POINTS);
5842
bglVertex3fv(curr_sv->down->co);
5846
bglBegin(GL_POINTS);
5847
bglVertex3fv(curr_sv->up->co);
5851
UI_ThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade);
5852
glPointSize(guide_size);
5853
bglBegin(GL_POINTS);
5854
bglVertex3fv(marker);
5861
glDisable(GL_BLEND);
5863
if (v3d && v3d->zbuf)
5864
glEnable(GL_DEPTH_TEST);
5869
static int doEdgeSlide(TransInfo *t, float perc)
5871
EdgeSlideData *sld = t->customData;
5872
TransDataEdgeSlideVert *svlist = sld->sv, *sv;
5878
if (sld->is_proportional == TRUE) {
5879
for (i = 0; i < sld->totsv; i++, sv++) {
5882
copy_v3_v3(vec, sv->upvec);
5883
mul_v3_fl(vec, perc);
5884
add_v3_v3v3(sv->v->co, sv->origvert.co, vec);
5887
copy_v3_v3(vec, sv->downvec);
5888
mul_v3_fl(vec, -perc);
5889
add_v3_v3v3(sv->v->co, sv->origvert.co, vec);
5895
* Implementation note, non proportional mode ignores the starting positions and uses only the
5896
* up/down verts, this could be changed/improved so the distance is still met but the verts are moved along
5897
* their original path (which may not be straight), however how it works now is OK and matches 2.4x - Campbell
5899
* \note len_v3v3(curr_sv->upvec, curr_sv->downvec)
5900
* is the same as the distance between the original vert locations, same goes for the lines below.
5902
TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
5903
const float curr_length_perc = curr_sv->edge_len * (((sld->flipped_vtx ? perc : -perc) + 1.0f) / 2.0f);
5908
for (i = 0; i < sld->totsv; i++, sv++) {
5909
if (sv->edge_len > FLT_EPSILON) {
5910
const float fac = min_ff(sv->edge_len, curr_length_perc) / sv->edge_len;
5912
add_v3_v3v3(up_co, sv->origvert.co, sv->upvec);
5913
add_v3_v3v3(down_co, sv->origvert.co, sv->downvec);
5915
if (sld->flipped_vtx) {
5916
interp_v3_v3v3(sv->v->co, down_co, up_co, fac);
5919
interp_v3_v3v3(sv->v->co, up_co, down_co, fac);
5925
projectEdgeSlideData(t, 0);
5930
int EdgeSlide(TransInfo *t, const int UNUSED(mval[2]))
5934
EdgeSlideData *sld = t->customData;
5935
bool flipped = sld->flipped_vtx;
5936
bool is_proportional = sld->is_proportional;
5938
final = t->values[0];
5940
snapGrid(t, &final);
5942
/* only do this so out of range values are not displayed */
5943
CLAMP(final, -1.0f, 1.0f);
5945
if (hasNumInput(&t->num)) {
5946
char c[NUM_STR_REP_LEN];
5948
applyNumInput(&t->num, &final);
5950
outputNumInput(&(t->num), c);
5952
BLI_snprintf(str, sizeof(str), IFACE_("Edge Slide: %s (E)ven: %s, (F)lipped: %s"),
5953
&c[0], !is_proportional ? IFACE_("ON") : IFACE_("OFF"), flipped ? IFACE_("ON") : IFACE_("OFF"));
5956
BLI_snprintf(str, sizeof(str), IFACE_("Edge Slide: %.4f (E)ven: %s, (F)lipped: %s"),
5957
final, !is_proportional ? IFACE_("ON") : IFACE_("OFF"), flipped ? IFACE_("ON") : IFACE_("OFF"));
5960
CLAMP(final, -1.0f, 1.0f);
5962
t->values[0] = final;
5966
doEdgeSlide(t, final);
5968
strcpy(str, IFACE_("Invalid Edge Selection"));
5969
t->state = TRANS_CANCEL;
5974
ED_area_headerprint(t->sa, str);
5980
/* ******************** Vert Slide *************** */
5981
static void calcVertSlideCustomPoints(struct TransInfo *t)
5983
VertSlideData *sld = t->customData;
5984
TransDataVertSlideVert *sv = &sld->sv[sld->curr_sv_index];
5985
float *co_orig = sv->co_orig_2d;
5986
float *co_curr = sv->co_link_orig_2d[sv->co_link_curr];
5987
const int start[2] = {co_orig[0], co_orig[1]};
5988
const int end[2] = {co_curr[0], co_curr[1]};
5990
if (sld->flipped_vtx && sld->is_proportional == false) {
5991
setCustomPoints(t, &t->mouse, start, end);
5994
setCustomPoints(t, &t->mouse, end, start);
5999
* Run once when initializing vert slide to find the reference edge
6001
static void calcVertSlideMouseActiveVert(struct TransInfo *t, const int mval[2])
6003
VertSlideData *sld = t->customData;
6004
float mval_fl[2] = {UNPACK2(mval)};
6005
TransDataVertSlideVert *sv;
6007
/* set the vertex to use as a reference for the mouse direction 'curr_sv_index' */
6009
float min_dist = FLT_MAX;
6012
for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) {
6013
dist = len_squared_v2v2(mval_fl, sv->co_orig_2d);
6014
if (dist < min_dist) {
6016
sld->curr_sv_index = i;
6021
* Run while moving the mouse to slide along the edge matching the mouse direction
6023
static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2])
6025
VertSlideData *sld = t->customData;
6026
float mval_fl[2] = {UNPACK2(mval)};
6029
TransDataVertSlideVert *sv;
6032
/* first get the direction of the original vertex */
6033
sub_v2_v2v2(dir, sld->sv[sld->curr_sv_index].co_orig_2d, mval_fl);
6036
for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) {
6037
if (sv->co_link_tot > 1) {
6038
float dir_dot_best = -FLT_MAX;
6039
int co_link_curr_best = -1;
6042
for (j = 0; j < sv->co_link_tot; j++) {
6045
sub_v2_v2v2(tdir, sv->co_orig_2d, sv->co_link_orig_2d[j]);
6047
dir_dot = dot_v2v2(dir, tdir);
6048
if (dir_dot > dir_dot_best) {
6049
dir_dot_best = dir_dot;
6050
co_link_curr_best = j;
6054
if (co_link_curr_best != -1) {
6055
sv->co_link_curr = co_link_curr_best;
6061
static int createVertSlideVerts(TransInfo *t)
6063
BMEditMesh *em = BMEdit_FromObject(t->obedit);
6069
TransDataVertSlideVert *sv_array;
6070
VertSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
6071
// View3D *v3d = NULL;
6072
RegionView3D *rv3d = NULL;
6073
ARegion *ar = t->ar;
6074
float projectMat[4][4];
6077
if (t->spacetype == SPACE_VIEW3D) {
6078
/* background mode support */
6079
// v3d = t->sa ? t->sa->spacedata.first : NULL;
6080
rv3d = ar ? ar->regiondata : NULL;
6083
sld->is_proportional = true;
6084
sld->curr_sv_index = 0;
6085
sld->flipped_vtx = false;
6088
/* ok, let's try to survive this */
6089
unit_m4(projectMat);
6092
ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat);
6096
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
6098
if (BM_elem_flag_test(v, BM_ELEM_SELECT) && v->e) {
6099
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
6100
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
6108
BM_elem_flag_enable(v, BM_ELEM_TAG);
6112
BM_elem_flag_disable(v, BM_ELEM_TAG);
6121
sv_array = MEM_callocN(sizeof(TransDataVertSlideVert) * j, "sv_array");
6124
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
6125
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
6128
copy_v3_v3(sv_array[j].co_orig_3d, v->co);
6131
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
6132
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
6137
sv_array[j].co_link_orig_3d = MEM_mallocN(sizeof(*sv_array[j].co_link_orig_3d) * k, __func__);
6138
sv_array[j].co_link_orig_2d = MEM_mallocN(sizeof(*sv_array[j].co_link_orig_2d) * k, __func__);
6139
sv_array[j].co_link_tot = k;
6142
BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
6143
if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
6144
BMVert *v_other = BM_edge_other_vert(e, v);
6145
copy_v3_v3(sv_array[j].co_link_orig_3d[k], v_other->co);
6147
ED_view3d_project_float_v2_m4(ar,
6148
sv_array[j].co_link_orig_3d[k],
6149
sv_array[j].co_link_orig_2d[k],
6153
copy_v2_v2(sv_array[j].co_link_orig_2d[k],
6154
sv_array[j].co_link_orig_3d[k]);
6161
ED_view3d_project_float_v2_m4(ar,
6162
sv_array[j].co_orig_3d,
6163
sv_array[j].co_orig_2d,
6167
copy_v2_v2(sv_array[j].co_orig_2d,
6168
sv_array[j].co_orig_3d);
6182
t->customData = sld;
6185
calcVertSlideMouseActiveVert(t, t->mval);
6186
calcVertSlideMouseActiveEdges(t, t->mval);
6192
void freeVertSlideVerts(TransInfo *t)
6194
VertSlideData *sld = t->customData;
6200
if (sld->totsv > 0) {
6201
TransDataVertSlideVert *sv = sld->sv;
6203
for (i = 0; i < sld->totsv; i++, sv++) {
6204
MEM_freeN(sv->co_link_orig_2d);
6205
MEM_freeN(sv->co_link_orig_3d);
6212
t->customData = NULL;
6217
void initVertSlide(TransInfo *t)
6221
t->mode = TFM_VERT_SLIDE;
6222
t->transform = VertSlide;
6223
t->handleEvent = handleEventVertSlide;
6225
if (!createVertSlideVerts(t)) {
6226
t->state = TRANS_CANCEL;
6230
sld = t->customData;
6235
t->customFree = freeVertSlideVerts;
6237
/* set custom point first if you want value to be initialized by init */
6238
calcVertSlideCustomPoints(t);
6239
initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO);
6245
t->snap[2] = t->snap[1] * 0.1f;
6247
t->num.increment = t->snap[1];
6249
t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
6252
int handleEventVertSlide(struct TransInfo *t, struct wmEvent *event)
6254
if (t->mode == TFM_VERT_SLIDE) {
6255
VertSlideData *sld = t->customData;
6258
switch (event->type) {
6260
if (event->val == KM_PRESS) {
6261
sld->is_proportional = !sld->is_proportional;
6262
if (sld->flipped_vtx) {
6263
calcVertSlideCustomPoints(t);
6270
if (event->val == KM_PRESS) {
6271
sld->flipped_vtx = !sld->flipped_vtx;
6272
calcVertSlideCustomPoints(t);
6279
/* use like a modifier key */
6280
if (event->val == KM_PRESS) {
6281
t->flag ^= T_ALT_TRANSFORM;
6282
calcVertSlideCustomPoints(t);
6290
switch (event->val) {
6291
case TFM_MODAL_EDGESLIDE_DOWN:
6293
sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv;
6296
case TFM_MODAL_EDGESLIDE_UP:
6298
sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv;
6306
/* don't recalculat the best edge */
6307
const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
6309
calcVertSlideMouseActiveEdges(t, event->mval);
6311
calcVertSlideCustomPoints(t);
6321
static void drawVertSlide(const struct bContext *C, TransInfo *t)
6323
if (t->mode == TFM_VERT_SLIDE) {
6324
VertSlideData *sld = (VertSlideData *)t->customData;
6327
View3D *v3d = CTX_wm_view3d(C);
6328
TransDataVertSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
6329
TransDataVertSlideVert *sv;
6330
const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
6331
const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f;
6332
const int alpha_shade = -30;
6333
const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
6336
if (v3d && v3d->zbuf)
6337
glDisable(GL_DEPTH_TEST);
6340
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
6342
glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT | GL_POINT_BIT);
6345
glMultMatrixf(t->obedit->obmat);
6347
glLineWidth(line_size);
6348
UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
6352
for (i = 0; i < sld->totsv; i++, sv++) {
6353
glVertex3fv(sv->co_orig_3d);
6354
glVertex3fv(sv->co_link_orig_3d[sv->co_link_curr]);
6359
for (i = 0; i < sld->totsv; i++, sv++) {
6361
sub_v3_v3v3(a, sv->co_link_orig_3d[sv->co_link_curr], sv->co_orig_3d);
6362
mul_v3_fl(a, 100.0f);
6364
add_v3_v3(a, sv->co_orig_3d);
6365
add_v3_v3(b, sv->co_orig_3d);
6373
glPointSize(ctrl_size);
6375
bglBegin(GL_POINTS);
6376
bglVertex3fv((sld->flipped_vtx && sld->is_proportional == FALSE) ?
6377
curr_sv->co_link_orig_3d[curr_sv->co_link_curr] :
6378
curr_sv->co_orig_3d);
6384
glDisable(GL_BLEND);
6386
if (v3d && v3d->zbuf)
6387
glEnable(GL_DEPTH_TEST);
6392
static int doVertSlide(TransInfo *t, float perc)
6394
VertSlideData *sld = t->customData;
6395
TransDataVertSlideVert *svlist = sld->sv, *sv;
6401
if (sld->is_proportional == TRUE) {
6402
for (i = 0; i < sld->totsv; i++, sv++) {
6403
interp_v3_v3v3(sv->v->co, sv->co_orig_3d, sv->co_link_orig_3d[sv->co_link_curr], perc);
6407
TransDataVertSlideVert *sv_curr = &sld->sv[sld->curr_sv_index];
6408
const float edge_len_curr = len_v3v3(sv_curr->co_orig_3d, sv_curr->co_link_orig_3d[sv_curr->co_link_curr]);
6409
const float tperc = perc * edge_len_curr;
6411
for (i = 0; i < sld->totsv; i++, sv++) {
6415
sub_v3_v3v3(dir, sv->co_link_orig_3d[sv->co_link_curr], sv->co_orig_3d);
6416
edge_len = normalize_v3(dir);
6418
if (edge_len > FLT_EPSILON) {
6419
if (sld->flipped_vtx) {
6420
madd_v3_v3v3fl(sv->v->co, sv->co_link_orig_3d[sv->co_link_curr], dir, -tperc);
6423
madd_v3_v3v3fl(sv->v->co, sv->co_orig_3d, dir, tperc);
6427
copy_v3_v3(sv->v->co, sv->co_orig_3d);
6435
int VertSlide(TransInfo *t, const int UNUSED(mval[2]))
6440
VertSlideData *sld = t->customData;
6441
const bool flipped = sld->flipped_vtx;
6442
const bool is_proportional = sld->is_proportional;
6443
const bool is_clamp = !(t->flag & T_ALT_TRANSFORM);
6444
const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num));
6446
final = t->values[0];
6448
snapGrid(t, &final);
6450
/* only do this so out of range values are not displayed */
6451
if (is_constrained) {
6452
CLAMP(final, 0.0f, 1.0f);
6457
str_p += BLI_snprintf(str_p, sizeof(str), IFACE_("Vert Slide: "));
6458
if (hasNumInput(&t->num)) {
6459
char c[NUM_STR_REP_LEN];
6460
applyNumInput(&t->num, &final);
6461
outputNumInput(&(t->num), c);
6462
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), "%s", &c[0]);
6465
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), "%.4f ", final);
6467
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), IFACE_("(E)ven: %s, "),
6468
!is_proportional ? IFACE_("ON") : IFACE_("OFF"));
6469
if (!is_proportional) {
6470
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), IFACE_("(F)lipped: %s, "),
6471
flipped ? IFACE_("ON") : IFACE_("OFF"));
6473
str_p += BLI_snprintf(str_p, sizeof(str) - (str_p - str), IFACE_("Alt or (C)lamp: %s"),
6474
is_clamp ? IFACE_("ON") : IFACE_("OFF"));
6475
/* done with header string */
6478
if (t->customData) {
6479
doVertSlide(t, final);
6482
strcpy(str, IFACE_("Invalid Vert Selection"));
6483
t->state = TRANS_CANCEL;
6488
ED_area_headerprint(t->sa, str);
6494
/* ******************** EditBone roll *************** */
6496
void initBoneRoll(TransInfo *t)
6498
t->mode = TFM_BONE_ROLL;
6499
t->transform = BoneRoll;
6501
initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
6506
t->snap[1] = (float)((5.0 / 180) * M_PI);
6507
t->snap[2] = t->snap[1] * 0.2f;
6509
t->num.increment = 1.0f;
6511
t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT;
6514
int BoneRoll(TransInfo *t, const int UNUSED(mval[2]))
6516
TransData *td = t->data;
6522
final = t->values[0];
6524
snapGrid(t, &final);
6526
if (hasNumInput(&t->num)) {
6527
char c[NUM_STR_REP_LEN];
6529
applyNumInput(&t->num, &final);
6531
outputNumInput(&(t->num), c);
6533
sprintf(str, IFACE_("Roll: %s"), &c[0]);
6535
final = DEG2RADF(final);
6538
sprintf(str, IFACE_("Roll: %.2f"), RAD2DEGF(final));
6541
/* set roll values */
6542
for (i = 0; i < t->total; i++, td++) {
6543
if (td->flag & TD_NOACTION)
6546
if (td->flag & TD_SKIP)
6549
*(td->val) = td->ival - final;
6554
ED_area_headerprint(t->sa, str);
6559
/* ************************** BAKE TIME ******************* */
6561
void initBakeTime(TransInfo *t)
6563
t->transform = BakeTime;
6564
initMouseInputMode(t, &t->mouse, INPUT_NONE);
6570
t->snap[2] = t->snap[1] * 0.1f;
6572
t->num.increment = t->snap[1];
6575
int BakeTime(TransInfo *t, const int mval[2])
6577
TransData *td = t->data;
6584
if (t->mouse.precision) {
6585
/* calculate ratio for shiftkey pos, and for total, and blend these for precision */
6586
time = (float)(t->center2d[0] - t->mouse.precision_mval[0]) * fac;
6587
time += 0.1f * ((float)(t->center2d[0] * fac - mval[0]) - time);
6590
time = (float)(t->center2d[0] - mval[0]) * fac;
6595
applyNumInput(&t->num, &time);
6597
/* header print for NumInput */
6598
if (hasNumInput(&t->num)) {
6599
char c[NUM_STR_REP_LEN];
6601
outputNumInput(&(t->num), c);
6604
sprintf(str, IFACE_("Time: +%s %s"), c, t->proptext);
6606
sprintf(str, IFACE_("Time: %s %s"), c, t->proptext);
6609
/* default header print */
6611
sprintf(str, IFACE_("Time: +%.3f %s"), time, t->proptext);
6613
sprintf(str, IFACE_("Time: %.3f %s"), time, t->proptext);
6616
for (i = 0; i < t->total; i++, td++) {
6617
if (td->flag & TD_NOACTION)
6620
if (td->flag & TD_SKIP)
6624
*td->val = td->ival + time * td->factor;
6625
if (td->ext->size && *td->val < *td->ext->size) *td->val = *td->ext->size;
6626
if (td->ext->quat && *td->val > *td->ext->quat) *td->val = *td->ext->quat;
6632
ED_area_headerprint(t->sa, str);
6637
/* ************************** MIRROR *************************** */
6639
void initMirror(TransInfo *t)
6641
t->transform = Mirror;
6642
initMouseInputMode(t, &t->mouse, INPUT_NONE);
6644
t->flag |= T_NULL_ONE;
6646
t->flag |= T_NO_ZERO;
6650
int Mirror(TransInfo *t, const int UNUSED(mval[2]))
6653
float size[3], mat[3][3];
6659
* This still recalcs transformation on mouse move
6660
* while it should only recalc on constraint change
6663
/* if an axis has been selected */
6664
if (t->con.mode & CON_APPLY) {
6665
size[0] = size[1] = size[2] = -1;
6667
size_to_mat3(mat, size);
6669
if (t->con.applySize) {
6670
t->con.applySize(t, NULL, mat);
6673
sprintf(str, IFACE_("Mirror%s"), t->con.text);
6675
for (i = 0, td = t->data; i < t->total; i++, td++) {
6676
if (td->flag & TD_NOACTION)
6679
if (td->flag & TD_SKIP)
6682
ElementResize(t, td, mat);
6687
ED_area_headerprint(t->sa, str);
6690
size[0] = size[1] = size[2] = 1;
6692
size_to_mat3(mat, size);
6694
for (i = 0, td = t->data; i < t->total; i++, td++) {
6695
if (td->flag & TD_NOACTION)
6698
if (td->flag & TD_SKIP)
6701
ElementResize(t, td, mat);
6706
if (t->flag & T_2D_EDIT)
6707
ED_area_headerprint(t->sa, IFACE_("Select a mirror axis (X, Y)"));
6709
ED_area_headerprint(t->sa, IFACE_("Select a mirror axis (X, Y, Z)"));
6715
/* ************************** ALIGN *************************** */
6717
void initAlign(TransInfo *t)
6719
t->flag |= T_NO_CONSTRAINT;
6721
t->transform = Align;
6723
initMouseInputMode(t, &t->mouse, INPUT_NONE);
6726
int Align(TransInfo *t, const int UNUSED(mval[2]))
6728
TransData *td = t->data;
6732
/* saving original center */
6733
copy_v3_v3(center, t->center);
6735
for (i = 0; i < t->total; i++, td++) {
6736
float mat[3][3], invmat[3][3];
6738
if (td->flag & TD_NOACTION)
6741
if (td->flag & TD_SKIP)
6744
/* around local centers */
6745
if (t->flag & (T_OBJECT | T_POSE)) {
6746
copy_v3_v3(t->center, td->center);
6749
if (t->settings->selectmode & SCE_SELECT_FACE) {
6750
copy_v3_v3(t->center, td->center);
6754
invert_m3_m3(invmat, td->axismtx);
6756
mul_m3_m3m3(mat, t->spacemtx, invmat);
6758
ElementRotation(t, td, mat, t->around);
6761
/* restoring original center */
6762
copy_v3_v3(t->center, center);
6766
ED_area_headerprint(t->sa, IFACE_("Align"));
6771
/* ************************** SEQ SLIDE *************************** */
6773
void initSeqSlide(TransInfo *t)
6775
t->transform = SeqSlide;
6777
initMouseInputMode(t, &t->mouse, INPUT_VECTOR);
6781
t->num.idx_max = t->idx_max;
6784
t->snap[1] = floor(t->scene->r.frs_sec / t->scene->r.frs_sec_base);
6787
t->num.increment = t->snap[1];
6790
static void headerSeqSlide(TransInfo *t, float val[2], char *str, size_t str_len)
6792
char tvec[NUM_STR_REP_LEN * 3];
6795
if (hasNumInput(&t->num)) {
6796
outputNumInput(&(t->num), tvec);
6799
sprintf(&tvec[0], "%.0f, %.0f", val[0], val[1]);
6803
str_p += BLI_snprintf(str, str_len, IFACE_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text);
6806
wmKeyMapItem *kmi = WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_TRANSLATE);
6808
str_p += WM_keymap_item_to_string(kmi, str_p, str_len - (str_p - str));
6811
str_p += BLI_snprintf(str_p, str_len - (str_p - str), IFACE_(" or Alt) Expand to fit %s"),
6812
(t->flag & T_ALT_TRANSFORM) ? IFACE_("ON") : IFACE_("OFF"));
6815
static void applySeqSlide(TransInfo *t, float val[2])
6817
TransData *td = t->data;
6820
for (i = 0; i < t->total; i++, td++) {
6823
if (td->flag & TD_NOACTION)
6826
if (td->flag & TD_SKIP)
6829
copy_v2_v2(tvec, val);
6831
mul_v2_fl(tvec, td->factor);
6833
td->loc[0] = td->iloc[0] + tvec[0];
6834
td->loc[1] = td->iloc[1] + tvec[1];
6838
int SeqSlide(TransInfo *t, const int UNUSED(mval[2]))
6842
if (t->con.mode & CON_APPLY) {
6843
float pvec[3] = {0.0f, 0.0f, 0.0f};
6845
t->con.applyVec(t, NULL, t->values, tvec, pvec);
6846
copy_v3_v3(t->values, tvec);
6849
snapGrid(t, t->values);
6850
applyNumInput(&t->num, t->values);
6853
t->values[0] = floor(t->values[0] + 0.5f);
6854
t->values[1] = floor(t->values[1] + 0.5f);
6856
headerSeqSlide(t, t->values, str, sizeof(str));
6857
applySeqSlide(t, t->values);
6861
ED_area_headerprint(t->sa, str);
6866
/* ************************** ANIM EDITORS - TRANSFORM TOOLS *************************** */
6868
/* ---------------- Special Helpers for Various Settings ------------- */
6871
/* This function returns the snapping 'mode' for Animation Editors only
6872
* We cannot use the standard snapping due to NLA-strip scaling complexities.
6874
// XXX these modifier checks should be keymappable
6875
static short getAnimEdit_SnapMode(TransInfo *t)
6877
short autosnap = SACTSNAP_OFF;
6879
if (t->spacetype == SPACE_ACTION) {
6880
SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
6883
autosnap = saction->autosnap;
6885
else if (t->spacetype == SPACE_IPO) {
6886
SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
6889
autosnap = sipo->autosnap;
6891
else if (t->spacetype == SPACE_NLA) {
6892
SpaceNla *snla = (SpaceNla *)t->sa->spacedata.first;
6895
autosnap = snla->autosnap;
6898
autosnap = SACTSNAP_OFF;
6901
/* toggle autosnap on/off
6902
* - when toggling on, prefer nearest frame over 1.0 frame increments
6904
if (t->modifiers & MOD_SNAP_INVERT) {
6906
autosnap = SACTSNAP_OFF;
6908
autosnap = SACTSNAP_FRAME;
6914
/* This function is used for testing if an Animation Editor is displaying
6915
* its data in frames or seconds (and the data needing to be edited as such).
6916
* Returns 1 if in seconds, 0 if in frames
6918
static short getAnimEdit_DrawTime(TransInfo *t)
6922
if (t->spacetype == SPACE_ACTION) {
6923
SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
6925
drawtime = (saction->flag & SACTION_DRAWTIME) ? 1 : 0;
6927
else if (t->spacetype == SPACE_NLA) {
6928
SpaceNla *snla = (SpaceNla *)t->sa->spacedata.first;
6930
drawtime = (snla->flag & SNLA_DRAWTIME) ? 1 : 0;
6932
else if (t->spacetype == SPACE_IPO) {
6933
SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
6935
drawtime = (sipo->flag & SIPO_DRAWTIME) ? 1 : 0;
6945
/* This function is used by Animation Editor specific transform functions to do
6946
* the Snap Keyframe to Nearest Frame/Marker
6948
static void doAnimEdit_SnapFrame(TransInfo *t, TransData *td, TransData2D *td2d, AnimData *adt, short autosnap)
6950
/* snap key to nearest frame? */
6951
if (autosnap == SACTSNAP_FRAME) {
6953
#if 0 /* 'do_time' disabled for now */
6955
const Scene *scene = t->scene;
6956
const short do_time = 0; //getAnimEdit_DrawTime(t); // NOTE: this works, but may be confusing behavior given the option's label, hence disabled
6957
const double secf = FPS;
6961
/* convert frame to nla-action time (if needed) */
6963
val = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP);
6967
#if 0 /* 'do_time' disabled for now */
6969
/* do the snapping to nearest frame/second */
6971
val = (float)(floor((val / secf) + 0.5f) * secf);
6976
val = floor(val + 0.5);
6979
/* convert frame out of nla-action time */
6981
*(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP);
6985
/* snap key to nearest marker? */
6986
else if (autosnap == SACTSNAP_MARKER) {
6989
/* convert frame to nla-action time (if needed) */
6991
val = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP);
6995
/* snap to nearest marker */
6996
// TODO: need some more careful checks for where data comes from
6997
val = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, val);
6999
/* convert frame out of nla-action time */
7001
*(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP);
7006
/* if the handles are to be moved too (as side-effect of keyframes moving, to keep the general effect)
7007
* offset them by the same amount so that the general angles are maintained (i.e. won't change while
7008
* handles are free-to-roam and keyframes are snap-locked)
7010
if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
7011
td2d->h1[0] = td2d->ih1[0] + *td->val - td->ival;
7013
if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
7014
td2d->h2[0] = td2d->ih2[0] + *td->val - td->ival;
7018
/* ----------------- Translation ----------------------- */
7020
void initTimeTranslate(TransInfo *t)
7022
/* this tool is only really available in the Action Editor... */
7023
if (!ELEM(t->spacetype, SPACE_ACTION, SPACE_SEQ)) {
7024
t->state = TRANS_CANCEL;
7027
t->mode = TFM_TIME_TRANSLATE;
7028
t->transform = TimeTranslate;
7030
initMouseInputMode(t, &t->mouse, INPUT_NONE);
7032
/* num-input has max of (n-1) */
7035
t->num.idx_max = t->idx_max;
7037
/* initialize snap like for everything else */
7039
t->snap[1] = t->snap[2] = 1.0f;
7041
t->num.increment = t->snap[1];
7044
static void headerTimeTranslate(TransInfo *t, char *str)
7046
char tvec[NUM_STR_REP_LEN * 3];
7048
/* if numeric input is active, use results from that, otherwise apply snapping to result */
7049
if (hasNumInput(&t->num)) {
7050
outputNumInput(&(t->num), tvec);
7053
const Scene *scene = t->scene;
7054
const short autosnap = getAnimEdit_SnapMode(t);
7055
const short do_time = getAnimEdit_DrawTime(t);
7056
const double secf = FPS;
7057
float val = t->values[0];
7059
/* apply snapping + frame->seconds conversions */
7060
if (autosnap == SACTSNAP_STEP) {
7062
val = floorf((double)val / secf + 0.5);
7064
val = floorf(val + 0.5f);
7068
val = (float)((double)val / secf);
7071
if (autosnap == SACTSNAP_FRAME)
7072
sprintf(&tvec[0], "%d.00 (%.4f)", (int)val, val);
7074
sprintf(&tvec[0], "%.4f", val);
7077
sprintf(str, IFACE_("DeltaX: %s"), &tvec[0]);
7080
static void applyTimeTranslate(TransInfo *t, float UNUSED(sval))
7082
TransData *td = t->data;
7083
TransData2D *td2d = t->data2d;
7084
Scene *scene = t->scene;
7087
const short do_time = getAnimEdit_DrawTime(t);
7088
const double secf = FPS;
7090
const short autosnap = getAnimEdit_SnapMode(t);
7092
float deltax, val /* , valprev */;
7094
/* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */
7095
for (i = 0; i < t->total; i++, td++, td2d++) {
7096
/* it is assumed that td->extra is a pointer to the AnimData,
7097
* whose active action is where this keyframe comes from
7098
* (this is only valid when not in NLA)
7100
AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
7102
/* valprev = *td->val; */ /* UNUSED */
7104
/* check if any need to apply nla-mapping */
7105
if (adt && t->spacetype != SPACE_SEQ) {
7106
deltax = t->values[0];
7108
if (autosnap == SACTSNAP_STEP) {
7110
deltax = (float)(floor(((double)deltax / secf) + 0.5) * secf);
7112
deltax = (float)(floor(deltax + 0.5f));
7115
val = BKE_nla_tweakedit_remap(adt, td->ival, NLATIME_CONVERT_MAP);
7117
*(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP);
7120
deltax = val = t->values[0];
7122
if (autosnap == SACTSNAP_STEP) {
7124
val = (float)(floor(((double)deltax / secf) + 0.5) * secf);
7126
val = (float)(floor(val + 0.5f));
7129
*(td->val) = td->ival + val;
7132
/* apply nearest snapping */
7133
doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap);
7137
int TimeTranslate(TransInfo *t, const int mval[2])
7139
View2D *v2d = (View2D *)t->view;
7140
float cval[2], sval[2];
7143
/* calculate translation amount from mouse movement - in 'time-grid space' */
7144
UI_view2d_region_to_view(v2d, mval[0], mval[0], &cval[0], &cval[1]);
7145
UI_view2d_region_to_view(v2d, t->imval[0], t->imval[0], &sval[0], &sval[1]);
7147
/* we only need to calculate effect for time (applyTimeTranslate only needs that) */
7148
t->values[0] = cval[0] - sval[0];
7150
/* handle numeric-input stuff */
7151
t->vec[0] = t->values[0];
7152
applyNumInput(&t->num, &t->vec[0]);
7153
t->values[0] = t->vec[0];
7154
headerTimeTranslate(t, str);
7156
applyTimeTranslate(t, sval[0]);
7160
ED_area_headerprint(t->sa, str);
7165
/* ----------------- Time Slide ----------------------- */
7167
void initTimeSlide(TransInfo *t)
7169
/* this tool is only really available in the Action Editor... */
7170
if (t->spacetype == SPACE_ACTION) {
7171
SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
7173
/* set flag for drawing stuff */
7174
saction->flag |= SACTION_MOVING;
7177
t->state = TRANS_CANCEL;
7181
t->mode = TFM_TIME_SLIDE;
7182
t->transform = TimeSlide;
7183
t->flag |= T_FREE_CUSTOMDATA;
7185
initMouseInputMode(t, &t->mouse, INPUT_NONE);
7187
/* num-input has max of (n-1) */
7190
t->num.idx_max = t->idx_max;
7192
/* initialize snap like for everything else */
7194
t->snap[1] = t->snap[2] = 1.0f;
7196
t->num.increment = t->snap[1];
7199
static void headerTimeSlide(TransInfo *t, float sval, char *str)
7201
char tvec[NUM_STR_REP_LEN * 3];
7203
if (hasNumInput(&t->num)) {
7204
outputNumInput(&(t->num), tvec);
7207
float minx = *((float *)(t->customData));
7208
float maxx = *((float *)(t->customData) + 1);
7209
float cval = t->values[0];
7212
val = 2.0f * (cval - sval) / (maxx - minx);
7213
CLAMP(val, -1.0f, 1.0f);
7215
sprintf(&tvec[0], "%.4f", val);
7218
sprintf(str, IFACE_("TimeSlide: %s"), &tvec[0]);
7221
static void applyTimeSlide(TransInfo *t, float sval)
7223
TransData *td = t->data;
7226
float minx = *((float *)(t->customData));
7227
float maxx = *((float *)(t->customData) + 1);
7229
/* set value for drawing black line */
7230
if (t->spacetype == SPACE_ACTION) {
7231
SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
7232
float cvalf = t->values[0];
7234
saction->timeslide = cvalf;
7237
/* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */
7238
for (i = 0; i < t->total; i++, td++) {
7239
/* it is assumed that td->extra is a pointer to the AnimData,
7240
* whose active action is where this keyframe comes from
7241
* (this is only valid when not in NLA)
7243
AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
7244
float cval = t->values[0];
7246
/* apply NLA-mapping to necessary values */
7248
cval = BKE_nla_tweakedit_remap(adt, cval, NLATIME_CONVERT_UNMAP);
7250
/* only apply to data if in range */
7251
if ((sval > minx) && (sval < maxx)) {
7252
float cvalc = CLAMPIS(cval, minx, maxx);
7256
if (td->ival < sval) {
7257
timefac = (sval - td->ival) / (sval - minx);
7258
*(td->val) = cvalc - timefac * (cvalc - minx);
7261
timefac = (td->ival - sval) / (maxx - sval);
7262
*(td->val) = cvalc + timefac * (maxx - cvalc);
7268
int TimeSlide(TransInfo *t, const int mval[2])
7270
View2D *v2d = (View2D *)t->view;
7271
float cval[2], sval[2];
7272
float minx = *((float *)(t->customData));
7273
float maxx = *((float *)(t->customData) + 1);
7276
/* calculate mouse co-ordinates */
7277
UI_view2d_region_to_view(v2d, mval[0], mval[1], &cval[0], &cval[1]);
7278
UI_view2d_region_to_view(v2d, t->imval[0], t->imval[1], &sval[0], &sval[1]);
7280
/* t->values[0] stores cval[0], which is the current mouse-pointer location (in frames) */
7281
// XXX Need to be able to repeat this
7282
t->values[0] = cval[0];
7284
/* handle numeric-input stuff */
7285
t->vec[0] = 2.0f * (cval[0] - sval[0]) / (maxx - minx);
7286
applyNumInput(&t->num, &t->vec[0]);
7287
t->values[0] = (maxx - minx) * t->vec[0] / 2.0f + sval[0];
7289
headerTimeSlide(t, sval[0], str);
7290
applyTimeSlide(t, sval[0]);
7294
ED_area_headerprint(t->sa, str);
7299
/* ----------------- Scaling ----------------------- */
7301
void initTimeScale(TransInfo *t)
7305
/* this tool is only really available in the Action Editor
7306
* AND NLA Editor (for strip scaling)
7308
if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA) == 0) {
7309
t->state = TRANS_CANCEL;
7312
t->mode = TFM_TIME_SCALE;
7313
t->transform = TimeScale;
7315
/* recalculate center2d to use CFRA and mouse Y, since that's
7316
* what is used in time scale */
7317
t->center[0] = t->scene->r.cfra;
7318
projectIntView(t, t->center, center);
7319
center[1] = t->imval[1];
7321
/* force a reinit with the center2d used here */
7322
initMouseInput(t, &t->mouse, center, t->imval);
7324
initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP);
7326
t->flag |= T_NULL_ONE;
7327
t->num.flag |= NUM_NULL_ONE;
7329
/* num-input has max of (n-1) */
7332
t->num.idx_max = t->idx_max;
7334
/* initialize snap like for everything else */
7336
t->snap[1] = t->snap[2] = 1.0f;
7338
t->num.increment = t->snap[1];
7341
static void headerTimeScale(TransInfo *t, char *str)
7343
char tvec[NUM_STR_REP_LEN * 3];
7345
if (hasNumInput(&t->num))
7346
outputNumInput(&(t->num), tvec);
7348
sprintf(&tvec[0], "%.4f", t->values[0]);
7350
sprintf(str, IFACE_("ScaleX: %s"), &tvec[0]);
7353
static void applyTimeScale(TransInfo *t)
7355
Scene *scene = t->scene;
7356
TransData *td = t->data;
7357
TransData2D *td2d = t->data2d;
7360
const short autosnap = getAnimEdit_SnapMode(t);
7361
const short do_time = getAnimEdit_DrawTime(t);
7362
const double secf = FPS;
7365
for (i = 0; i < t->total; i++, td++, td2d++) {
7366
/* it is assumed that td->extra is a pointer to the AnimData,
7367
* whose active action is where this keyframe comes from
7368
* (this is only valid when not in NLA)
7370
AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
7371
float startx = CFRA;
7372
float fac = t->values[0];
7374
if (autosnap == SACTSNAP_STEP) {
7376
fac = (float)(floor((double)fac / secf + 0.5) * secf);
7378
fac = (float)(floor(fac + 0.5f));
7381
/* check if any need to apply nla-mapping */
7383
startx = BKE_nla_tweakedit_remap(adt, startx, NLATIME_CONVERT_UNMAP);
7385
/* now, calculate the new value */
7386
*(td->val) = ((td->ival - startx) * fac) + startx;
7388
/* apply nearest snapping */
7389
doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap);
7393
int TimeScale(TransInfo *t, const int UNUSED(mval[2]))
7397
/* handle numeric-input stuff */
7398
t->vec[0] = t->values[0];
7399
applyNumInput(&t->num, &t->vec[0]);
7400
t->values[0] = t->vec[0];
7401
headerTimeScale(t, str);
7407
ED_area_headerprint(t->sa, str);
7412
/* ************************************ */
7414
void BIF_TransformSetUndo(const char *UNUSED(str))
7417
//Trans.undostr = str;
7421
/* TODO, move to: transform_queries.c */
7422
bool checkUseLocalCenter_GraphEdit(TransInfo *t)
7424
return ((t->around == V3D_LOCAL) && !ELEM3(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE));