Blender V4.5
editors/transform/transform.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_listbase.h"
10#include "BLI_math_matrix.h"
11#include "BLI_math_vector.h"
12#include "BLI_rect.h"
13
14#include "BKE_context.hh"
15#include "BKE_editmesh.hh"
16#include "BKE_global.hh"
17#include "BKE_layer.hh"
18#include "BKE_mask.h"
19#include "BKE_screen.hh"
20#include "BKE_workspace.hh"
21
22#include "GPU_state.hh"
23
24#include "ED_clip.hh"
25#include "ED_gpencil_legacy.hh"
26#include "ED_image.hh"
27#include "ED_screen.hh"
28#include "ED_space_api.hh"
29#include "ED_uvedit.hh"
30
31#include "ANIM_keyframing.hh"
32
33#include "SEQ_transform.hh"
34
35#include "WM_api.hh"
36#include "WM_message.hh"
37
38#include "UI_interface_icons.hh"
39#include "UI_resources.hh"
40#include "UI_view2d.hh"
41
42#include "RNA_access.hh"
43
44#include "BLF_api.hh"
45#include "BLT_translation.hh"
46
47#include "transform.hh"
49#include "transform_convert.hh"
51#include "transform_gizmo.hh"
52#include "transform_mode.hh"
54#include "transform_snap.hh"
55
56/* Disabling, since when you type you know what you are doing,
57 * and being able to set it to zero is handy. */
58// #define USE_NUM_NO_ZERO.
59
60namespace blender::ed::transform {
61
62/* -------------------------------------------------------------------- */
65
66void transform_view_vector_calc(const TransInfo *t, const float focus[3], float r_vec[3])
67{
68 if (t->persp != RV3D_ORTHO) {
69 sub_v3_v3v3(r_vec, t->viewinv[3], focus);
70 }
71 else {
72 copy_v3_v3(r_vec, t->viewinv[2]);
73 }
74 normalize_v3(r_vec);
75}
76
78{
80 return false;
81 }
82 return ((around == V3D_AROUND_LOCAL_ORIGINS) &&
84}
85
87
88/* ************************** SPACE DEPENDENT CODE **************************** */
89
91{
92 if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && t->region &&
94 {
95 RegionView3D *rv3d = static_cast<RegionView3D *>(t->region->regiondata);
96
97 copy_m4_m4(t->viewmat, rv3d->viewmat);
98 copy_m4_m4(t->viewinv, rv3d->viewinv);
99 copy_m4_m4(t->persmat, rv3d->persmat);
100 copy_m4_m4(t->persinv, rv3d->persinv);
101 t->persp = rv3d->persp;
102 }
103 else {
104 unit_m4(t->viewmat);
105 unit_m4(t->viewinv);
106 unit_m4(t->persmat);
107 unit_m4(t->persinv);
108 t->persp = RV3D_ORTHO;
109 }
110}
111
112void setTransformViewAspect(TransInfo *t, float r_aspect[3])
113{
114 copy_v3_fl(r_aspect, 1.0f);
115
116 if (t->spacetype == SPACE_IMAGE) {
117 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
118
119 if (t->options & CTX_MASK) {
120 ED_space_image_get_aspect(sima, &r_aspect[0], &r_aspect[1]);
121 }
122 else if (t->options & CTX_PAINT_CURVE) {
123 /* Pass. */
124 }
125 else {
126 ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]);
127 }
128 }
129 else if (t->spacetype == SPACE_SEQ) {
130 if (t->options & CTX_CURSOR) {
131 const float2 aspect = seq::image_preview_unit_to_px(t->scene, r_aspect);
132 copy_v2_v2(r_aspect, aspect);
133 }
134 }
135 else if (t->spacetype == SPACE_CLIP) {
136 SpaceClip *sclip = static_cast<SpaceClip *>(t->area->spacedata.first);
137
138 if (t->options & CTX_MOVIECLIP) {
139 ED_space_clip_get_aspect_dimension_aware(sclip, &r_aspect[0], &r_aspect[1]);
140 }
141 else {
142 ED_space_clip_get_aspect(sclip, &r_aspect[0], &r_aspect[1]);
143 }
144 }
145 else if (t->spacetype == SPACE_GRAPH) {
146 /* Depends on context of usage. */
147 }
148}
149
150static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
151{
152 float divx = BLI_rcti_size_x(&v2d->mask);
153 float divy = BLI_rcti_size_y(&v2d->mask);
154
155 r_vec[0] = BLI_rctf_size_x(&v2d->cur) * dx / divx;
156 r_vec[1] = BLI_rctf_size_y(&v2d->cur) * dy / divy;
157 r_vec[2] = 0.0f;
158}
159
160static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
161{
162 float divx = BLI_rcti_size_x(&v2d->mask);
163 float divy = BLI_rcti_size_y(&v2d->mask);
164
165 float mulx = BLI_rctf_size_x(&v2d->cur);
166 float muly = BLI_rctf_size_y(&v2d->cur);
167
168 /* Difference with #convertViewVec2D. */
169 /* Clamp w/h, mask only. */
170 if (mulx / divx < muly / divy) {
171 divy = divx;
172 muly = mulx;
173 }
174 else {
175 divx = divy;
176 mulx = muly;
177 }
178 /* End difference. */
179
180 r_vec[0] = mulx * dx / divx;
181 r_vec[1] = muly * dy / divy;
182 r_vec[2] = 0.0f;
183}
184
185void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
186{
187 if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
188 if (t->options & CTX_PAINT_CURVE) {
189 r_vec[0] = dx;
190 r_vec[1] = dy;
191 }
192 else {
193 const float xy_delta[2] = {float(dx), float(dy)};
194 ED_view3d_win_to_delta(t->region, xy_delta, t->zfac, r_vec);
195 }
196 }
197 else if (t->spacetype == SPACE_IMAGE) {
198 if (t->options & CTX_MASK) {
199 convertViewVec2D_mask(static_cast<View2D *>(t->view), r_vec, dx, dy);
200 }
201 else if (t->options & CTX_PAINT_CURVE) {
202 r_vec[0] = dx;
203 r_vec[1] = dy;
204 }
205 else {
206 convertViewVec2D(static_cast<View2D *>(t->view), r_vec, dx, dy);
207 }
208
209 r_vec[0] *= t->aspect[0];
210 r_vec[1] *= t->aspect[1];
211 }
212 else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
213 convertViewVec2D(static_cast<View2D *>(t->view), r_vec, dx, dy);
214 }
215 else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
216 convertViewVec2D(&t->region->v2d, r_vec, dx, dy);
217 }
218 else if (t->spacetype == SPACE_CLIP) {
219 if (t->options & CTX_MASK) {
220 convertViewVec2D_mask(static_cast<View2D *>(t->view), r_vec, dx, dy);
221 }
222 else {
223 convertViewVec2D(static_cast<View2D *>(t->view), r_vec, dx, dy);
224 }
225
226 r_vec[0] *= t->aspect[0];
227 r_vec[1] *= t->aspect[1];
228 }
229 else {
230 printf("%s: called in an invalid context\n", __func__);
231 zero_v3(r_vec);
232 }
233}
234
236{
237 const ARegion *region = t->region;
238
239 if (UNLIKELY(region == nullptr)) {
240 /* While this function probably wont be calved without a region.
241 * Doing so shouldn't cause errors. */
242 adr[0] = 0.0f;
243 adr[1] = 0.0f;
244 return;
245 }
246
247 bool changed = false;
248 switch (t->spacetype) {
249 case SPACE_VIEW3D: {
250 if (region->regiontype == RGN_TYPE_WINDOW) {
251 /* NOTE(@ideasman42): When picking a fallback there isn't a "correct" location.
252 * By default the region center is the fallback, use this unless there is a reason not to.
253 *
254 * One exception is when transforming the camera from the camera viewpoint.
255 * In this case it's logical to use the camera frames center, see: #141663. */
256 if (t->options & CTX_CAMERA) {
257 const View3D *v3d = static_cast<View3D *>(t->view);
258 const RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
259 if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
260 /* Exclude any camera "shift" because the un-shifted point is the pivot.
261 *
262 * This may not be the case when transforming a cameras parent however
263 * in these situations it's not practical to find a screen space location
264 * for a 3D point that couldn't be projected. */
265 const bool no_shift = true;
266 rctf viewborder = {0};
268 t->scene, t->depsgraph, region, v3d, rv3d, no_shift, &viewborder);
269 adr[0] = BLI_rctf_cent_x(&viewborder);
270 adr[1] = BLI_rctf_cent_y(&viewborder);
271 changed = true;
272 }
273 }
274 }
275 break;
276 }
277 }
278
279 if (changed == false) {
280 adr[0] = region->winx / 2.0f;
281 adr[1] = region->winy / 2.0f;
282 }
283}
284
285void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
286{
287 if (t->spacetype == SPACE_VIEW3D) {
288 if (t->region->regiontype == RGN_TYPE_WINDOW) {
290 /* This is what was done in 2.64, perhaps we can be smarter? */
291 adr[0] = int(2140000000.0f);
292 adr[1] = int(2140000000.0f);
293 }
294 }
295 }
296 else if (t->spacetype == SPACE_IMAGE) {
297 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
298
299 if (t->options & CTX_MASK) {
300 float v[2];
301
302 v[0] = vec[0] / t->aspect[0];
303 v[1] = vec[1] / t->aspect[1];
304
305 BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v);
306
308
309 adr[0] = v[0];
310 adr[1] = v[1];
311 }
312 else if (t->options & CTX_PAINT_CURVE) {
313 adr[0] = vec[0];
314 adr[1] = vec[1];
315 }
316 else {
317 float v[2];
318
319 v[0] = vec[0] / t->aspect[0];
320 v[1] = vec[1] / t->aspect[1];
321
322 UI_view2d_view_to_region(static_cast<const View2D *>(t->view), v[0], v[1], &adr[0], &adr[1]);
323 }
324 }
325 else if (t->spacetype == SPACE_ACTION) {
326 int out[2] = {0, 0};
327#if 0
328 SpaceAction *sact = t->area->spacedata.first;
329
330 if (sact->flag & SACTION_DRAWTIME) {
331 // vec[0] = vec[0] / ((t->scene->r.frs_sec / t->scene->r.frs_sec_base));
332 /* Same as below. */
333 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
334 }
335 else
336#endif
337 {
338 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
339 }
340
341 adr[0] = out[0];
342 adr[1] = out[1];
343 }
344 else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_NLA)) {
345 int out[2] = {0, 0};
346
347 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
348 adr[0] = out[0];
349 adr[1] = out[1];
350 }
351 else if (t->spacetype == SPACE_SEQ) { /* XXX not tested yet, but should work. */
352 int out[2] = {0, 0};
353
354 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &out[0], &out[1]);
355 adr[0] = out[0];
356 adr[1] = out[1];
357 }
358 else if (t->spacetype == SPACE_CLIP) {
359 SpaceClip *sc = static_cast<SpaceClip *>(t->area->spacedata.first);
360
361 if (t->options & CTX_MASK) {
363
364 if (clip) {
365 float v[2];
366
367 v[0] = vec[0] / t->aspect[0];
368 v[1] = vec[1] / t->aspect[1];
369
371
373
374 adr[0] = v[0];
375 adr[1] = v[1];
376 }
377 else {
378 adr[0] = 0;
379 adr[1] = 0;
380 }
381 }
382 else if (t->options & CTX_MOVIECLIP) {
383 float v[2];
384
385 v[0] = vec[0] / t->aspect[0];
386 v[1] = vec[1] / t->aspect[1];
387
388 UI_view2d_view_to_region(static_cast<const View2D *>(t->view), v[0], v[1], &adr[0], &adr[1]);
389 }
390 else {
391 BLI_assert(0);
392 }
393 }
394 else if (t->spacetype == SPACE_NODE) {
395 UI_view2d_view_to_region((View2D *)t->view, vec[0], vec[1], &adr[0], &adr[1]);
396 }
397}
398void projectIntView(TransInfo *t, const float vec[3], int adr[2])
399{
401}
402
403void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
404{
405 switch (t->spacetype) {
406 case SPACE_VIEW3D: {
407 if (t->options & CTX_PAINT_CURVE) {
408 adr[0] = vec[0];
409 adr[1] = vec[1];
410 }
411 else if (t->region->regiontype == RGN_TYPE_WINDOW) {
412 /* Allow points behind the view #33643. */
414 /* XXX, 2.64 and prior did this, weak! */
416 }
417 return;
418 }
419 break;
420 }
421 default: {
422 int a[2] = {0, 0};
423 projectIntView(t, vec, a);
424 adr[0] = a[0];
425 adr[1] = a[1];
426 break;
427 }
428 }
429}
430void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
431{
433}
434
435void applyAspectRatio(TransInfo *t, float vec[2])
436{
437 if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) &&
438 !(t->options & CTX_PAINT_CURVE))
439 {
440 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
441
442 if ((sima->flag & SI_COORDFLOATS) == 0) {
443 int width, height;
444 ED_space_image_get_size(sima, &width, &height);
445
446 vec[0] *= width;
447 vec[1] *= height;
448 }
449
450 vec[0] /= t->aspect[0];
451 vec[1] /= t->aspect[1];
452 }
453 else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
454 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
455 vec[0] /= t->aspect[0];
456 vec[1] /= t->aspect[1];
457 }
458 }
459}
460
461void removeAspectRatio(TransInfo *t, float vec[2])
462{
463 if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) {
464 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
465
466 if ((sima->flag & SI_COORDFLOATS) == 0) {
467 int width, height;
468 ED_space_image_get_size(sima, &width, &height);
469
470 vec[0] /= width;
471 vec[1] /= height;
472 }
473
474 vec[0] *= t->aspect[0];
475 vec[1] *= t->aspect[1];
476 }
477 else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) {
478 if (t->options & (CTX_MOVIECLIP | CTX_MASK)) {
479 vec[0] *= t->aspect[0];
480 vec[1] *= t->aspect[1];
481 }
482 }
483}
484
485static void viewRedrawForce(const bContext *C, TransInfo *t)
486{
487 if (t->options & CTX_GPENCIL_STROKES) {
488 if (t->obedit_type == OB_GREASE_PENCIL) {
490 }
491 }
492 else if (t->spacetype == SPACE_VIEW3D) {
493 if (t->options & CTX_PAINT_CURVE) {
494 wmWindow *window = CTX_wm_window(C);
496 }
497 else {
498 /* Do we need more refined tags? */
499 if (t->options & CTX_POSE_BONE) {
501 }
502 else {
504 }
505
506 /* For real-time animation record - send notifiers recognized by animation editors. */
507 /* XXX: is this notifier a lame duck? */
508 if ((t->animtimer) && animrig::is_autokey_on(t->scene)) {
510 }
511 }
512 }
513 else if (t->spacetype == SPACE_ACTION) {
514 // SpaceAction *saction = (SpaceAction *)t->area->spacedata.first;
516 }
517 else if (t->spacetype == SPACE_GRAPH) {
518 // SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
520 }
521 else if (t->spacetype == SPACE_NLA) {
523 }
524 else if (t->spacetype == SPACE_NODE) {
525 // ED_area_tag_redraw(t->area);
527 }
528 else if (t->spacetype == SPACE_SEQ) {
530 /* Key-frames on strips has been moved, so make sure related editors are informed. */
532 }
533 else if (t->spacetype == SPACE_IMAGE) {
534 if (t->options & CTX_MASK) {
536
538 }
539 else if (t->options & CTX_PAINT_CURVE) {
540 wmWindow *window = CTX_wm_window(C);
542 }
543 else if (t->options & CTX_CURSOR) {
545 }
546 else {
547 /* XXX how to deal with lock? */
549 if (sima->lock) {
553 }
554 else {
556 }
557 }
558 }
559 else if (t->spacetype == SPACE_CLIP) {
561
564
565 /* Objects could be parented to tracking data, so send this for viewport refresh. */
567
569 }
572
574 }
575 }
576}
577
579{
580 ED_area_status_text(t->area, nullptr);
581 WorkSpace *workspace = CTX_wm_workspace(C);
582 if (workspace) {
584 }
585
586 if (t->spacetype == SPACE_VIEW3D) {
587 /* If auto-keying is enabled, send notifiers that keyframes were added. */
590 }
591
592 /* Redraw UV editor. */
593 const char uvcalc_correct_flag = ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) ?
596
597 if ((t->data_type == &TransConvertType_Mesh) &&
598 (t->settings->uvcalc_flag & uvcalc_correct_flag))
599 {
601 }
602 }
603}
604
605/* ************************************************* */
606
607static bool transform_modal_item_poll(const wmOperator *op, int value)
608{
609 const TransInfo *t = static_cast<const TransInfo *>(op->customdata);
611 if (value == TFM_MODAL_EDIT_SNAP_SOURCE_OFF) {
612 return true;
613 }
614 if (!ELEM(
616 {
617 return false;
618 }
619 }
620
621 switch (value) {
622 case TFM_MODAL_CANCEL: {
623 /* TODO: Canceling with LMB is not possible when the operator is activated
624 * through tweak and the LMB is pressed.
625 * Therefore, this item should not appear in the status bar. */
626 break;
627 }
631 if ((t->flag & T_PROP_EDIT) == 0) {
632 return false;
633 }
634 break;
635 }
638 if (t->spacetype != SPACE_VIEW3D) {
639 return false;
640 }
641 if (value == TFM_MODAL_ADD_SNAP) {
642 if (!(t->tsnap.status & SNAP_TARGET_FOUND)) {
643 return false;
644 }
645 }
646 else {
647 if (!t->tsnap.selectedPoint) {
648 return false;
649 }
650 }
651 break;
652 }
653 case TFM_MODAL_AXIS_Z:
658 if (t->flag & T_2D_EDIT) {
659 return false;
660 }
661 [[fallthrough]];
662 case TFM_MODAL_AXIS_X:
663 case TFM_MODAL_AXIS_Y:
665 if (t->flag & T_NO_CONSTRAINT) {
666 return false;
667 }
668 break;
669 case TFM_MODAL_CONS_OFF: {
670 if ((t->con.mode & CON_APPLY) == 0) {
671 return false;
672 }
673 break;
674 }
679 if (t->spacetype != SPACE_NODE) {
680 return false;
681 }
682 break;
683 }
686 if ((t->flag & T_AUTOIK) == 0) {
687 return false;
688 }
689 break;
690 }
692 case TFM_MODAL_ROTATE:
693 case TFM_MODAL_RESIZE:
698 return false;
699 }
700 if (value == TFM_MODAL_TRANSLATE && t->mode == TFM_TRANSLATION) {
701 /* The tracking transform in MovieClip has an alternate translate that modifies the offset
702 * of the tracks. */
704 }
705 if (value == TFM_MODAL_ROTATE && t->mode == TFM_ROTATION) {
706 return false;
707 }
708 if (value == TFM_MODAL_RESIZE && t->mode == TFM_RESIZE) {
709 /* The tracking transform in MovieClip has an alternate resize that only affects the
710 * tracker size and not the search area. */
712 }
713 if (value == TFM_MODAL_VERT_EDGE_SLIDE &&
715 /* WORKAROUND: Avoid repeated keys in status bar.
716 *
717 * Previously, `Vert/Edge Slide` and `Move` were triggered by the same modal key.
718 * But now, to fix #100129 (Status bar incorrectly shows "[G] Move"), `Vert/Edge Slide`
719 * has its own modal key. However by default it uses the same key as `Move` (G). So, to
720 * avoid displaying the same key twice (G and G), only display this modal key during the
721 * `Move` operation.
722 *
723 * Ideally we should check if it really uses the same key. */
724 t->mode != TFM_TRANSLATION))
725 {
726 return false;
727 }
728 if (value == TFM_MODAL_TRACKBALL &&
729 /* WORKAROUND: Avoid repeated keys in status bar.
730 *
731 * Previously, `Trackball` and `Rotate` were triggered by the same modal key.
732 * But to fix the status bar incorrectly showing "[R] Rotate", `Trackball` has now its
733 * own modal key. However by default it uses the same key as `Rotate` (R). So, to avoid
734 * displaying the same key twice (R and R), only display this modal key during the
735 * `Rotate` operation.
736 *
737 * Ideally we should check if it really uses the same key. */
738 t->mode != TFM_ROTATION)
739 {
740 return false;
741 }
742 if (value == TFM_MODAL_ROTATE_NORMALS) {
743 return t->mode == TFM_ROTATION && t->data_type == &TransConvertType_Mesh;
744 }
745 break;
746 }
748 return false;
751 return false;
752 }
753 if (!ELEM(
755 {
756 /* More modes can be added over time if this feature proves useful for them. */
757 return false;
758 }
759 if (t->options & CTX_CAMERA) {
760 /* Not supported. */
761 return false;
762 }
763 break;
764 }
767 /* Returning `false` will not prevent the navigation from working, it will just not display
768 * the shortcut in the header.
769 * Return `false` here to prevent this modal item from affecting the state with
770 * #T_ALT_TRANSFORM is used by the operator. */
771 return false;
772 }
773 return t->vod != nullptr;
774 }
775 return true;
776}
777
779{
780 static const EnumPropertyItem modal_items[] = {
781 {TFM_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
782 {TFM_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
783 {TFM_MODAL_AXIS_X, "AXIS_X", 0, "X Axis", ""},
784 {TFM_MODAL_AXIS_Y, "AXIS_Y", 0, "Y Axis", ""},
785 {TFM_MODAL_AXIS_Z, "AXIS_Z", 0, "Z Axis", ""},
786 {TFM_MODAL_PLANE_X, "PLANE_X", 0, "X Plane", ""},
787 {TFM_MODAL_PLANE_Y, "PLANE_Y", 0, "Y Plane", ""},
788 {TFM_MODAL_PLANE_Z, "PLANE_Z", 0, "Z Plane", ""},
789 {TFM_MODAL_CONS_OFF, "CONS_OFF", 0, "Clear Constraints", ""},
790 {TFM_MODAL_EDIT_SNAP_SOURCE_ON, "EDIT_SNAP_SOURCE_ON", 0, "Set Snap Base", ""},
791 {TFM_MODAL_EDIT_SNAP_SOURCE_OFF, "EDIT_SNAP_SOURCE_OFF", 0, "Set Snap Base (Off)", ""},
792 {TFM_MODAL_SNAP_INV_ON, "SNAP_INV_ON", 0, "Snap Invert", ""},
793 {TFM_MODAL_SNAP_INV_OFF, "SNAP_INV_OFF", 0, "Snap Invert (Off)", ""},
794 {TFM_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap Toggle", ""},
795 {TFM_MODAL_ADD_SNAP, "ADD_SNAP", 0, "Add Snap Point", ""},
796 {TFM_MODAL_REMOVE_SNAP, "REMOVE_SNAP", 0, "Remove Last Snap Point", ""},
797 {NUM_MODAL_INCREMENT_UP, "INCREMENT_UP", 0, "Numinput Increment Up", ""},
798 {NUM_MODAL_INCREMENT_DOWN, "INCREMENT_DOWN", 0, "Numinput Increment Down", ""},
799 {TFM_MODAL_PROPSIZE_UP, "PROPORTIONAL_SIZE_UP", 0, "Increase Proportional Influence", ""},
801 "PROPORTIONAL_SIZE_DOWN",
802 0,
803 "Decrease Proportional Influence",
804 ""},
805 {TFM_MODAL_PROPSIZE, "PROPORTIONAL_SIZE", 0, "Adjust Proportional Influence", ""},
806 {TFM_MODAL_AUTOIK_LEN_INC, "AUTOIK_CHAIN_LEN_UP", 0, "Increase Max AutoIK Chain Length", ""},
808 "AUTOIK_CHAIN_LEN_DOWN",
809 0,
810 "Decrease Max AutoIK Chain Length",
811 ""},
813 "INSERTOFS_TOGGLE_DIR",
814 0,
815 "Toggle Direction for Node Auto-Offset",
816 ""},
817 {TFM_MODAL_NODE_ATTACH_ON, "NODE_ATTACH_ON", 0, "Node Attachment", ""},
818 {TFM_MODAL_NODE_ATTACH_OFF, "NODE_ATTACH_OFF", 0, "Node Attachment (Off)", ""},
819 {TFM_MODAL_TRANSLATE, "TRANSLATE", 0, "Move", ""},
820 {TFM_MODAL_VERT_EDGE_SLIDE, "VERT_EDGE_SLIDE", 0, "Vert/Edge Slide", ""},
821 {TFM_MODAL_ROTATE, "ROTATE", 0, "Rotate", ""},
822 {TFM_MODAL_TRACKBALL, "TRACKBALL", 0, "Trackball", ""},
823 {TFM_MODAL_RESIZE, "RESIZE", 0, "Resize", ""},
824 {TFM_MODAL_ROTATE_NORMALS, "ROTATE_NORMALS", 0, "Rotate Normals", ""},
825 {TFM_MODAL_AUTOCONSTRAINT, "AUTOCONSTRAIN", 0, "Automatic Constraint", ""},
826 {TFM_MODAL_AUTOCONSTRAINTPLANE, "AUTOCONSTRAINPLANE", 0, "Automatic Constraint Plane", ""},
827 {TFM_MODAL_PRECISION, "PRECISION", 0, "Precision Mode", ""},
828 {TFM_MODAL_PASSTHROUGH_NAVIGATE, "PASSTHROUGH_NAVIGATE", 0, "Navigate", ""},
829 {TFM_MODAL_NODE_FRAME, "NODE_FRAME", 0, "Attach/Detach Frame", ""},
830 {0, nullptr, 0, nullptr, nullptr},
831 };
832
833 wmKeyMap *keymap = WM_modalkeymap_ensure(keyconf, "Transform Modal Map", modal_items);
835
836 /* Default modal map values:
837 *
838 * \code{.c}
839 * WM_modalkeymap_add_item(keymap,
840 * &(const KeyMapItem_Params){
841 * .type = EVT_RETKEY,
842 * .value = KM_PRESS,
843 * .modifier = KM_ANY,
844 * .direction = KM_ANY,
845 * },
846 * TFM_MODAL_CONFIRM);
847 * WM_modalkeymap_add_item(keymap,
848 * &(const KeyMapItem_Params){
849 * .type = EVT_ESCKEY,
850 * .value = KM_PRESS,
851 * .modifier = KM_ANY,
852 * .direction = KM_ANY,
853 * },
854 * TFM_MODAL_CANCEL);
855 * WM_modalkeymap_add_item(keymap,
856 * &(const KeyMapItem_Params){
857 * .type = EVT_PAGEUPKEY,
858 * .value = KM_PRESS,
859 * .modifier = KM_ANY,
860 * .direction = KM_ANY,
861 * },
862 * TFM_MODAL_AUTOIK_LEN_INC);
863 * WM_modalkeymap_add_item(keymap,
864 * &(const KeyMapItem_Params){
865 * .type = EVT_PAGEDOWNKEY,
866 * .value = KM_PRESS,
867 * .modifier = KM_ANY,
868 * .direction = KM_ANY,
869 * },
870 * TFM_MODAL_AUTOIK_LEN_DEC);
871 * WM_modalkeymap_add_item(keymap,
872 * &(const KeyMapItem_Params){
873 * .type = EVT_GKEY,
874 * .value = KM_PRESS,
875 * .modifier = KM_ANY,
876 * .direction = KM_ANY,
877 * },
878 * TFM_MODAL_TRANSLATE);
879 * WM_modalkeymap_add_item(keymap,
880 * &(const KeyMapItem_Params){
881 * .type = EVT_RKEY,
882 * .value = KM_PRESS,
883 * .modifier = KM_ANY,
884 * .direction = KM_ANY,
885 * },
886 * TFM_MODAL_ROTATE);
887 * WM_modalkeymap_add_item(keymap,
888 * &(const KeyMapItem_Params){
889 * .type = EVT_SKEY,
890 * .value = KM_PRESS,
891 * .modifier = KM_ANY,
892 * .direction = KM_ANY,
893 * },
894 * TFM_MODAL_RESIZE);
895 * WM_modalkeymap_add_item(keymap,
896 * &(const KeyMapItem_Params){
897 * .type = MIDDLEMOUSE,
898 * .value = KM_PRESS,
899 * .modifier = KM_ANY,
900 * .direction = KM_ANY,
901 * },
902 * TFM_MODAL_AUTOCONSTRAINT);
903 * WM_modalkeymap_add_item(keymap,
904 * &(const KeyMapItem_Params){
905 * .type = MIDDLEMOUSE,
906 * .value = KM_PRESS,
907 * .modifier = KM_SHIFT,
908 * .direction = KM_ANY,
909 * },
910 * TFM_MODAL_AUTOCONSTRAINTPLANE);
911 * \endcode
912 */
913
914 return keymap;
915}
916
917static bool transform_event_modal_constraint(TransInfo *t, short modal_type)
918{
919 if (t->flag & T_NO_CONSTRAINT) {
920 return false;
921 }
922
923 if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) {
924 return false;
925 }
926
927 int constraint_curr = -1;
928
931
932 /* Avoid changing orientation in this case. */
933 constraint_curr = -2;
934 }
935 else if (t->con.mode & CON_APPLY) {
936 constraint_curr = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2);
937 }
938
939 int constraint_new;
940 const char *msg_2d = "", *msg_3d = "";
941
942 /* Initialize. */
943 switch (modal_type) {
944 case TFM_MODAL_AXIS_X:
945 msg_2d = IFACE_("along X");
946 msg_3d = IFACE_("along %s X");
947 constraint_new = CON_AXIS0;
948 break;
949 case TFM_MODAL_AXIS_Y:
950 msg_2d = IFACE_("along Y");
951 msg_3d = IFACE_("along %s Y");
952 constraint_new = CON_AXIS1;
953 break;
954 case TFM_MODAL_AXIS_Z:
955 msg_2d = IFACE_("along Z");
956 msg_3d = IFACE_("along %s Z");
957 constraint_new = CON_AXIS2;
958 break;
960 msg_3d = IFACE_("locking %s X");
961 constraint_new = CON_AXIS1 | CON_AXIS2;
962 break;
964 msg_3d = IFACE_("locking %s Y");
965 constraint_new = CON_AXIS0 | CON_AXIS2;
966 break;
968 msg_3d = IFACE_("locking %s Z");
969 constraint_new = CON_AXIS0 | CON_AXIS1;
970 break;
971 default:
972 /* Invalid key. */
973 return false;
974 }
975
976 if (t->flag & T_2D_EDIT) {
977 BLI_assert(modal_type < TFM_MODAL_PLANE_X);
978 if (constraint_new == CON_AXIS2) {
979 return false;
980 }
981
983 /* Setup the 2d msg string so it writes out the transform space. */
984 msg_2d = msg_3d;
985
986 short orient_index = 1;
987 if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) {
988 /* Successive presses on existing axis, cycle orientation modes. */
989 orient_index = short((t->orient_curr + 1) % int(ARRAY_SIZE(t->orient)));
990 }
991
992 transform_orientations_current_set(t, orient_index);
993 if (orient_index != 0) {
994 /* Make sure that we don't stop the constraint unless we are looped back around to
995 * "no constraint". */
996 constraint_curr = -1;
997 }
998 }
999
1000 if (constraint_curr == constraint_new) {
1001 stopConstraint(t);
1002 }
1003 else {
1004 setUserConstraint(t, constraint_new, msg_2d);
1005 }
1006 }
1007 else {
1008 short orient_index = 1;
1009 if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) {
1010 /* Successive presses on existing axis, cycle orientation modes. */
1011 orient_index = short((t->orient_curr + 1) % int(ARRAY_SIZE(t->orient)));
1012 }
1013
1014 transform_orientations_current_set(t, orient_index);
1015 if (orient_index == 0) {
1016 stopConstraint(t);
1017 }
1018 else {
1019 setUserConstraint(t, constraint_new, msg_3d);
1020 }
1021
1022 /* Take the opportunity to update the gizmo. */
1024 }
1025 return true;
1026}
1027
1029{
1030 bool is_navigating = t->vod ? ((RegionView3D *)t->region->regiondata)->rflag & RV3D_NAVIGATING :
1031 false;
1032
1033 /* Handle modal numinput events first, if already activated. */
1034 if (!is_navigating && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1035 hasNumInput(&t->num) && handleNumInput(t->context, &(t->num), event))
1036 {
1037 t->redraw |= TREDRAW_HARD;
1038 }
1039 else if (event->type == TIMER) {
1040 if (ED_uvedit_live_unwrap_timer_check(static_cast<const wmTimer *>(event->customdata))) {
1041 t->redraw |= TREDRAW_HARD;
1042 }
1043 }
1044 else if (!is_navigating && event->type == MOUSEMOVE) {
1045 t->mval = float2(event->mval);
1046
1047 /* Use this for soft redraw. Might cause flicker in object mode. */
1048 // t->redraw |= TREDRAW_SOFT;
1049 t->redraw |= TREDRAW_HARD;
1050
1051 if (t->state == TRANS_STARTING) {
1052 t->state = TRANS_RUNNING;
1053 }
1054
1055 applyMouseInput(t, &t->mouse, t->mval, t->values);
1056
1057 /* Snapping mouse move events. */
1058 t->redraw |= handleSnapping(t, event);
1059 }
1060 /* Handle modal keymap first. */
1061 /* Enforce redraw of transform when modifiers are used. */
1062 else if (event->type == EVT_MODAL_MAP) {
1063 switch (event->val) {
1064 case TFM_MODAL_CANCEL:
1065 if (!(t->modifiers & MOD_EDIT_SNAP_SOURCE)) {
1066 t->state = TRANS_CANCEL;
1067 }
1068 break;
1069 case TFM_MODAL_CONFIRM:
1070 if (!(t->modifiers & MOD_EDIT_SNAP_SOURCE)) {
1071 t->state = TRANS_CONFIRM;
1072 }
1073 break;
1075 case TFM_MODAL_ROTATE:
1076 case TFM_MODAL_RESIZE:
1080 /* Only switch when. */
1082 break;
1083 }
1084
1085 if ((event->val == TFM_MODAL_TRANSLATE && t->mode == TFM_TRANSLATION) ||
1086 (event->val == TFM_MODAL_RESIZE && t->mode == TFM_RESIZE))
1087 {
1090
1091 t->flag ^= T_ALT_TRANSFORM;
1092 t->redraw |= TREDRAW_HARD;
1093 }
1094 break;
1095 }
1096
1097 if ((event->val == TFM_MODAL_ROTATE && t->mode == TFM_ROTATION) ||
1098 (event->val == TFM_MODAL_TRACKBALL && t->mode == TFM_TRACKBALL) ||
1100 (event->val == TFM_MODAL_VERT_EDGE_SLIDE &&
1102 {
1103 break;
1104 }
1105
1107 break;
1108 }
1109
1111 resetTransModal(t);
1113
1114 if (event->val == TFM_MODAL_TRANSLATE) {
1116 }
1117 else if (event->val == TFM_MODAL_ROTATE) {
1118 transform_mode_init(t, nullptr, TFM_ROTATION);
1119 }
1120 else if (event->val == TFM_MODAL_TRACKBALL) {
1122 }
1123 else if (event->val == TFM_MODAL_ROTATE_NORMALS) {
1125 }
1126 else if (event->val == TFM_MODAL_RESIZE) {
1127 /* Scale isn't normally very useful after extrude along normals, see #39756 */
1128 if ((t->con.mode & CON_APPLY) && (t->orient[t->orient_curr].type == V3D_ORIENT_NORMAL)) {
1129 stopConstraint(t);
1130 }
1131 transform_mode_init(t, nullptr, TFM_RESIZE);
1132 }
1133 else {
1134 /* First try Edge Slide. */
1136 /* If that fails, try Vertex Slide. */
1137 if (t->state == TRANS_CANCEL) {
1138 resetTransModal(t);
1139 t->state = TRANS_STARTING;
1141 }
1142 /* Vert Slide can fail on unconnected vertices (rare but possible). */
1143 if (t->state == TRANS_CANCEL) {
1144 resetTransModal(t);
1145 t->state = TRANS_STARTING;
1148 }
1149 }
1150
1151 /* Need to reinitialize after mode change. */
1152 initSnapping(t, nullptr);
1153 applyMouseInput(t, &t->mouse, t->mval, t->values);
1154 t->redraw |= TREDRAW_HARD;
1155 break;
1156
1158 if (!(t->modifiers & MOD_SNAP_INVERT)) {
1161 t->redraw |= TREDRAW_HARD;
1162 }
1163 break;
1165 if (t->modifiers & MOD_SNAP_INVERT) {
1168 t->redraw |= TREDRAW_HARD;
1169 }
1170 break;
1172 t->modifiers ^= MOD_SNAP;
1174 t->redraw |= TREDRAW_HARD;
1175 break;
1176 case TFM_MODAL_AXIS_X:
1177 case TFM_MODAL_AXIS_Y:
1178 case TFM_MODAL_AXIS_Z:
1179 case TFM_MODAL_PLANE_X:
1180 case TFM_MODAL_PLANE_Y:
1181 case TFM_MODAL_PLANE_Z:
1182 if (transform_event_modal_constraint(t, event->val)) {
1183 t->redraw |= TREDRAW_HARD;
1184 }
1185 break;
1186 case TFM_MODAL_CONS_OFF:
1187 if ((t->flag & T_NO_CONSTRAINT) == 0) {
1188 stopConstraint(t);
1189 t->redraw |= TREDRAW_HARD;
1190 }
1191 break;
1192 case TFM_MODAL_ADD_SNAP:
1193 addSnapPoint(t);
1194 t->redraw |= TREDRAW_HARD;
1195 break;
1197 removeSnapPoint(t);
1198 t->redraw |= TREDRAW_HARD;
1199 break;
1200 case TFM_MODAL_PROPSIZE:
1201 /* MOUSEPAN usage... */
1202 if (t->flag & T_PROP_EDIT) {
1203 float fac = 1.0f + 0.005f * (event->xy[1] - event->prev_xy[1]);
1204 t->prop_size *= fac;
1205 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1206 t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->clip_end),
1208 }
1209 else {
1211 }
1213 t->redraw |= TREDRAW_HARD;
1214 }
1215 break;
1217 if (t->flag & T_PROP_EDIT) {
1218 t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1219 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1220 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end);
1221 }
1222 else {
1224 }
1226 t->redraw |= TREDRAW_HARD;
1227 }
1228 break;
1230 if (t->flag & T_PROP_EDIT) {
1231 t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1234 t->redraw |= TREDRAW_HARD;
1235 }
1236 break;
1238 if (t->flag & T_AUTOIK) {
1240 t->redraw |= TREDRAW_HARD;
1241 }
1242 break;
1244 if (t->flag & T_AUTOIK) {
1246 t->redraw |= TREDRAW_HARD;
1247 }
1248 break;
1250 if (t->spacetype == SPACE_NODE) {
1251 SpaceNode *snode = (SpaceNode *)t->area->spacedata.first;
1252
1254
1257 }
1258 else if (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_LEFT) {
1260 }
1261 else {
1262 BLI_assert(0);
1263 }
1264
1265 t->redraw |= TREDRAW_SOFT;
1266 }
1267 break;
1270 t->redraw |= TREDRAW_HARD;
1271 break;
1274 t->redraw |= TREDRAW_HARD;
1275 break;
1278 t->redraw |= TREDRAW_HARD;
1279 break;
1280
1283 if ((t->flag & T_RELEASE_CONFIRM) && (event->prev_val == KM_RELEASE) &&
1284 event->prev_type == t->launch_event)
1285 {
1286 /* Confirm transform if launch key is released after mouse move. */
1287 t->state = TRANS_CONFIRM;
1288 }
1289 else if ((t->flag & T_NO_CONSTRAINT) == 0) {
1291 /* Confirm. */
1294 t->redraw = TREDRAW_HARD;
1295 }
1296 else {
1297 if (t->options & CTX_CAMERA) {
1298 /* Exception for switching to dolly, or trackball, in camera view. */
1299 if (t->mode == TFM_TRANSLATION) {
1300 setLocalConstraint(t, (CON_AXIS2), IFACE_("along local Z"));
1301 }
1302 else if (t->mode == TFM_ROTATION) {
1305 }
1306 t->redraw = TREDRAW_HARD;
1307 }
1308 else {
1309 t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ?
1312 if (t->con.mode & CON_APPLY) {
1313 stopConstraint(t);
1315
1316 /* In this case we might just want to remove the constraint,
1317 * so set #TREDRAW_SOFT to only select the constraint on the next mouse move event.
1318 * This way we can kind of "cancel" due to confirmation without constraint. */
1319 t->redraw = TREDRAW_SOFT;
1320 }
1321 else {
1323
1324 /* When first called, set #TREDRAW_HARD to select constraint immediately in
1325 * #selectConstraint. */
1327 }
1328 }
1329 }
1330 }
1331 break;
1333 if (is_navigating) {
1334 /* WORKAROUND: During navigation, due to key conflicts, precision may be unintentionally
1335 * enabled. */
1336 }
1337 else if (event->prev_val == KM_PRESS) {
1339 /* Mouse position during Snap to Grid is not affected by precision. */
1340 if (!(validSnap(t) && t->tsnap.target_type == SCE_SNAP_TO_GRID)) {
1341 t->mouse.precision = true;
1342 }
1343
1344 t->redraw |= TREDRAW_HARD;
1345 }
1346 else if (event->prev_val == KM_RELEASE) {
1348 t->mouse.precision = false;
1349 t->redraw |= TREDRAW_HARD;
1350 }
1351 break;
1353 if (!(t->modifiers & MOD_EDIT_SNAP_SOURCE)) {
1355 t->redraw |= TREDRAW_HARD;
1356 }
1357 break;
1358 default:
1359 break;
1360 }
1361 }
1362 /* Else do non-mapped events. */
1363 else if (event->val == KM_PRESS) {
1364 switch (event->type) {
1365 case EVT_CKEY:
1366 if (event->flag & WM_EVENT_IS_REPEAT) {
1367 break;
1368 }
1369 if (event->modifier & KM_ALT) {
1370 if (!(t->options & CTX_NO_PET)) {
1371 t->flag ^= T_PROP_CONNECTED;
1374 t->redraw = TREDRAW_HARD;
1375 }
1376 }
1377 break;
1378 case EVT_OKEY:
1379 if (event->flag & WM_EVENT_IS_REPEAT) {
1380 break;
1381 }
1382 if ((t->flag & T_PROP_EDIT) && (event->modifier & KM_SHIFT)) {
1383 t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX;
1385 t->redraw |= TREDRAW_HARD;
1386 }
1387 break;
1388 case EVT_PADPLUSKEY:
1389 if ((event->modifier & KM_ALT) && (t->flag & T_PROP_EDIT)) {
1390 t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1391 if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) {
1392 t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end);
1393 }
1395 t->redraw = TREDRAW_HARD;
1396 }
1397 break;
1398 case EVT_PADMINUS:
1399 if ((event->modifier & KM_ALT) && (t->flag & T_PROP_EDIT)) {
1400 t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f;
1402 t->redraw = TREDRAW_HARD;
1403 }
1404 break;
1405 case EVT_LEFTALTKEY:
1406 case EVT_RIGHTALTKEY:
1408 t->flag |= T_ALT_TRANSFORM;
1409 t->redraw |= TREDRAW_HARD;
1410 }
1411 break;
1412 default:
1413 break;
1414 }
1415 }
1416 else if (event->val == KM_RELEASE) {
1417 switch (event->type) {
1418 case EVT_LEFTALTKEY:
1419 case EVT_RIGHTALTKEY:
1420 /* TODO: Modal Map */
1422 t->flag &= ~T_ALT_TRANSFORM;
1423 t->redraw |= TREDRAW_HARD;
1424 }
1425 break;
1426 default: {
1427 break;
1428 }
1429 }
1430
1431 /* Confirm transform if launch key is released after mouse move. */
1432 if ((t->flag & T_RELEASE_CONFIRM) && event->type == t->launch_event) {
1433 t->state = TRANS_CONFIRM;
1434 }
1435 }
1436
1437 /* Per transform event, if present. */
1438 if (t->mode_info && t->mode_info->handle_event_fn) {
1439 t->redraw |= t->mode_info->handle_event_fn(t, event);
1440 }
1441
1442 /* Try to init modal numinput now, if possible. */
1443 if (!t->redraw && ((event->val == KM_PRESS) || (event->type == EVT_MODAL_MAP)) &&
1444 handleNumInput(t->context, &(t->num), event))
1445 {
1446 t->redraw |= TREDRAW_HARD;
1447 }
1448
1449 if (t->redraw && !ISMOUSE_MOTION(event->type)) {
1450 /* The status area is currently also tagged to update by the notifiers in
1451 * `viewRedrawForce`. However, this may change in the future, and tagging
1452 * the region twice doesn't add any overhead. */
1454
1456 ED_workspace_status_text(t->context, nullptr);
1457 }
1458 }
1459
1460 if (!is_navigating && t->redraw) {
1461 return wmOperatorStatus(0);
1462 }
1463 return OPERATOR_PASS_THROUGH;
1464}
1465
1466bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
1467{
1468 TransInfo *t = MEM_callocN<TransInfo>("TransInfo data");
1469 bool success;
1470
1471 t->context = C;
1472
1473 t->state = TRANS_RUNNING;
1474
1475 /* Avoid calculating proportional editing. */
1476 t->options = CTX_NO_PET;
1477
1478 t->mode = TFM_DUMMY;
1479
1480 initTransInfo(C, t, nullptr, nullptr);
1481
1482 /* Avoid doing connectivity lookups (when V3D_AROUND_LOCAL_ORIGINS is set). */
1484
1485 create_trans_data(C, t); /* Make TransData structs from selection. */
1486
1487 t->around = centerMode; /* Override user-defined mode. */
1488
1489 if (t->data_len_all == 0) {
1490 success = false;
1491 }
1492 else {
1493 success = true;
1494
1495 calculateCenter(t);
1496
1497 if (cent2d) {
1498 copy_v2_v2(cent2d, t->center2d);
1499 }
1500
1501 if (cent3d) {
1502 /* Copy center from constraint center. Transform center can be local. */
1503 copy_v3_v3(cent3d, t->center_global);
1504 }
1505 }
1506
1507 /* Does insert keyframes, and clears base flags; doesn't read `transdata`. */
1509
1510 postTrans(C, t);
1511
1512 MEM_freeN(t);
1513
1514 return success;
1515}
1516
1518{
1519 /* Don't show overlays when not the active view and when overlay is disabled: #57139 */
1520 bool ok = false;
1521 if (region == t->region) {
1522 ok = true;
1523 }
1524 else {
1525 if (t->spacetype == SPACE_VIEW3D) {
1526 View3D *v3d = static_cast<View3D *>(t->view);
1527 if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) {
1528 ok = true;
1529 }
1530 }
1531 }
1532 return ok;
1533}
1534
1535static void drawTransformView(const bContext * /*C*/, ARegion *region, void *arg)
1536{
1537 TransInfo *t = static_cast<TransInfo *>(arg);
1538
1539 if (!transinfo_show_overlay(t, region)) {
1540 return;
1541 }
1542
1543 GPU_line_width(1.0f);
1544
1545 drawConstraint(t);
1546
1547 switch (t->spacetype) {
1548 case SPACE_GRAPH:
1549 case SPACE_ACTION:
1550 /* Different visualization because the proportional editing in these editors only looks at
1551 * the x-axis. */
1552 drawPropRange(t);
1553 break;
1554
1555 default:
1556 drawPropCircle(t);
1557 break;
1558 }
1559
1560 drawSnapping(t);
1561
1562 if (region == t->region && t->mode_info && t->mode_info->draw_fn) {
1563 t->mode_info->draw_fn(t);
1564 }
1565}
1566
1571static void drawAutoKeyWarning(TransInfo *t, ARegion *region)
1572{
1573 const char *printable = IFACE_("Auto Keying On");
1574 float printable_size[2];
1575 int xco, yco;
1576 int offset = 0;
1577
1578 const rcti *rect = ED_region_visible_rect(region);
1579
1580 View3D *v3d = nullptr;
1581 Scene *scene = nullptr;
1582 if (t->spacetype == SPACE_VIEW3D) {
1583 v3d = static_cast<View3D *>(t->view);
1584 scene = t->scene;
1585 }
1586
1587 const int font_id = BLF_set_default();
1589 font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]);
1590
1591 /* Check to see if the Navigation Gizmo is enabled. */
1592 if ((t->spacetype != SPACE_VIEW3D) || (v3d == nullptr) ||
1594 {
1595 offset = 10;
1596 }
1597 else {
1598 /* Depending on user MINI_AXIS preference, pad accordingly. */
1599 switch ((eUserpref_MiniAxisType)U.mini_axis_type) {
1601 offset = U.gizmo_size_navigate_v3d;
1602 break;
1604 offset = U.rvisize * std::min((U.pixelsize / U.scale_factor), 1.0f) * 2.5f;
1605 break;
1607 offset = U.rvisize;
1608 break;
1609 }
1610 }
1611
1612 offset *= U.scale_factor;
1613
1614 xco = (rect->xmax - U.widget_unit) - int(printable_size[0]) - offset;
1615 yco = (rect->ymax - U.widget_unit);
1616
1617 /* Warning text (to clarify meaning of overlays)
1618 * - Original color was red to match the icon, but that clashes badly with a less nasty border.
1619 */
1620
1621 float text_color[4], shadow_color[4];
1622 if (v3d && scene) {
1623 ED_view3d_text_colors_get(scene, v3d, text_color, shadow_color);
1624 }
1625 else {
1626 UI_GetThemeColor4fv(TH_TEXT_HI, text_color);
1627 UI_GetThemeColor4fv(TH_BACK, text_color);
1628 }
1629 BLF_color4fv(BLF_default(), text_color);
1631 BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX);
1632
1633 /* Auto-key recording icon. */
1635
1636 xco -= U.widget_unit;
1637 yco -= int(printable_size[1]) / 2;
1638
1639 UI_icon_draw(xco, yco, ICON_REC);
1640
1642}
1643
1644static void drawTransformPixel(const bContext * /*C*/, ARegion *region, void *arg)
1645{
1646 TransInfo *t = static_cast<TransInfo *>(arg);
1647
1648 if (!transinfo_show_overlay(t, region)) {
1649 return;
1650 }
1651
1652 if (region == t->region) {
1653 Scene *scene = t->scene;
1654 ViewLayer *view_layer = t->view_layer;
1655 BKE_view_layer_synced_ensure(scene, view_layer);
1656 Object *ob = BKE_view_layer_active_object_get(view_layer);
1657
1658 /* Draw auto-key-framing hint in the corner
1659 * - only draw if enabled (advanced users may be distracted/annoyed),
1660 * for objects that will be auto-keyframed (no point otherwise),
1661 * AND only for the active region (as showing all is too overwhelming)
1662 */
1663 if ((U.keying_flag & AUTOKEY_FLAG_NOWARNING) == 0) {
1664 if (region == t->region) {
1665 if (t->options & (CTX_OBJECT | CTX_POSE_BONE)) {
1666 if (ob && animrig::autokeyframe_cfra_can_key(scene, &ob->id)) {
1667 drawAutoKeyWarning(t, region);
1668 }
1669 }
1670 }
1671 }
1672 }
1673}
1674
1676{
1678 PropertyRNA *prop;
1679
1680 bool use_prop_edit = false;
1681 int prop_edit_flag = 0;
1682
1683 /* Save proportional edit settings.
1684 * Skip saving proportional edit if it was not actually used.
1685 * Note that this value is being saved even if the operation is canceled. This is to maintain a
1686 * behavior already used by users. */
1687 if (!(t->options & CTX_NO_PET)) {
1688 if (t->flag & T_PROP_EDIT_ALL) {
1689 if (t->flag & T_PROP_EDIT) {
1690 use_prop_edit = true;
1691 }
1692 if (t->flag & T_PROP_CONNECTED) {
1693 prop_edit_flag |= PROP_EDIT_CONNECTED;
1694 }
1695 if (t->flag & T_PROP_PROJECTED) {
1696 prop_edit_flag |= PROP_EDIT_PROJECTED;
1697 }
1698 }
1699
1700 /* If modal, save settings back in scene if not set as operator argument. */
1701 if ((t->flag & T_MODAL) || (op->flag & OP_IS_REPEAT)) {
1702 /* Save settings if not set in operator. */
1703 if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit")) &&
1704 !RNA_property_is_set(op->ptr, prop))
1705 {
1708
1709 if (t->spacetype == SPACE_GRAPH) {
1710 ts->proportional_fcurve = use_prop_edit;
1711 }
1712 else if (t->spacetype == SPACE_ACTION) {
1713 ts->proportional_action = use_prop_edit;
1714 }
1715 else if (t->options & CTX_MASK) {
1716 ts->proportional_mask = use_prop_edit;
1717 }
1718 else if (obact && obact->mode == OB_MODE_OBJECT) {
1719 ts->proportional_objects = use_prop_edit;
1720 }
1721 else {
1722 if (use_prop_edit) {
1724 }
1725 else {
1727 }
1728 }
1729 }
1730
1731 if ((prop = RNA_struct_find_property(op->ptr, "proportional_size"))) {
1732 ts->proportional_size = RNA_property_is_set(op->ptr, prop) ?
1733 RNA_property_float_get(op->ptr, prop) :
1734 t->prop_size;
1735 }
1736
1737 if ((prop = RNA_struct_find_property(op->ptr, "proportional_edit_falloff")) &&
1738 !RNA_property_is_set(op->ptr, prop))
1739 {
1740 ts->prop_mode = t->prop_mode;
1741 }
1742 }
1743 }
1744
1745 if (t->state == TRANS_CANCEL) {
1746 /* No need to edit operator properties or tool settings if we are canceling the operation.
1747 * These properties must match the original ones. */
1748 return;
1749 }
1750
1751 if (!(t->options & CTX_NO_PET)) {
1752 if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit"))) {
1753 RNA_property_boolean_set(op->ptr, prop, use_prop_edit);
1754 RNA_boolean_set(op->ptr, "use_proportional_connected", prop_edit_flag & PROP_EDIT_CONNECTED);
1755 RNA_boolean_set(op->ptr, "use_proportional_projected", prop_edit_flag & PROP_EDIT_PROJECTED);
1756 RNA_enum_set(op->ptr, "proportional_edit_falloff", t->prop_mode);
1757 RNA_float_set(op->ptr, "proportional_size", t->prop_size);
1758 }
1759 }
1760
1761 /* Save back mode in case we're in the generic operator. */
1762 if ((prop = RNA_struct_find_property(op->ptr, "mode"))) {
1763 RNA_property_enum_set(op->ptr, prop, t->mode);
1764 }
1765
1766 if ((prop = RNA_struct_find_property(op->ptr, "value"))) {
1767 if (RNA_property_array_check(prop)) {
1769 }
1770 else {
1771 RNA_property_float_set(op->ptr, prop, t->values_final[0]);
1772 }
1773 }
1774
1775 /* Save snapping settings. */
1776 if ((prop = RNA_struct_find_property(op->ptr, "snap"))) {
1777 bool is_snap_enabled = (t->modifiers & MOD_SNAP) != 0;
1778
1779 /* Update the snap toggle in `ToolSettings`. */
1780 if (
1781 /* Update only if snapping has changed during a modal operation. */
1782 (t->flag & T_MODAL) &&
1783 /* Skip updating if the snapping mode does not match the snap types. */
1785 /* Skip updating the snap toggle if it was not explicitly set by the user. */
1786 !(t->modifiers & MOD_SNAP_FORCED) &&
1787 /* Skip updating the snap toggle if snapping was enabled via operator properties. */
1788 !RNA_property_is_set(op->ptr, prop))
1789 {
1790 /* Type is #eSnapFlag, but type must match various snap attributes in #ToolSettings. */
1791 short *snap_flag_ptr;
1792
1793 wmMsgParams_RNA msg_key_params = {{}};
1794 msg_key_params.ptr = RNA_pointer_create_discrete(&t->scene->id, &RNA_ToolSettings, ts);
1795 if ((snap_flag_ptr = transform_snap_flag_from_spacetype_ptr(t, &msg_key_params.prop)) &&
1796 (is_snap_enabled != bool(*snap_flag_ptr & SCE_SNAP)))
1797 {
1798 SET_FLAG_FROM_TEST(*snap_flag_ptr, is_snap_enabled, SCE_SNAP);
1799 WM_msg_publish_rna_params(t->mbus, &msg_key_params);
1800 }
1801 }
1802
1803 RNA_property_boolean_set(op->ptr, prop, is_snap_enabled);
1804
1805 if ((prop = RNA_struct_find_property(op->ptr, "snap_elements"))) {
1806 RNA_property_enum_set(op->ptr, prop, t->tsnap.mode);
1808 op->ptr, "use_snap_project", (t->tsnap.mode & SCE_SNAP_INDIVIDUAL_PROJECT) != 0);
1809 RNA_enum_set(op->ptr, "snap_target", t->tsnap.source_operation);
1810
1812 RNA_boolean_set(op->ptr, "use_snap_self", (target & SCE_SNAP_TARGET_NOT_ACTIVE) == 0);
1813 RNA_boolean_set(op->ptr, "use_snap_edit", (target & SCE_SNAP_TARGET_NOT_EDITED) == 0);
1814 RNA_boolean_set(op->ptr, "use_snap_nonedit", (target & SCE_SNAP_TARGET_NOT_NONEDITED) == 0);
1816 op->ptr, "use_snap_selectable", (target & SCE_SNAP_TARGET_ONLY_SELECTABLE) != 0);
1817 }
1818 }
1819
1820 if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) {
1821 RNA_property_boolean_set(op->ptr, prop, (t->flag & T_NO_MIRROR) == 0);
1822 }
1823
1824 if ((prop = RNA_struct_find_property(op->ptr, "orient_axis"))) {
1825 if (t->flag & T_MODAL) {
1826 if (t->con.mode & CON_APPLY) {
1827 int orient_axis = constraintModeToIndex(t);
1828 if (orient_axis != -1) {
1829 RNA_property_enum_set(op->ptr, prop, orient_axis);
1830 }
1831 }
1832 else {
1833 RNA_property_enum_set(op->ptr, prop, t->orient_axis);
1834 }
1835 }
1836 }
1837 if ((prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) {
1838 if (t->flag & T_MODAL) {
1840 }
1841 }
1842
1843 if ((prop = RNA_struct_find_property(op->ptr, "orient_type"))) {
1844 short orient_type_set, orient_type_curr;
1845 orient_type_set = RNA_property_is_set(op->ptr, prop) ? RNA_property_enum_get(op->ptr, prop) :
1846 -1;
1847 orient_type_curr = t->orient[t->orient_curr].type;
1848
1849 if (!ELEM(orient_type_curr, orient_type_set, V3D_ORIENT_CUSTOM_MATRIX)) {
1850 RNA_property_enum_set(op->ptr, prop, orient_type_curr);
1851 orient_type_set = orient_type_curr;
1852 }
1853
1854 if ((prop = RNA_struct_find_property(op->ptr, "orient_matrix_type")) &&
1855 !RNA_property_is_set(op->ptr, prop))
1856 {
1857 /* Set the first time to register on redo. */
1858 RNA_property_enum_set(op->ptr, prop, orient_type_set);
1859 RNA_float_set_array(op->ptr, "orient_matrix", &t->spacemtx[0][0]);
1860 }
1861 }
1862
1863 if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) {
1864 bool constraint_axis[3] = {false, false, false};
1865 if (t->con.mode & CON_APPLY) {
1866 if (t->con.mode & CON_AXIS0) {
1867 constraint_axis[0] = true;
1868 }
1869 if (t->con.mode & CON_AXIS1) {
1870 constraint_axis[1] = true;
1871 }
1872 if (t->con.mode & CON_AXIS2) {
1873 constraint_axis[2] = true;
1874 }
1875 RNA_property_boolean_set_array(op->ptr, prop, constraint_axis);
1876 }
1877 else {
1878 RNA_property_unset(op->ptr, prop);
1879 }
1880 }
1881
1882 {
1883 const char *prop_id = nullptr;
1884 bool prop_state = true;
1885 if (t->mode == TFM_SHRINKFATTEN) {
1886 prop_id = "use_even_offset";
1887 prop_state = false;
1888 }
1889
1890 if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) {
1891 RNA_property_boolean_set(op->ptr, prop, ((t->flag & T_ALT_TRANSFORM) == 0) == prop_state);
1892 }
1893 }
1894
1895 if ((prop = RNA_struct_find_property(op->ptr, "correct_uv"))) {
1897 op->ptr, prop, (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE) != 0);
1898 }
1899}
1900
1901bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
1902{
1903 int options = 0;
1904 PropertyRNA *prop;
1905
1906 mode = transform_mode_really_used(C, eTfmMode(mode));
1907
1908 t->context = C;
1909
1910 /* Added initialize, for external calls to set stuff in TransInfo, like undo string. */
1911
1912 t->state = TRANS_STARTING;
1913
1914 if ((prop = RNA_struct_find_property(op->ptr, "cursor_transform")) &&
1915 RNA_property_is_set(op->ptr, prop))
1916 {
1917 if (RNA_property_boolean_get(op->ptr, prop)) {
1919 }
1920 }
1921
1922 if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) &&
1923 RNA_property_is_set(op->ptr, prop))
1924 {
1925 if (RNA_property_boolean_get(op->ptr, prop)) {
1927 }
1928 }
1929
1930 if ((prop = RNA_struct_find_property(op->ptr, "gpencil_strokes")) &&
1931 RNA_property_is_set(op->ptr, prop))
1932 {
1933 if (RNA_property_boolean_get(op->ptr, prop)) {
1935 }
1936 }
1937
1938 if ((prop = RNA_struct_find_property(op->ptr, "view2d_edge_pan")) &&
1939 RNA_property_is_set(op->ptr, prop))
1940 {
1941 if (RNA_property_boolean_get(op->ptr, prop)) {
1943 }
1944 }
1945
1947
1948 t->mode = eTfmMode(mode);
1949
1950 /* Needed to translate tweak events to mouse buttons. */
1952 t->is_launch_event_drag = event ? (event->val == KM_CLICK_DRAG) : false;
1953
1954 unit_m3(t->spacemtx);
1955
1956 initTransInfo(C, t, op, event);
1957
1958 if (!G.background) {
1959 if (t->spacetype == SPACE_VIEW3D) {
1966 }
1967 else if (ELEM(t->spacetype,
1969 SPACE_CLIP,
1970 SPACE_NODE,
1973 SPACE_SEQ))
1974 {
1979 }
1980 }
1981
1982 create_trans_data(C, t); /* Make #TransData structs from selection. */
1983
1984 if (t->data_len_all == 0) {
1985 postTrans(C, t);
1986 return false;
1987 }
1988
1989 /* When proportional editing is enabled, data_len_all can be non zero when
1990 * nothing is selected, if this is the case we can end the transform early.
1991 *
1992 * By definition transform-data has selected items in beginning,
1993 * so only the first item in each container needs to be checked
1994 * when looking for the presence of selected data. */
1995 if (t->flag & T_PROP_EDIT) {
1996 bool has_selected_any = false;
1998 if (tc->data_len == 0) {
1999 continue;
2000 }
2001
2002 if (!tc->sorted_index_map) {
2003 BLI_assert_msg(tc->data[0].flag & TD_SELECTED,
2004 "Without sorted_index_map, all items are expected to be selected");
2005 has_selected_any = true;
2006 break;
2007 }
2008
2009 const int first_selected_index = tc->sorted_index_map[0];
2010 TransData *td = &tc->data[first_selected_index];
2011 if (td->flag & TD_SELECTED) {
2012 has_selected_any = true;
2013 break;
2014 }
2015 }
2016
2017 if (!has_selected_any) {
2018 postTrans(C, t);
2019 return false;
2020 }
2021 }
2022
2023 if (event) {
2024 /* Keymap for shortcut header prints. */
2026
2027 /* Stupid code to have Ctrl-Click on gizmo work ok.
2028 *
2029 * Do this only for translation/rotation/resize because only these
2030 * modes are available from gizmo and doing such check could
2031 * lead to keymap conflicts for other modes (see #31584)
2032 */
2034 LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &t->keymap->items) {
2035 if (kmi->flag & KMI_INACTIVE) {
2036 continue;
2037 }
2038
2039 if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) {
2040 if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) &&
2041 (event->modifier & KM_CTRL)) ||
2042 (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) &&
2043 (event->modifier & KM_SHIFT)) ||
2044 (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) ||
2045 ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY)))
2046 {
2048 }
2049 break;
2050 }
2051 }
2052 }
2053 if (t->data_type == &TransConvertType_Node) {
2054 /* Set the initial auto-attach flag based on whether the chosen keymap key is pressed at the
2055 * start of the operator. */
2057 LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &t->keymap->items) {
2058 if (kmi->flag & KMI_INACTIVE) {
2059 continue;
2060 }
2061
2062 if (kmi->propvalue == TFM_MODAL_NODE_ATTACH_OFF && kmi->val == KM_PRESS) {
2063 if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) &&
2064 (event->modifier & KM_CTRL)) ||
2065 (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) &&
2066 (event->modifier & KM_SHIFT)) ||
2067 (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) ||
2068 ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY)) ||
2069 ((kmi->type == EVT_HYPER) && (event->modifier & KM_HYPER)))
2070 {
2072 }
2073 break;
2074 }
2075 }
2076 }
2077 }
2078
2079 initSnapping(t, op); /* Initialize snapping data AFTER mode flags. */
2080
2081 /* EVIL! pose-mode code can switch translation to rotate when 1 bone is selected.
2082 * will be removed (ton). */
2083
2084 /* EVIL2: we gave as argument also texture space context bit... was cleared. */
2085
2086 /* EVIL3: extend mode for animation editors also switches modes...
2087 * but is best way to avoid duplicate code. */
2088 mode = t->mode;
2089
2091 calculateCenter(t);
2092
2093 if (event) {
2094 /* Initialize accurate transform to settings requested by keymap. */
2095 bool use_accurate = false;
2096 if ((prop = RNA_struct_find_property(op->ptr, "use_accurate")) &&
2097 RNA_property_is_set(op->ptr, prop))
2098 {
2099 if (RNA_property_boolean_get(op->ptr, prop)) {
2100 use_accurate = true;
2101 }
2102 }
2103
2104 initMouseInput(t, &t->mouse, t->center2d, t->mval, use_accurate);
2105 }
2106
2107 transform_mode_init(t, op, mode);
2108
2109 if (t->state == TRANS_CANCEL) {
2110 postTrans(C, t);
2111 return false;
2112 }
2113
2114 /* Transformation axis from operator. */
2115 if ((prop = RNA_struct_find_property(op->ptr, "orient_axis")) &&
2116 RNA_property_is_set(op->ptr, prop))
2117 {
2118 t->orient_axis = RNA_property_enum_get(op->ptr, prop);
2119 }
2120 if ((prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho")) &&
2121 RNA_property_is_set(op->ptr, prop))
2122 {
2124 }
2125
2126 /* Constraint init from operator. */
2127 if (t->con.mode & CON_APPLY) {
2128 setUserConstraint(t, t->con.mode, "%s");
2129 }
2130
2131 /* Don't write into the values when non-modal because they are already set from operator redo
2132 * values. */
2133 if (t->flag & T_MODAL) {
2134 /* Setup the mouse input with initial values. */
2135 applyMouseInput(t, &t->mouse, t->mouse.imval, t->values);
2136 }
2137
2138 if ((prop = RNA_struct_find_property(op->ptr, "preserve_clnor"))) {
2139 if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
2140
2142 BMEditMesh *em = nullptr; /* BKE_editmesh_from_object(t->obedit); */
2143 bool do_skip = false;
2144
2145 /* Currently only used for two of three most frequent transform ops,
2146 * can include more ops.
2147 * Note that scaling cannot be included here,
2148 * non-uniform scaling will affect normals. */
2150 if (em->bm->totvertsel == em->bm->totvert) {
2151 /* No need to invalidate if whole mesh is selected. */
2152 do_skip = true;
2153 }
2154 }
2155
2156 if (t->flag & T_MODAL) {
2157 RNA_property_boolean_set(op->ptr, prop, false);
2158 }
2159 else if (!do_skip) {
2160 const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop);
2161 if (preserve_clnor) {
2163 t->flag |= T_CLNOR_REBUILD;
2164 }
2165 BM_lnorspace_invalidate(em->bm, true);
2166 }
2167 }
2168 }
2169 }
2170
2171 t->context = nullptr;
2172
2173 return true;
2174}
2175
2177{
2178 t->context = C;
2179
2180 if (t->redraw == TREDRAW_HARD) {
2182 if (t->mode_info) {
2183 t->mode_info->transform_fn(t); /* Calls #recalc_data(). */
2184 }
2185 }
2186
2187 if (t->redraw & TREDRAW_SOFT) {
2188 viewRedrawForce(C, t);
2189 }
2190
2192
2193 /* If auto confirm is on, break after one pass. */
2194 if (t->options & CTX_AUTOCONFIRM) {
2195 t->state = TRANS_CONFIRM;
2196 }
2197
2198 t->context = nullptr;
2199}
2200
2202{
2204
2205 t->context = C;
2206
2208 /* Handle restoring objects. */
2209 if (t->state == TRANS_CANCEL) {
2210 exit_code = OPERATOR_CANCELLED;
2211 restoreTransObjects(t); /* Calls #recalc_data(). */
2212 }
2213 else {
2214 if (t->flag & T_CLNOR_REBUILD) {
2216 BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
2217 BM_lnorspace_rebuild(em->bm, true);
2218 }
2219 }
2220 exit_code = OPERATOR_FINISHED;
2221 }
2222
2223 /* Does insert keyframes, and clears base flags; doesn't read `transdata`. */
2225
2226 /* Free data, also handles overlap [in freeTransCustomData()]. */
2227 postTrans(C, t);
2228
2229 /* Send events out for redraws. */
2230 viewRedrawPost(C, t);
2231
2232 viewRedrawForce(C, t);
2233
2235 }
2236
2237 t->context = nullptr;
2238
2239 return exit_code;
2240}
2241
2243{
2244 /* Currently only checks for editmode. */
2245 if (t->flag & T_EDIT) {
2246 if ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
2248 {
2249 /* Not all editmode supports axis-matrix. */
2250 return true;
2251 }
2252 }
2253
2254 return false;
2255}
2256
2257bool transform_apply_matrix(TransInfo *t, float mat[4][4])
2258{
2259 if (t->mode_info && t->mode_info->transform_matrix_fn) {
2260 t->mode_info->transform_matrix_fn(t, mat);
2261 return true;
2262 }
2263 return false;
2264}
2265
2266void transform_final_value_get(const TransInfo *t, float *value, const int value_num)
2267{
2268 memcpy(value, t->values_final, sizeof(float) * value_num);
2269}
2270
2271} // namespace blender::ed::transform
Functions to insert, delete or modify keyframes.
WorkSpace * CTX_wm_workspace(const bContext *C)
Mask * CTX_data_edit_mask(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_editmesh_lnorspace_update(BMEditMesh *em)
Definition editmesh.cc:220
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Object * BKE_view_layer_edit_object_get(const ViewLayer *view_layer)
void BKE_mask_coord_to_image(struct Image *image, struct ImageUser *iuser, float r_co[2], const float co[2])
void BKE_mask_coord_to_movieclip(struct MovieClip *clip, struct MovieClipUser *user, float r_co[2], const float co[2])
void BKE_workspace_status_clear(WorkSpace *workspace)
Definition workspace.cc:648
int BLF_set_default()
void BLF_shadow(int fontid, FontShadowType type, const float rgba[4]=nullptr)
Definition blf.cc:928
void BLF_width_and_height(int fontid, const char *str, size_t str_len, float *r_width, float *r_height) ATTR_NONNULL()
Definition blf.cc:792
void BLF_color4fv(int fontid, const float rgba[4])
Definition blf.cc:502
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:468
int BLF_default()
void BLF_draw_default(float x, float y, float z, const char *str, size_t str_len) ATTR_NONNULL()
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
void unit_m3(float m[3][3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void unit_m4(float m[4][4])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v3(float n[3])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:189
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:185
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
#define ARRAY_SIZE(arr)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define UNLIKELY(x)
#define ELEM(...)
#define IFACE_(msgid)
@ SACTION_DRAWTIME
@ OB_MODE_OBJECT
@ OB_MBALL
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OB_MESH
@ OB_CURVES_LEGACY
@ OB_CURVES
@ UVCALC_TRANSFORM_CORRECT_SLIDE
@ UVCALC_TRANSFORM_CORRECT
@ PROP_MODE_MAX
@ SCE_SNAP
@ PROP_EDIT_PROJECTED
@ PROP_EDIT_USE
@ PROP_EDIT_CONNECTED
eSnapTargetOP
@ SCE_SNAP_TARGET_NOT_ACTIVE
@ SCE_SNAP_TARGET_NOT_NONEDITED
@ SCE_SNAP_TARGET_ONLY_SELECTABLE
@ SCE_SNAP_TARGET_NOT_EDITED
@ SCE_SNAP_INDIVIDUAL_PROJECT
@ SCE_SNAP_TO_GRID
@ RGN_TYPE_WINDOW
#define RGN_TYPE_ANY
@ SI_COORDFLOATS
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_NODE
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ SNODE_INSERTOFS_DIR_RIGHT
@ SNODE_INSERTOFS_DIR_LEFT
#define SPACE_TYPE_ANY
@ AUTOKEY_FLAG_NOWARNING
eUserpref_MiniAxisType
@ USER_MINI_AXIS_TYPE_GIZMO
@ USER_MINI_AXIS_TYPE_MINIMAL
@ USER_MINI_AXIS_TYPE_NONE
@ V3D_GIZMO_HIDE
@ V3D_GIZMO_HIDE_NAVIGATE
@ V3D_HIDE_OVERLAYS
@ V3D_ORIENT_NORMAL
@ V3D_ORIENT_CUSTOM_MATRIX
@ RV3D_CAMOB
@ RV3D_ORTHO
@ V3D_AROUND_CENTER_BOUNDS
@ V3D_AROUND_LOCAL_ORIGINS
@ RV3D_NAVIGATING
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
MovieClip * ED_space_clip_get_clip(const SpaceClip *sc)
bool ED_space_clip_check_show_trackedit(const SpaceClip *sc)
bool ED_space_clip_check_show_maskedit(const SpaceClip *sc)
void ED_space_clip_get_aspect(const SpaceClip *sc, float *r_aspx, float *r_aspy)
void ED_clip_point_stable_pos__reverse(const SpaceClip *sc, const ARegion *region, const float co[2], float r_co[2])
the reverse of ED_clip_point_stable_pos(), gets the marker region coords. better name here?...
void ED_space_clip_get_aspect_dimension_aware(const SpaceClip *sc, float *r_aspx, float *r_aspy)
void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
void ED_space_image_get_uv_aspect(SpaceImage *sima, float *r_aspx, float *r_aspy)
void ED_space_image_get_aspect(SpaceImage *sima, float *r_aspx, float *r_aspy)
void ED_image_point_pos__reverse(SpaceImage *sima, const ARegion *region, const float co[2], float r_co[2])
#define NUM_MODAL_INCREMENT_DOWN
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Definition numinput.cc:311
#define NUM_MODAL_INCREMENT_UP
bool hasNumInput(const NumInput *n)
Definition numinput.cc:170
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:714
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:872
const rcti * ED_region_visible_rect(ARegion *region)
Definition area.cc:4118
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
#define REGION_DRAW_POST_PIXEL
bool ED_uvedit_live_unwrap_timer_check(const wmTimer *timer)
eV3DProjTest
Definition ED_view3d.hh:278
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:279
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
void ED_view3d_win_to_delta(const ARegion *region, const float xy_delta[2], float zfac, float r_out[3])
void ED_view3d_calc_camera_border(const Scene *scene, const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const RegionView3D *rv3d, bool no_shift, rctf *r_viewborder)
void ED_view3d_text_colors_get(const Scene *scene, const View3D *v3d, float r_text_color[4], float r_shadow_color[4])
eV3DProjStatus ED_view3d_project_int_global(const ARegion *region, const float co[3], int r_co[2], eV3DProjTest flag)
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:166
#define C
Definition RandGen.cpp:29
void UI_icon_draw(float x, float y, int icon_id)
@ TH_BACK
@ TH_TEXT_HI
void UI_GetThemeColor4fv(int colorid, float col[4])
void UI_view2d_view_to_region(const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL()
Definition view2d.cc:1722
#define ND_SEQUENCER
Definition WM_types.hh:434
#define NC_GEOM
Definition WM_types.hh:390
@ KM_CTRL
Definition WM_types.hh:276
@ KM_ALT
Definition WM_types.hh:277
@ KM_HYPER
Definition WM_types.hh:289
@ KM_OSKEY
Definition WM_types.hh:279
@ KM_SHIFT
Definition WM_types.hh:275
@ WM_EVENT_IS_REPEAT
Definition WM_types.hh:681
#define ND_DATA
Definition WM_types.hh:506
#define NC_ANIMATION
Definition WM_types.hh:385
@ KM_PRESS
Definition WM_types.hh:308
@ KM_CLICK_DRAG
Definition WM_types.hh:316
@ KM_RELEASE
Definition WM_types.hh:309
#define NC_MOVIECLIP
Definition WM_types.hh:394
#define NC_SCENE
Definition WM_types.hh:375
#define ND_SPACE_NODE_VIEW
Definition WM_types.hh:533
#define ND_POSE
Definition WM_types.hh:455
#define NA_EDITED
Definition WM_types.hh:581
#define ND_NLA
Definition WM_types.hh:494
#define ND_TRANSFORM
Definition WM_types.hh:453
#define NC_MASK
Definition WM_types.hh:395
#define ND_KEYS
Definition WM_types.hh:460
#define ND_KEYFRAME
Definition WM_types.hh:491
#define NC_OBJECT
Definition WM_types.hh:376
#define NC_SPACE
Definition WM_types.hh:389
#define U
void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
ATTR_WARN_UNUSED_RESULT const BMVert * v
CCL_NAMESPACE_BEGIN struct Options options
#define out
#define printf(...)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define G(x, y, z)
bool is_autokey_on(const Scene *scene)
bool autokeyframe_cfra_can_key(const Scene *scene, ID *id)
static void viewRedrawPost(bContext *C, TransInfo *t)
void calculateCenter(TransInfo *t)
void transformApply(bContext *C, TransInfo *t)
TransConvertTypeInfo TransConvertType_Mesh
void setTransformViewMatrices(TransInfo *t)
void resetTransRestrictions(TransInfo *t)
wmKeyMap * transform_modal_keymap(wmKeyConfig *keyconf)
void special_aftertrans_update(bContext *C, TransInfo *t)
void postSelectConstraint(TransInfo *t)
void transform_snap_flag_from_modifiers_set(TransInfo *t)
TransConvertTypeInfo TransConvertType_Node
bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], float cent2d[2])
bool transform_mode_is_changeable(const int mode)
void setUserConstraint(TransInfo *t, int mode, const char text_[])
static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy)
void removeSnapPoint(TransInfo *t)
bool transformModeUseSnap(const TransInfo *t)
void initSnapping(TransInfo *t, wmOperator *op)
bool validSnap(const TransInfo *t)
void setLocalConstraint(TransInfo *t, int mode, const char text[])
static bool transform_modal_item_poll(const wmOperator *op, int value)
void projectFloatView(TransInfo *t, const float vec[3], float adr[2])
static void drawTransformPixel(const bContext *, ARegion *region, void *arg)
void initMouseInput(TransInfo *t, MouseInput *mi, const float2 &center, const float2 &mval, bool precision)
void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy)
void restoreTransObjects(TransInfo *t)
eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event)
void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
void resetTransModal(TransInfo *t)
eTfmMode transform_mode_really_used(bContext *C, eTfmMode mode)
void transform_view_vector_calc(const TransInfo *t, const float focus[3], float r_vec[3])
void transform_draw_cursor_draw(bContext *C, const blender::int2 &xy, const blender::float2 &, void *customdata)
static bool transinfo_show_overlay(TransInfo *t, ARegion *region)
void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV3DProjTest flag)
static void drawAutoKeyWarning(TransInfo *t, ARegion *region)
static void viewRedrawForce(const bContext *C, TransInfo *t)
void addSnapPoint(TransInfo *t)
void applyMouseInput(TransInfo *t, MouseInput *mi, const float2 &mval, float output[3])
void transform_autoik_update(TransInfo *t, short mode)
void transform_gizmo_3d_model_from_constraint_and_mode_set(TransInfo *t)
static void drawTransformView(const bContext *, ARegion *region, void *arg)
void projectIntView(TransInfo *t, const float vec[3], int adr[2])
void transform_mode_snap_source_init(TransInfo *t, wmOperator *op)
void setTransformViewAspect(TransInfo *t, float r_aspect[3])
void projectFloatViewCenterFallback(TransInfo *t, float adr[2])
void drawSnapping(TransInfo *t)
wmOperatorStatus transformEvent(TransInfo *t, wmOperator *op, const wmEvent *event)
void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event)
void sort_trans_data_dist(TransInfo *t)
bool transform_draw_cursor_poll(bContext *C)
static bool transform_event_modal_constraint(TransInfo *t, short modal_type)
void removeAspectRatio(TransInfo *t, float vec[2])
void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag)
void initSelectConstraint(TransInfo *t)
short * transform_snap_flag_from_spacetype_ptr(TransInfo *t, const PropertyRNA **r_prop=nullptr)
void selectConstraint(TransInfo *t)
void transform_final_value_get(const TransInfo *t, float *value, const int value_num)
wmOperatorStatus transformEnd(bContext *C, TransInfo *t)
void calculatePropRatio(TransInfo *t)
bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
void postTrans(bContext *C, TransInfo *t)
TransConvertTypeInfo TransConvertType_MeshUV
TransConvertTypeInfo TransConvertType_Tracking
void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
bool transform_apply_matrix(TransInfo *t, float mat[4][4])
void applyAspectRatio(TransInfo *t, float vec[2])
void drawConstraint(TransInfo *t)
int constraintModeToIndex(const TransInfo *t)
void drawPropCircle(TransInfo *t)
void transform_gizmo_3d_model_from_constraint_and_mode_restore(TransInfo *t)
void create_trans_data(bContext *C, TransInfo *t)
bool transdata_check_local_islands(TransInfo *t, short around)
void transform_orientations_current_set(TransInfo *t, const short orient_index)
float2 image_preview_unit_to_px(const Scene *scene, const float2 co_src)
VecBase< float, 2 > float2
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_property_array_check(PropertyRNA *prop)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
void RNA_property_float_set_array(PointerRNA *ptr, PropertyRNA *prop, const float *values)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
void RNA_property_unset(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_boolean_set_array(PointerRNA *ptr, PropertyRNA *prop, const bool *values)
void * regiondata
ARegionRuntimeHandle * runtime
int totvert
int totvertsel
void * first
float persmat[4][4]
float persinv[4][4]
float viewmat[4][4]
float viewinv[4][4]
ListBase spacedata
struct MovieClipUser user
struct MovieClip * clip
struct ImageUser iuser
struct Image * image
struct Object * camera
TransConvertTypeInfo * data_type
Definition transform.hh:805
struct blender::ed::transform::TransInfo::@040124034302070131153200326237043302276016250327 orient[3]
enum eRedrawFlag(* handle_event_fn)(TransInfo *, const wmEvent *)
void(* transform_matrix_fn)(TransInfo *, float[4][4])
int ymax
int xmax
wmEventType prev_type
Definition WM_types.hh:809
wmEventModifierFlag modifier
Definition WM_types.hh:771
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int mval[2]
Definition WM_types.hh:760
eWM_EventFlag flag
Definition WM_types.hh:785
short prev_val
Definition WM_types.hh:811
void * customdata
Definition WM_types.hh:804
bool(* poll_modal_item)(const struct wmOperator *op, int value)
const PropertyRNA * prop
wmKeyMap * modalkeymap
Definition WM_types.hh:1142
struct wmOperatorType * type
struct PointerRNA * ptr
#define T_PROP_SIZE_MIN
Definition transform.hh:31
#define T_PROP_EDIT_ALL
Definition transform.hh:28
#define T_PROP_SIZE_MAX
Definition transform.hh:32
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:42
conversion and adaptation of different datablocks to a common struct.
transform modes used by different operators.
void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *)
Definition wm_draw.cc:1573
int WM_userdef_event_type_from_keymap_type(int kmitype)
void WM_window_status_area_tag_redraw(wmWindow *win)
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
#define ISMOUSE_MOTION(event_type)
@ EVT_OKEY
@ TIMER
@ EVT_MODAL_MAP
@ EVT_RIGHTCTRLKEY
@ EVT_CKEY
@ EVT_OSKEY
@ EVT_LEFTCTRLKEY
@ MOUSEMOVE
@ EVT_RIGHTALTKEY
@ EVT_PADMINUS
@ EVT_LEFTALTKEY
@ EVT_RIGHTSHIFTKEY
@ EVT_LEFTSHIFTKEY
@ EVT_PADPLUSKEY
@ EVT_HYPER
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:929
wmKeyMap * WM_keymap_active(const wmWindowManager *wm, wmKeyMap *keymap)
void WM_msg_publish_rna_params(wmMsgBus *mbus, const wmMsgParams_RNA *msg_key_params)
wmPaintCursor * WM_paint_cursor_activate(short space_type, short region_type, bool(*poll)(bContext *C), wmPaintCursorDraw draw, void *customdata)
uint8_t flag
Definition wm_window.cc:139