Blender V5.0
transform_snap.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_bounds.hh"
10#include "BLI_listbase.h"
11#include "BLI_math_matrix.h"
12#include "BLI_math_rotation.h"
13#include "BLI_math_vector.h"
14#include "BLI_time.h"
15
16#include "DNA_userdef_types.h"
17
18#include "GPU_immediate.hh"
19#include "GPU_matrix.hh"
20#include "GPU_state.hh"
21
22#include "BKE_editmesh.hh"
23#include "BKE_layer.hh"
24#include "BKE_object.hh"
25#include "BKE_scene.hh"
26
27#include "RNA_access.hh"
28#include "RNA_prototypes.hh"
29
30#include "WM_api.hh"
31
32#include "ED_image.hh"
33#include "ED_node.hh"
35#include "ED_uvedit.hh"
36
37#include "UI_resources.hh"
38#include "UI_view2d.hh"
39
40#include "SEQ_sequencer.hh"
41
42#include "transform.hh"
44#include "transform_convert.hh"
45#include "transform_mode.hh"
46#include "transform_snap.hh"
47
48namespace blender::ed::transform {
49
50/* Use half of flt-max so we can scale up without an exception. */
51
52/* -------------------------------------------------------------------- */
55
56static void setSnappingCallback(TransInfo *t);
57
58static void snap_target_view3d_fn(TransInfo *t, float *vec);
59static void snap_target_uv_fn(TransInfo *t, float *vec);
60static void snap_target_sequencer_fn(TransInfo *t, float *vec);
61static void snap_target_nla_fn(TransInfo *t, float *vec);
62
63static void snap_source_median_fn(TransInfo *t);
64static void snap_source_center_fn(TransInfo *t);
65static void snap_source_closest_fn(TransInfo *t);
66static void snap_source_active_fn(TransInfo *t);
67
69 TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]);
70
72
73/* -------------------------------------------------------------------- */
76
77#if 0
78int BIF_snappingSupported(Object *obedit)
79{
80 int status = 0;
81
82 /* Only support object mesh, armature, curves. */
83 if (obedit == nullptr ||
85 {
86 status = 1;
87 }
88
89 return status;
90}
91#endif
92
94{
96 View3D *v3d = static_cast<View3D *>(t->view);
98 return true;
99 }
100 if (v3d->shading.type == OB_RENDER &&
103 {
104 return true;
105 }
107 return true;
108 }
109 return false;
110}
111
119
121{
123 /* Those space-types define their own invert behavior instead of toggling it on/off. */
124 return;
125 }
126 if (t->spacetype == SPACE_GRAPH) {
127 /* This is to stay consistent with the behavior from 3.6. */
128 if (t->modifiers & MOD_SNAP_INVERT) {
130 }
131 else {
133 }
134 /* In 3.6 when snapping was disabled, pressing the invert button would turn on snapping.
135 * But it wouldn't turn it off when it was enabled. */
136 if ((t->modifiers & MOD_SNAP) || (t->modifiers & MOD_SNAP_INVERT)) {
137 t->tsnap.flag |= SCE_SNAP;
138 }
139 else {
140 t->tsnap.flag &= ~SCE_SNAP;
141 }
142 return;
143 }
145 (((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP) ||
147 SCE_SNAP);
148}
149
151{
152 return (t->tsnap.flag & SCE_SNAP) != 0;
153}
154
156{
157 /* The VSE and animation editors should not depend on the snapping options of the 3D viewport. */
159 return true;
160 }
161 ToolSettings *ts = t->settings;
162 if (t->mode == TFM_TRANSLATION) {
164 }
165 if (t->mode == TFM_ROTATION) {
167 }
168 if (t->mode == TFM_RESIZE) {
170 }
171 if (ELEM(t->mode,
177 {
178 return true;
179 }
180
181 return false;
182}
183
184static bool doForceIncrementSnap(const TransInfo *t)
185{
187 /* These spaces don't support increment snapping. */
188 return false;
189 }
190
191 if (t->spacetype == SPACE_SEQ && ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
192 return true;
193 }
194
195 if (t->modifiers & MOD_SNAP_FORCED) {
196 return false;
197 }
198
199 return !transformModeUseSnap(t);
200}
201
203{
204 uchar col[4], selectedCol[4], activeCol[4];
206 return;
207 }
208
209 const bool draw_source = (t->flag & T_DRAW_SNAP_SOURCE) &&
211 const bool draw_target = (t->tsnap.status & (SNAP_TARGET_FOUND | SNAP_MULTI_POINTS));
212
213 if (!(draw_source || draw_target)) {
214 return;
215 }
216
217 if (t->spacetype == SPACE_SEQ) {
219 col[3] = 128;
220 }
221 else if (t->spacetype != SPACE_IMAGE) {
223 col[3] = 128;
224
225 UI_GetThemeColor3ubv(TH_SELECT, selectedCol);
226 selectedCol[3] = 128;
227
229 activeCol[3] = 192;
230 }
231
232 if (t->spacetype == SPACE_VIEW3D) {
233 const float *source_loc = nullptr;
234 const float *target_loc = nullptr;
235
237
240 /* Draw snap points. */
241
242 float size = 2.0f * UI_GetThemeValuef(TH_VERTEX_SIZE);
243 float view_inv[4][4];
244 copy_m4_m4(view_inv, rv3d->viewinv);
245
247 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
248
250
253 if (p == t->tsnap.selectedPoint) {
254 immUniformColor4ubv(selectedCol);
255 }
256 else {
258 }
259 imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size, view_inv, pos);
260 }
261 }
262
264 }
265
266 if (draw_source) {
267 source_loc = t->tsnap.snap_source;
268 }
269
270 if (t->tsnap.status & SNAP_TARGET_FOUND) {
271 target_loc = t->tsnap.snap_target;
272 }
273
275 rv3d, source_loc, target_loc, t->tsnap.source_type, t->tsnap.target_type, col, activeCol);
276
277 /* Draw normal if needed. */
278 if (target_loc && usingSnappingNormal(t) && validSnappingNormal(t)) {
280 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
281
283 immUniformColor4ubv(activeCol);
285 immVertex3fv(pos, target_loc);
287 target_loc[0] + t->tsnap.snapNormal[0],
288 target_loc[1] + t->tsnap.snapNormal[1],
289 target_loc[2] + t->tsnap.snapNormal[2]);
290 immEnd();
292 }
293
295 }
296 else if (t->spacetype == SPACE_IMAGE) {
298 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
299
300 float x, y;
301 const float snap_point[2] = {
302 t->tsnap.snap_target[0] / t->aspect[0],
303 t->tsnap.snap_target[1] / t->aspect[1],
304 };
305 UI_view2d_view_to_region_fl(&t->region->v2d, UNPACK2(snap_point), &x, &y);
306 float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize;
307
310
312 immUniformColor3ub(255, 255, 255);
313 imm_draw_circle_wire_2d(pos, x, y, radius, 8);
315
317 }
318 else if (t->spacetype == SPACE_SEQ) {
319 const ARegion *region = t->region;
322 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
325 float pixelx = BLI_rctf_size_x(&region->v2d.cur) / BLI_rcti_size_x(&region->v2d.mask);
326
327 if (region->regiontype == RGN_TYPE_PREVIEW) {
328 if (t->tsnap.direction & DIR_GLOBAL_X) {
330 t->tsnap.snap_target[0] - pixelx,
331 region->v2d.cur.ymax,
332 t->tsnap.snap_target[0] + pixelx,
333 region->v2d.cur.ymin);
334 }
335 if (t->tsnap.direction & DIR_GLOBAL_Y) {
337 region->v2d.cur.xmin,
338 t->tsnap.snap_target[1] - pixelx,
339 region->v2d.cur.xmax,
340 t->tsnap.snap_target[1] + pixelx);
341 }
342 }
343 else {
345 t->tsnap.snap_target[0] - pixelx,
346 region->v2d.cur.ymax,
347 t->tsnap.snap_target[0] + pixelx,
348 region->v2d.cur.ymin);
349 }
350
353 }
354}
355
357{
359
360#if 0 /* XXX: need a proper selector for all snap mode. */
361 if (BIF_snappingSupported(t->obedit) && (event->type == EVT_TABKEY) &&
362 (event->modifier & KM_SHIFT))
363 {
364 /* Toggle snap and reinitialize. */
366 initSnapping(t, nullptr);
368 }
369#endif
370 if (event->type == MOUSEMOVE) {
372 }
373
374 return status;
375}
376
379 TransData *td,
380 TransDataExtension *td_ext)
381{
382 float iloc[3], loc[3], no[3];
383 float mval_fl[2];
384
385 copy_v3_v3(iloc, td->loc);
386 if (tc->use_local_mat) {
387 mul_m4_v3(tc->mat, iloc);
388 }
389 else if (t->options & CTX_OBJECT) {
390 Object *ob = static_cast<Object *>(td->extra);
392 copy_v3_v3(iloc, ob->object_to_world().location());
393 }
394
397 {
398 return false;
399 }
400
401 SnapObjectParams snap_object_params{};
402 snap_object_params.snap_target_select = t->tsnap.target_operation;
403 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
404 snap_object_params.occlusion_test = SNAP_OCCLUSION_ALWAYS;
405 snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0;
406
409 t->depsgraph,
410 t->region,
411 static_cast<const View3D *>(t->view),
413 &snap_object_params,
414 nullptr,
415 mval_fl,
416 nullptr,
417 nullptr,
418 loc,
419 no);
420 if (hit != SCE_SNAP_TO_FACE) {
421 return false;
422 }
423
424 float tvec[3];
425 sub_v3_v3v3(tvec, loc, iloc);
426
427 mul_m3_v3(td->smtx, tvec);
428
429 add_v3_v3(td->loc, tvec);
430
431 if ((t->tsnap.flag & SCE_SNAP_ROTATE) && (t->options & CTX_OBJECT)) {
432 /* Handle alignment as well. */
433 const float *original_normal;
434 float mat[3][3];
435
436 /* In pose mode, we want to align normals with Y axis of bones. */
437 original_normal = td->axismtx[2];
438
439 rotation_between_vecs_to_mat3(mat, original_normal, no);
440
441 transform_data_ext_rotate(td, td_ext, mat, true);
442
443 /* TODO: support constraints for rotation too? see #ElementRotation. */
444 }
445 return true;
446}
447
449{
450 float init_loc[3];
451 float prev_loc[3];
452 float snap_loc[3], snap_no[3];
453
454 copy_v3_v3(init_loc, td->iloc);
455 copy_v3_v3(prev_loc, td->loc);
456 if (tc->use_local_mat) {
457 mul_m4_v3(tc->mat, init_loc);
458 mul_m4_v3(tc->mat, prev_loc);
459 }
460 else if (t->options & CTX_OBJECT) {
461 Object *ob = static_cast<Object *>(td->extra);
463 copy_v3_v3(init_loc, ob->object_to_world().location());
464 }
465
466 SnapObjectParams snap_object_params{};
467 snap_object_params.snap_target_select = t->tsnap.target_operation;
468 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
469 snap_object_params.occlusion_test = SNAP_OCCLUSION_ALWAYS;
470 snap_object_params.use_backface_culling = false;
471 snap_object_params.face_nearest_steps = t->tsnap.face_nearest_steps;
473
476 t->depsgraph,
477 t->region,
478 static_cast<const View3D *>(t->view),
480 &snap_object_params,
481 init_loc,
482 nullptr,
483 prev_loc,
484 nullptr,
485 snap_loc,
486 snap_no);
487
488 if (hit != SCE_SNAP_INDIVIDUAL_NEAREST) {
489 return;
490 }
491
492 float tvec[3];
493 sub_v3_v3v3(tvec, snap_loc, prev_loc);
494 mul_m3_v3(td->smtx, tvec);
495 add_v3_v3(td->loc, tvec);
496
497 /* TODO: support snap alignment similar to #SCE_SNAP_INDIVIDUAL_PROJECT? */
498}
499
501{
502 if (!transform_snap_is_active(t)) {
503 return false;
504 }
505
507}
508
510{
512 return;
513 }
514
515 /* XXX: flickers in object mode. */
517 TransData *td = tc->data;
518 TransDataExtension *td_ext = tc->data_ext;
519 for (int i = 0; i < tc->data_len; i++, td++) {
520 if (td->flag & TD_SKIP) {
521 continue;
522 }
523
524 if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) {
525 continue;
526 }
527
528 /* If both face ray-cast and face nearest methods are enabled, start with face ray-cast and
529 * fall back to face nearest ray-cast does not hit. */
530 bool hit = false;
532 hit = applyFaceProject(t, tc, td, td_ext);
533 if (td_ext) {
534 td_ext++;
535 }
536 }
537
538 if (!hit && t->tsnap.mode & SCE_SNAP_INDIVIDUAL_NEAREST) {
539 applyFaceNearest(t, tc, td);
540 }
541#if 0 /* TODO: support this? */
542 constraintTransLim(t, td);
543#endif
544 }
545 }
546}
547
558
560{
562 return;
563 }
564
565 if (t->tsnap.mode != SCE_SNAP_TO_INCREMENT) {
566 double current = BLI_time_now_seconds();
567
568 /* Time base quirky code to go around find-nearest slowness. */
569 /* TODO: add exception for object mode, no need to slow it down then. */
570 if (current - t->tsnap.last >= 0.01) {
571 if (t->tsnap.snap_target_fn) {
572 t->tsnap.snap_target_fn(t, vec);
573 }
574 if (t->tsnap.snap_source_fn) {
575 t->tsnap.snap_source_fn(t);
576 }
577
578 t->tsnap.last = current;
579 }
580
581 if (validSnap(t)) {
582 t->mode_info->snap_apply_fn(t, vec);
583 }
584 }
585}
586
601
603{
604 return (t->tsnap.flag & SCE_SNAP_ROTATE) != 0;
605}
606
608{
609 if (validSnap(t)) {
610 if (!is_zero_v3(t->tsnap.snapNormal)) {
611 return true;
612 }
613 }
614
615 return false;
616}
617
618static bool bm_edge_is_snap_target(BMEdge *e, void * /*user_data*/)
619{
622 {
623 return false;
624 }
625
626 return true;
627}
628
629static bool bm_face_is_snap_target(BMFace *f, void * /*user_data*/)
630{
632 return false;
633 }
634
635 BMLoop *l_iter, *l_first;
636 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
637 do {
638 if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
639 return false;
640 }
641 } while ((l_iter = l_iter->next) != l_first);
642
643 return true;
644}
645
647{
648 ToolSettings *ts = t->settings;
649 switch (t->spacetype) {
650 case SPACE_VIEW3D:
651 if (r_prop) {
652 *r_prop = &rna_ToolSettings_use_snap;
653 }
654 return &ts->snap_flag;
655 case SPACE_NODE:
656 if (r_prop) {
657 *r_prop = &rna_ToolSettings_use_snap_node;
658 }
659 return &ts->snap_flag_node;
660 case SPACE_IMAGE:
661 if (r_prop) {
662 *r_prop = &rna_ToolSettings_use_snap_uv;
663 }
664 return &ts->snap_uv_flag;
665 case SPACE_SEQ:
666 if (r_prop) {
667 *r_prop = &rna_ToolSettings_use_snap_sequencer;
668 }
669 return &ts->snap_flag_seq;
670 case SPACE_ACTION:
671 case SPACE_NLA:
672 if (r_prop) {
673 *r_prop = &rna_ToolSettings_use_snap_anim;
674 }
675 return &ts->snap_flag_anim;
676 case SPACE_GRAPH: {
677 SpaceGraph *graph_editor = static_cast<SpaceGraph *>(t->area->spacedata.first);
678 switch (graph_editor->mode) {
680 /* The driver editor has a separate snapping flag so it can be kept disabled while
681 * keeping it enabled in the Graph Editor. */
682 return &ts->snap_flag_driver;
683
684 case SIPO_MODE_ANIMATION: {
685 if (r_prop) {
686 *r_prop = &rna_ToolSettings_use_snap_anim;
687 }
688 return &ts->snap_flag_anim;
689 }
690 default:
692 break;
693 }
694 }
695 }
696 /* #SPACE_EMPTY.
697 * It can happen when the operator is called via a handle in `bpy.app.handlers`. */
698 return nullptr;
699}
700
702{
703 if (short *snap_flag = transform_snap_flag_from_spacetype_ptr(t)) {
704 return eSnapFlag(*snap_flag);
705 }
706
707 /* #SPACE_EMPTY.
708 * It can happen when the operator is called via a handle in `bpy.app.handlers`. */
709 return eSnapFlag(0);
710}
711
713{
714 ToolSettings *ts = t->settings;
715
716 if (t->spacetype == SPACE_NODE) {
717 return eSnapMode(ts->snap_node_mode);
718 }
719
720 if (t->spacetype == SPACE_IMAGE) {
721 return eSnapMode(ts->snap_uv_mode);
722 }
723
724 if (t->spacetype == SPACE_SEQ) {
726 }
727
728 if (t->spacetype == SPACE_VIEW3D) {
731 }
732 eSnapMode snap_mode = eSnapMode(ts->snap_mode);
733 if (t->mode == TFM_TRANSLATION) {
734 /* Use grid-snap for absolute snap while translating, see: #147246. */
735 if ((snap_mode & SCE_SNAP_TO_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID)) {
736 snap_mode &= ~SCE_SNAP_TO_INCREMENT;
737 snap_mode |= SCE_SNAP_TO_GRID;
738 }
739 }
740 return snap_mode;
741 }
742
744 return eSnapMode(ts->snap_anim_mode);
745 }
746
747 if (t->spacetype == SPACE_GRAPH) {
748 SpaceGraph *graph_editor = static_cast<SpaceGraph *>(t->area->spacedata.first);
749 switch (graph_editor->mode) {
751 /* Snapping to full values is the only mode that currently makes
752 * sense for the driver editor. */
753 return SCE_SNAP_TO_FRAME;
754
756 return eSnapMode(ts->snap_anim_mode);
757
758 default:
760 break;
761 }
762 }
763
765}
766
768{
769 /* `t->tsnap.target_operation` not initialized yet. */
771
772 eSnapTargetOP target_operation = SCE_SNAP_TARGET_ALL;
773
777 const int obedit_type = t->obedit_type;
778 if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
779 /* Particles edit mode. */
780 }
782 /* In "Edit Strokes" mode,
783 * snap tool can perform snap to selected or active objects (see #49632)
784 * TODO: perform self snap in gpencil_strokes.
785 *
786 * When we're moving the origins, allow snapping onto our own geometry (see #69132). */
787 }
788 else if (obedit_type != -1) {
789 /* Edit mode. */
790 if (obedit_type == OB_MESH) {
791 /* Editing a mesh. */
792 if ((t->flag & T_PROP_EDIT) != 0) {
793 /* Exclude editmesh when using proportional edit. */
794 target_operation |= SCE_SNAP_TARGET_NOT_EDITED;
795 }
796 /* UV editing must never snap to the selection as this is what is transformed. */
797 if (t->spacetype == SPACE_IMAGE) {
798 target_operation |= SCE_SNAP_TARGET_NOT_SELECTED;
799 }
800 }
801 else if (ELEM(obedit_type, OB_ARMATURE, OB_CURVES_LEGACY, OB_SURF, OB_LATTICE, OB_MBALL)) {
802 /* Temporary limited to edit mode armature, curves, surfaces, lattices, and meta-balls.
803 */
804 target_operation |= SCE_SNAP_TARGET_NOT_SELECTED;
805 }
806 }
807 else {
808 /* Object or pose mode. */
810 }
811 }
812 else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
813 target_operation |= SCE_SNAP_TARGET_NOT_SELECTED;
814 }
815
816 /* Use scene defaults only when transform is modal. */
817 if (t->flag & T_MODAL) {
818 ToolSettings *ts = t->settings;
821 SET_FLAG_FROM_TEST(target_operation,
824 SET_FLAG_FROM_TEST(target_operation,
827 SET_FLAG_FROM_TEST(target_operation,
830 }
831
832 return target_operation;
833}
834
836{
837 if (t->data_type == &TransConvertType_Mesh) {
838 /* Ignore elements being transformed. */
841 (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
845 }
846 else {
847 /* Ignore hidden geometry in the general case. */
850 (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
851 (bool (*)(BMEdge *, void *))BM_elem_cb_check_hflag_disabled,
852 (bool (*)(BMFace *, void *))BM_elem_cb_check_hflag_disabled,
854 }
855}
856
858{
859 if (!transformModeUseSnap(t)) {
860 /* In this case, snapping is always disabled by default. */
861 t->modifiers &= ~MOD_SNAP;
862 }
863 else if (t->flag & T_MODAL) {
864 /* Use scene defaults only when transform is modal. */
865 if (t->tsnap.flag & SCE_SNAP) {
866 t->modifiers |= MOD_SNAP;
867 }
868 }
869
870 if (doForceIncrementSnap(t)) {
872 }
873
874 if ((t->spacetype != SPACE_VIEW3D) || (t->flag & T_NO_PROJECT)) {
875 /* Force project off when not supported. */
877 }
878
881 }
882}
883
884void transform_snap_grid_init(const TransInfo *t, float r_snap[3], float *r_snap_precision)
885{
886 /* Default values. */
887 r_snap[0] = r_snap[1] = 1.0f;
888 r_snap[2] = 0.0f;
889 *r_snap_precision = 0.1f;
890
891 if (t->spacetype == SPACE_VIEW3D) {
892 /* Used by incremental snap. */
893 if (t->region->regiontype == RGN_TYPE_WINDOW) {
894 View3D *v3d = static_cast<View3D *>(t->area->spacedata.first);
895 r_snap[0] = r_snap[1] = r_snap[2] = ED_view3d_grid_view_scale(
896 t->scene, v3d, t->region, nullptr);
897 }
898 }
899 else if (t->spacetype == SPACE_IMAGE) {
900 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
901 const View2D *v2d = &t->region->v2d;
902 int grid_size = SI_GRID_STEPS_LEN;
903 float zoom_factor = ED_space_image_zoom_level(v2d, grid_size);
904 float grid_steps_x[SI_GRID_STEPS_LEN];
905 float grid_steps_y[SI_GRID_STEPS_LEN];
906
907 ED_space_image_grid_steps(sima, grid_steps_x, grid_steps_y, grid_size);
908 /* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */
909 r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps_x, zoom_factor);
910 r_snap[1] = ED_space_image_increment_snap_value(grid_size, grid_steps_y, zoom_factor);
911 *r_snap_precision = 0.5f;
912 }
913 else if (t->spacetype == SPACE_CLIP) {
914 r_snap[0] = r_snap[1] = 0.125f;
915 *r_snap_precision = 0.5f;
916 }
917 else if (t->spacetype == SPACE_NODE) {
918 r_snap[0] = r_snap[1] = space_node::grid_size_get();
919 }
920}
921
923{
924 ToolSettings *ts = t->settings;
925 eSnapSourceOP snap_source = eSnapSourceOP(ts->snap_target);
926
927 resetSnapping(t);
928
933
935
936 /* Overwrite defaults with values ​​in properties. */
937 PropertyRNA *prop;
938 if (op && (prop = RNA_struct_find_property(op->ptr, "snap"))) {
939 if (RNA_property_is_set(op->ptr, prop)) {
941 }
942
943 if ((prop = RNA_struct_find_property(op->ptr, "snap_elements")) &&
944 RNA_property_is_set(op->ptr, prop))
945 {
947 }
948
949 /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of
950 * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved
951 * geometry is snapped). */
952 if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) &&
953 RNA_property_is_set(op->ptr, prop))
954 {
955 snap_source = eSnapSourceOP(RNA_property_enum_get(op->ptr, prop));
956 }
957
958 if ((prop = RNA_struct_find_property(op->ptr, "snap_point")) &&
959 RNA_property_is_set(op->ptr, prop))
960 {
964 }
965
966 /* Snap align only defined in specific cases. */
967 if ((prop = RNA_struct_find_property(op->ptr, "snap_align")) &&
968 RNA_property_is_set(op->ptr, prop))
969 {
971
972 RNA_float_get_array(op->ptr, "snap_normal", t->tsnap.snapNormal);
974 }
975
976 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_project")) &&
977 RNA_property_is_set(op->ptr, prop))
978 {
981 }
982
983 /* Use_snap_self is misnamed and should be use_snap_active. */
984 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_self")) &&
985 RNA_property_is_set(op->ptr, prop))
986 {
988 !RNA_property_boolean_get(op->ptr, prop),
990 }
991
992 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_edit")) &&
993 RNA_property_is_set(op->ptr, prop))
994 {
996 !RNA_property_boolean_get(op->ptr, prop),
998 }
999
1000 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonedit")) &&
1001 RNA_property_is_set(op->ptr, prop))
1002 {
1004 !RNA_property_boolean_get(op->ptr, prop),
1006 }
1007
1008 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) &&
1009 RNA_property_is_set(op->ptr, prop))
1010 {
1012 RNA_property_boolean_get(op->ptr, prop),
1014 }
1015 }
1016
1017 t->tsnap.source_operation = snap_source;
1019}
1020
1022{
1026
1027 if (t->spacetype == SPACE_VIEW3D) {
1028 if (t->tsnap.object_context == nullptr) {
1032 }
1033 }
1034 else if (t->spacetype == SPACE_SEQ) {
1035 if (t->tsnap.seq_context == nullptr) {
1037 }
1038 }
1039
1040 /* Default increment values. */
1041 t->increment = float3(1.0f);
1042 t->increment_precision = 0.1f;
1043}
1044
1058
1060{
1061 /* The final value of increment with precision is `t->increment[0] * t->increment_precision`.
1062 * Therefore, we divide `snap_angle_increment_*_precision` by `snap_angle_increment_*`
1063 * to compute `increment_precision`. */
1064 float increment;
1065 float increment_precision;
1066 if (t->spacetype == SPACE_VIEW3D) {
1067 increment = t->settings->snap_angle_increment_3d;
1068 increment_precision = t->settings->snap_angle_increment_3d_precision;
1069 }
1070 else {
1071 increment = t->settings->snap_angle_increment_2d;
1072 increment_precision = t->settings->snap_angle_increment_2d_precision;
1073 }
1074
1075 t->increment[0] = increment;
1076 if (increment != 0.0f) {
1077 t->increment_precision = float(double(increment_precision) / double(increment));
1078 }
1079 else {
1080 t->increment_precision = 1.0f;
1081 }
1082}
1083
1085{
1086 if (t->spacetype == SPACE_VIEW3D) {
1087 if (t->options & CTX_CAMERA) {
1088 /* Not with camera selected in camera view. */
1089 return;
1090 }
1092 }
1093 else if (t->spacetype == SPACE_IMAGE) {
1094 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
1097
1098 const bool is_uv_editor = sima->mode == SI_MODE_UV;
1099 const bool has_edit_object = obact && BKE_object_is_in_editmode(obact);
1100 if (is_uv_editor && has_edit_object) {
1102 }
1103 }
1104 else if (t->spacetype == SPACE_NODE) {
1105 /* Pass. */
1106 }
1107 else if (t->spacetype == SPACE_SEQ) {
1109 /* The target is calculated along with the snap point. */
1110 return;
1111 }
1112 else if (t->spacetype == SPACE_NLA) {
1114 /* The target is calculated along with the snap point. */
1115 return;
1116 }
1117 else {
1118 return;
1119 }
1120
1121 switch (t->tsnap.source_operation) {
1124 break;
1126 if (!ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
1128 break;
1129 }
1130 /* Can't do TARGET_CENTER with these modes,
1131 * use TARGET_MEDIAN instead. */
1135 break;
1138
1139 /* XXX, workaround: active needs to be calculated before transforming, otherwise
1140 * `t->tsnap.snap_source` will be calculated with the transformed data since we're not
1141 * reading from 'td->center' in this case. (See: #40241 and #40348). */
1143 break;
1144 }
1145}
1146
1148{
1149 /* Currently only 3D viewport works for snapping points. */
1152
1153 t->tsnap.selectedPoint = p;
1154
1156
1157 BLI_addtail(&t->tsnap.points, p);
1158
1160 }
1161}
1162
1164{
1166
1167 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1168 TransSnapPoint *closest_p = nullptr;
1169 float dist_min_sq = TRANSFORM_SNAP_MAX_PX;
1170 float screen_loc[2];
1171
1173 float dist_sq;
1174
1175 if (ED_view3d_project_float_global(t->region, p->co, screen_loc, V3D_PROJ_TEST_NOP) !=
1177 {
1178 continue;
1179 }
1180
1181 dist_sq = len_squared_v2v2(t->mval, screen_loc);
1182
1183 if (dist_sq < dist_min_sq) {
1184 closest_p = p;
1185 dist_min_sq = dist_sq;
1186 }
1187 }
1188
1189 if (closest_p) {
1190 if (t->tsnap.selectedPoint != closest_p) {
1192 }
1193
1194 t->tsnap.selectedPoint = closest_p;
1195 }
1196 }
1197
1198 return status;
1199}
1200
1202{
1203 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1205
1206 if (t->tsnap.selectedPoint) {
1208
1211 }
1212
1213 t->tsnap.selectedPoint = nullptr;
1214 }
1215 }
1216}
1217
1218void getSnapPoint(const TransInfo *t, float vec[3])
1219{
1220 if (t->tsnap.points.first) {
1221 TransSnapPoint *p;
1222 int total = 0;
1223
1224 vec[0] = vec[1] = vec[2] = 0;
1225
1226 for (p = static_cast<TransSnapPoint *>(t->tsnap.points.first); p; p = p->next, total++) {
1227 add_v3_v3(vec, p->co);
1228 }
1229
1230 if (t->tsnap.status & SNAP_TARGET_FOUND) {
1231 add_v3_v3(vec, t->tsnap.snap_target);
1232 total++;
1233 }
1234
1235 mul_v3_fl(vec, 1.0f / total);
1236 }
1237 else {
1238 copy_v3_v3(vec, t->tsnap.snap_target);
1239 }
1240}
1241
1243{
1244 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1247 t->tsnap.selectedPoint = nullptr;
1248 }
1249}
1250
1252
1253/* -------------------------------------------------------------------- */
1256
1257static void snap_grid_uv_apply(TransInfo *t, const float grid_dist[2], float r_out[2])
1258{
1259 float3 in;
1260 convertViewVec(t, in, t->mval[0] - t->center2d[0], t->mval[1] - t->center2d[1]);
1261
1262 if (t->con.mode & CON_APPLY) {
1263 /* We need to clear the previous Snap to Grid result,
1264 * otherwise #t->con.applyVec will have no effect. */
1268 }
1269
1270 const float *center_global = t->center_global;
1271 for (int i = 0; i < 2; i++) {
1272 const float iter_fac = grid_dist[i];
1273 r_out[i] = iter_fac * roundf((in[i] + center_global[i]) / iter_fac);
1274 }
1275}
1276
1277static bool snap_grid_uv(TransInfo *t, float r_val[2])
1278{
1279 float grid_dist[2];
1280 mul_v2_v2v2(grid_dist, t->snap_spatial, t->aspect);
1281 if (t->modifiers & MOD_PRECISION) {
1282 mul_v2_fl(grid_dist, t->snap_spatial_precision);
1283 }
1284
1285 /* Early bailing out if no need to snap */
1286 if (is_zero_v2(grid_dist)) {
1287 return false;
1288 }
1289
1290 snap_grid_uv_apply(t, grid_dist, r_val);
1292 return true;
1293}
1294
1296
1297/* -------------------------------------------------------------------- */
1300
1301static void snap_target_view3d_fn(TransInfo *t, float * /*vec*/)
1302{
1304 float loc[3];
1305 float no[3];
1306 bool found = false;
1307 eSnapMode snap_elem = SCE_SNAP_TO_NONE;
1308 float dist_px = SNAP_MIN_DISTANCE; /* Use a user defined value here. */
1309
1311 zero_v3(no); /* objects won't set this */
1312 snap_elem = snapObjectsTransform(t, t->mval, &dist_px, loc, no);
1313 found = (snap_elem != SCE_SNAP_TO_NONE);
1314 }
1315 if ((found == false) && (t->tsnap.mode & SCE_SNAP_TO_VOLUME)) {
1316 bool use_peel = (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0;
1317 found = peelObjectsTransform(t, t->mval, use_peel, loc, no, nullptr);
1318
1319 if (found) {
1320 snap_elem = SCE_SNAP_TO_VOLUME;
1321 }
1322 }
1323
1324 if (found == true) {
1325 copy_v3_v3(t->tsnap.snap_target, loc);
1326 copy_v3_v3(t->tsnap.snapNormal, no);
1327
1329
1330 if (snap_elem == SCE_SNAP_TO_GRID && t->mode_info != &TransMode_translate) {
1331 /* Change it to #SCE_SNAP_TO_POINT so we can see the symbol for other modes. */
1332 snap_elem = SCE_SNAP_TO_POINT;
1333 }
1334 }
1335 else {
1337 }
1338
1339 t->tsnap.target_type = snap_elem;
1340}
1341
1342static void snap_target_uv_fn(TransInfo *t, float * /*vec*/)
1343{
1345 bool found = false;
1346 if (t->tsnap.mode & SCE_SNAP_TO_VERTEX) {
1347 const Vector<Object *> objects =
1349 t->scene, t->view_layer, nullptr);
1350
1351 float dist_sq = square_f(float(SNAP_MIN_DISTANCE));
1353 t->scene,
1354 objects,
1355 t->mval,
1357 &dist_sq,
1358 t->tsnap.snap_target))
1359 {
1360 t->tsnap.snap_target[0] *= t->aspect[0];
1361 t->tsnap.snap_target[1] *= t->aspect[1];
1363 found = true;
1364 }
1365 }
1366
1367 if (!found && (t->tsnap.mode & SCE_SNAP_TO_GRID)) {
1368 found = snap_grid_uv(t, t->tsnap.snap_target);
1369 }
1370
1372}
1373
1374static void snap_target_sequencer_fn(TransInfo *t, float * /*vec*/)
1375{
1377 if (snap_sequencer_calc(t)) {
1379 }
1380 else {
1382 }
1383}
1384
1385static void snap_target_nla_fn(TransInfo *t, float *vec)
1386{
1388 if (transform_snap_nla_calc(t, vec)) {
1390 }
1391 else {
1393 }
1394}
1395
1397
1398/* -------------------------------------------------------------------- */
1401
1402void tranform_snap_target_median_calc(const TransInfo *t, float r_median[3])
1403{
1404 int i_accum = 0;
1405
1406 zero_v3(r_median);
1407
1409 float v[3];
1410 zero_v3(v);
1411
1412 int num_selected = 0;
1413 tc->foreach_index_selected([&](const int i) {
1414 add_v3_v3(v, tc->data[i].center);
1415 num_selected++;
1416 });
1417
1418 if (num_selected == 0) {
1419 /* Is this possible? */
1420 continue;
1421 }
1422
1423 mul_v3_fl(v, 1.0 / num_selected);
1424
1425 if (tc->use_local_mat) {
1426 mul_m4_v3(tc->mat, v);
1427 }
1428
1429 add_v3_v3(r_median, v);
1430 i_accum++;
1431 }
1432
1433 mul_v3_fl(r_median, 1.0 / i_accum);
1434}
1435
1437{
1438 /* Only need to calculate once. */
1439 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1441
1444 }
1445}
1446
1448{
1449 /* Only need to calculate once. */
1450 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1451 if (calculateCenterActive(t, true, t->tsnap.snap_source)) {
1454 }
1455 else {
1456 /* No active, default to median, */
1460 }
1461 }
1462}
1463
1465{
1466 /* Only need to calculate once. */
1467 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1471 }
1472}
1473
1475{
1476 /* Only valid if a snap point has been selected. */
1477 if (!(t->tsnap.status & SNAP_TARGET_FOUND)) {
1478 return;
1479 }
1480
1482 /* Previously Snap to Grid had its own snap source which was always the result of
1483 * #snap_source_median_fn. Now this mode shares the same code, so to not change the behavior
1484 * too much when using Closest, use the transform pivot as the snap source in this case. */
1487 /* Use #SCE_SNAP_TO_POINT to differentiate from 'Closest' bounds and thus avoid recalculating
1488 * the median center. */
1490 }
1491 }
1492 else {
1493 float dist_closest = 0.0f;
1494 TransData *closest = nullptr;
1495
1496 /* Object mode. */
1497 if (t->options & CTX_OBJECT) {
1499 tc->foreach_index_selected([&](const int i) {
1500 TransData *td = &tc->data[i];
1501
1502 std::optional<Bounds<float3>> bounds;
1503
1504 if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
1505 Object *ob = static_cast<Object *>(td->extra);
1507 }
1508
1509 /* Use bound-box if possible. */
1510 if (bounds) {
1511 TransDataExtension *td_ext = &tc->data_ext[i];
1512 const std::array<float3, 8> bounds_corners = bounds::corners(*bounds);
1513 int j;
1514
1515 for (j = 0; j < 8; j++) {
1516 float loc[3];
1517 float dist;
1518
1519 copy_v3_v3(loc, bounds_corners[j]);
1520 mul_m4_v3(td_ext->obmat, loc);
1521
1522 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1523
1524 if ((dist != TRANSFORM_DIST_INVALID) &&
1525 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1526 {
1527 copy_v3_v3(t->tsnap.snap_source, loc);
1528 closest = td;
1529 dist_closest = dist;
1530 }
1531 }
1532 }
1533 /* Use element center otherwise. */
1534 else {
1535 float loc[3];
1536 float dist;
1537
1538 copy_v3_v3(loc, td->center);
1539
1540 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1541
1542 if ((dist != TRANSFORM_DIST_INVALID) &&
1543 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1544 {
1545 copy_v3_v3(t->tsnap.snap_source, loc);
1546 closest = td;
1547 }
1548 }
1549 });
1550 }
1551 }
1552 else {
1554 tc->foreach_index_selected([&](const int i) {
1555 TransData *td = &tc->data[i];
1556
1557 float loc[3];
1558 float dist;
1559
1560 copy_v3_v3(loc, td->center);
1561
1562 if (tc->use_local_mat) {
1563 mul_m4_v3(tc->mat, loc);
1564 }
1565
1566 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1567
1568 if ((dist != TRANSFORM_DIST_INVALID) &&
1569 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1570 {
1571 copy_v3_v3(t->tsnap.snap_source, loc);
1572 closest = td;
1573 dist_closest = dist;
1574 }
1575 });
1576 }
1577 }
1578 }
1579
1581}
1582
1584
1585/* -------------------------------------------------------------------- */
1588
1590 TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
1591{
1592 SnapObjectParams snap_object_params{};
1593 snap_object_params.snap_target_select = t->tsnap.target_operation;
1594 snap_object_params.grid_size = (t->modifiers & MOD_PRECISION) ?
1596 t->snap_spatial[0];
1597 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
1598 snap_object_params.occlusion_test = SNAP_OCCLUSION_AS_SEEM;
1599 snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0;
1600
1601 float *prev_co = (t->tsnap.status & SNAP_SOURCE_FOUND) ? t->tsnap.snap_source : t->center_global;
1602 float *grid_co = nullptr, grid_co_stack[3];
1603 if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && (t->con.mode & CON_APPLY) && t->mode != TFM_ROTATION) {
1604 /* Without this position adjustment, the snap may be far from the expected constraint point. */
1605 grid_co = grid_co_stack;
1606 convertViewVec(t, grid_co, mval[0] - t->center2d[0], mval[1] - t->center2d[1]);
1608 transform_constraint_get_nearest(t, grid_co, grid_co);
1609 add_v3_v3(grid_co, t->center_global);
1610 }
1611
1613 t->depsgraph,
1614 t->region,
1615 static_cast<const View3D *>(t->view),
1616 t->tsnap.mode,
1617 &snap_object_params,
1618 grid_co,
1619 mval,
1620 prev_co,
1621 dist_px,
1622 r_loc,
1623 r_no);
1624}
1625
1627
1628/* -------------------------------------------------------------------- */
1631
1633 const float mval[2],
1634 const bool use_peel_object,
1635 /* Return args. */
1636 float r_loc[3],
1637 float r_no[3],
1638 float *r_thickness)
1639{
1640 SnapObjectParams snap_object_params{};
1641 snap_object_params.snap_target_select = t->tsnap.target_operation;
1642 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
1643
1644 ListBase depths_peel = {nullptr};
1646 t->depsgraph,
1647 t->region,
1648 static_cast<const View3D *>(t->view),
1649 &snap_object_params,
1650 mval,
1651 -1.0f,
1652 false,
1653 &depths_peel);
1654
1655 if (!BLI_listbase_is_empty(&depths_peel)) {
1656 /* At the moment we only use the hits of the first object. */
1657 SnapObjectHitDepth *hit_min = static_cast<SnapObjectHitDepth *>(depths_peel.first);
1658 for (SnapObjectHitDepth *iter = hit_min->next; iter; iter = iter->next) {
1659 if (iter->depth < hit_min->depth) {
1660 hit_min = iter;
1661 }
1662 }
1663 SnapObjectHitDepth *hit_max = nullptr;
1664
1665 if (use_peel_object) {
1666 /* If peeling objects, take the first and last from each object. */
1667 hit_max = hit_min;
1668 LISTBASE_FOREACH (SnapObjectHitDepth *, iter, &depths_peel) {
1669 if ((iter->depth > hit_max->depth) && (iter->ob_uuid == hit_min->ob_uuid)) {
1670 hit_max = iter;
1671 }
1672 }
1673 }
1674 else {
1675 /* Otherwise, pair first with second and so on. */
1676 LISTBASE_FOREACH (SnapObjectHitDepth *, iter, &depths_peel) {
1677 if ((iter != hit_min) && (iter->ob_uuid == hit_min->ob_uuid)) {
1678 if (hit_max == nullptr) {
1679 hit_max = iter;
1680 }
1681 else if (iter->depth < hit_max->depth) {
1682 hit_max = iter;
1683 }
1684 }
1685 }
1686 /* In this case has only one hit. treat as ray-cast. */
1687 if (hit_max == nullptr) {
1688 hit_max = hit_min;
1689 }
1690 }
1691
1692 mid_v3_v3v3(r_loc, hit_min->co, hit_max->co);
1693
1694 if (r_thickness) {
1695 *r_thickness = hit_max->depth - hit_min->depth;
1696 }
1697
1698 /* XXX, is there a correct normal in this case ???, for now just z up. */
1699 r_no[0] = 0.0;
1700 r_no[1] = 0.0;
1701 r_no[2] = 1.0;
1702
1703 LISTBASE_FOREACH_MUTABLE (SnapObjectHitDepth *, link, &depths_peel) {
1704 MEM_delete(link);
1705 }
1706 return true;
1707 }
1708 return false;
1709}
1710
1712
1713/* -------------------------------------------------------------------- */
1716
1717static void snap_increment_apply(const TransInfo *t, const float loc[3], float r_out[3])
1718{
1719 bool use_precision = (t->modifiers & MOD_PRECISION) != 0;
1720
1721 /* Relative snapping in fixed increments. */
1722 for (int i = 0; i <= t->idx_max; i++) {
1723 const float iter_fac = use_precision ? t->increment[i] * t->increment_precision :
1724 t->increment[i];
1725 if (iter_fac != 0.0f) {
1726 r_out[i] = iter_fac * roundf(loc[i] / iter_fac);
1727 }
1728 }
1729}
1730
1731bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float *r_val)
1732{
1733 if (!transform_snap_is_active(t)) {
1734 return false;
1735 }
1736
1737 if (t->spacetype == SPACE_SEQ) {
1738 /* Sequencer has its own dedicated enum for snap_mode with increment snap bit overridden. */
1739 return false;
1740 }
1741
1742 if (!(t->tsnap.mode & SCE_SNAP_TO_INCREMENT)) {
1743 return false;
1744 }
1745
1746 if (t->spacetype != SPACE_VIEW3D && validSnap(t)) {
1747 /* Only do something if using absolute or incremental grid snapping
1748 * and there is no valid snap point. */
1749 return false;
1750 }
1751
1752 if (use_local_space) {
1753 mul_m3_v3(t->spacemtx_inv, r_val);
1754 }
1755
1756 snap_increment_apply(t, r_val, r_val);
1757
1758 if (use_local_space) {
1759 mul_m3_v3(t->spacemtx, r_val);
1760 }
1761
1762 return true;
1763}
1764
1765bool transform_snap_increment(const TransInfo *t, float *r_val)
1766{
1767 return transform_snap_increment_ex(t, false, r_val);
1768}
1769
1771{
1773 {
1774 return (t->modifiers & MOD_PRECISION) ? t->increment[0] * t->increment_precision :
1775 t->increment[0];
1776 }
1777
1778 return 0.0f;
1779}
1780
1788
1790
1791/* -------------------------------------------------------------------- */
1794
1796 const float p1[3],
1797 const float p2[3])
1798{
1799 return len_squared_v3v3(p1, p2);
1800}
1801
1803
1804} // namespace blender::ed::transform
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
std::optional< blender::Bounds< blender::float3 > > BKE_object_boundbox_eval_cached_get(const Object *ob)
void BKE_object_eval_transform_all(Depsgraph *depsgraph, Scene *scene, Object *object)
bool BKE_scene_uses_blender_workbench(const Scene *scene)
Definition scene.cc:2829
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE int max_ii(int a, int b)
MINLINE float square_f(float a)
void mul_m3_v3(const float M[3][3], float r[3])
void mul_m4_v3(const float M[4][4], float r[3])
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void rotation_between_vecs_to_mat3(float m[3][3], const float v1[3], const float v2[3])
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE bool is_zero_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
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
unsigned char uchar
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#define UNPACK2(a)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define POINTER_FROM_UINT(i)
@ OB_SOLID
@ OB_RENDER
@ OB_MODE_PARTICLE_EDIT
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_ARMATURE
@ OB_MESH
@ OB_CURVES_LEGACY
struct Object Object
#define SCE_SNAP_TO_GEOM
#define SCE_SNAP_TO_VERTEX
@ SCE_SNAP_PEEL_OBJECT
@ SCE_SNAP_TO_INCLUDE_EDITED
@ SCE_SNAP_NOT_TO_ACTIVE
@ SCE_SNAP
@ SCE_SNAP_TO_INCLUDE_NONEDITED
@ SCE_SNAP_ROTATE
@ SCE_SNAP_ABS_GRID
@ SCE_SNAP_BACKFACE_CULLING
@ SCE_SNAP_KEEP_ON_SAME_OBJECT
@ SCE_SNAP_TO_ONLY_SELECTABLE
@ SCE_SNAP_TRANSFORM_MODE_SCALE
@ SCE_SNAP_TRANSFORM_MODE_ROTATE
@ SCE_SNAP_TRANSFORM_MODE_TRANSLATE
eSnapSourceOP
@ SCE_SNAP_SOURCE_MEDIAN
@ SCE_SNAP_SOURCE_CLOSEST
@ SCE_SNAP_SOURCE_ACTIVE
@ SCE_SNAP_SOURCE_CENTER
eSnapTargetOP
@ SCE_SNAP_TARGET_NOT_ACTIVE
@ SCE_SNAP_TARGET_NOT_NONEDITED
@ SCE_SNAP_TARGET_ONLY_SELECTABLE
@ SCE_SNAP_TARGET_ALL
@ SCE_SNAP_TARGET_NOT_SELECTED
@ SCE_SNAP_TARGET_NOT_EDITED
@ SCE_SNAP_INDIVIDUAL_NEAREST
@ SCE_SNAP_TO_EDGE
@ SCE_SNAP_TO_FACE
@ SCE_SNAP_INDIVIDUAL_PROJECT
@ SCE_SNAP_TO_EDGE_ENDPOINT
@ SCE_SNAP_TO_INCREMENT
@ SCE_SNAP_TO_GRID
@ SCE_SNAP_TO_EDGE_MIDPOINT
@ SCE_SNAP_TO_FRAME
@ SCE_SNAP_TO_VOLUME
@ SCE_SNAP_TO_EDGE_PERPENDICULAR
@ SCE_SNAP_TO_POINT
@ SCE_SNAP_TO_NONE
@ RGN_TYPE_WINDOW
@ RGN_TYPE_PREVIEW
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_NODE
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ SIPO_MODE_DRIVERS
@ SIPO_MODE_ANIMATION
#define SI_GRID_STEPS_LEN
@ SI_MODE_UV
@ V3D_SHADING_BACKFACE_CULLING
float ED_space_image_zoom_level(const View2D *v2d, int grid_dimension)
float ED_space_image_increment_snap_value(int grid_dimensions, const float grid_steps[SI_GRID_STEPS_LEN], float zoom_factor)
void ED_space_image_grid_steps(SpaceImage *sima, float grid_steps_x[SI_GRID_STEPS_LEN], float grid_steps_y[SI_GRID_STEPS_LEN], int grid_dimension)
#define ED_transform_snap_object_time_average_print()
bool ED_uvedit_nearest_uv_multi(const View2D *v2d, const Scene *scene, blender::Span< Object * > objects, const float mval_fl[2], const bool ignore_selected, float *dist_sq, float r_uv[2])
float ED_view3d_grid_view_scale(const Scene *scene, const View3D *v3d, const ARegion *region, const char **r_grid_unit)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:279
float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3])
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d, const float source_loc[3], const float target_loc[3], const eSnapMode source_type, const eSnapMode target_type, const uchar source_color[4], const uchar target_color[4])
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
void immUniformColor4ubv(const unsigned char rgba[4])
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immUniformColor3ub(unsigned char r, unsigned char g, unsigned char b)
void immVertex3f(uint attr_id, float x, float y, float z)
GPUVertFormat * immVertexFormat()
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void imm_drawcircball(const float cent[3], float radius, const float tmat[4][4], uint pos)
void immRectf(uint pos, float x1, float y1, float x2, float y2)
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
void GPU_matrix_push_projection()
void GPU_matrix_pop_projection()
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:114
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_depth_test(GPUDepthTest test)
Definition gpu_state.cc:68
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
@ TH_TRANSFORM
@ TH_VERTEX_SIZE
@ TH_SELECT
@ TH_SEQ_ACTIVE
@ TH_ACTIVE
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
float UI_GetThemeValuef(int colorid)
void UI_view2d_view_to_region_fl(const View2D *v2d, float x, float y, float *r_region_x, float *r_region_y) ATTR_NONNULL()
Definition view2d.cc:1739
@ KM_SHIFT
Definition WM_types.hh:278
#define U
bool BM_elem_cb_check_hflag_disabled(BMElem *ele, void *user_data)
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
#define BM_elem_flag_test(ele, hflag)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
bool closest(btVector3 &v)
nullptr float
#define roundf(x)
uint pos
uint col
#define in
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
std::array< VecBase< T, 3 >, 8 > corners(const Bounds< VecBase< T, 3 > > &bounds)
TransConvertTypeInfo TransConvertType_Mesh
static void snap_source_active_fn(TransInfo *t)
static bool bm_face_is_snap_target(BMFace *f, void *)
void transform_snap_reset_from_mode(TransInfo *t, wmOperator *op)
void snap_object_context_destroy(SnapObjectContext *sctx)
static void snap_target_sequencer_fn(TransInfo *t, float *vec)
void transform_snap_flag_from_modifiers_set(TransInfo *t)
bool peelObjectsTransform(TransInfo *t, const float mval[2], const bool use_peel_object, float r_loc[3], float r_no[3], float *r_thickness)
void getSnapPoint(const TransInfo *t, float vec[3])
void resetSnapping(TransInfo *t)
bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3])
static void snap_source_center_fn(TransInfo *t)
bool validSnappingNormal(const TransInfo *t)
static bool snap_use_backface_culling(const TransInfo *t)
static void snap_target_view3d_fn(TransInfo *t, float *vec)
static void snap_target_uv_fn(TransInfo *t, float *vec)
static bool transform_snap_mixed_is_active(const TransInfo *t)
bool transform_snap_increment(const TransInfo *t, float *r_val)
bool usingSnappingNormal(const TransInfo *t)
static void initSnappingMode(TransInfo *t)
void removeSnapPoint(TransInfo *t)
void transform_snap_mixed_apply(TransInfo *t, float *vec)
bool transformModeUseSnap(const TransInfo *t)
void initSnapping(TransInfo *t, wmOperator *op)
void tranform_snap_source_restore_context(TransInfo *t)
bool validSnap(const TransInfo *t)
void transform_snap_project_individual_apply(TransInfo *t)
float transform_snap_distance_len_squared_fn(TransInfo *, const float p1[3], const float p2[3])
static void snap_multipoints_free(TransInfo *t)
void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
static void applyFaceNearest(TransInfo *t, TransDataContainer *tc, TransData *td)
static eSnapMode snap_mode_from_spacetype(TransInfo *t)
static void snap_increment_apply(const TransInfo *t, const float loc[3], float r_out[3])
static bool doForceIncrementSnap(const TransInfo *t)
eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event)
bool transform_snap_project_individual_is_active(const TransInfo *t)
eRedrawFlag updateSelectedSnapPoint(TransInfo *t)
eSnapMode snap_object_project_view3d(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const eSnapMode snap_to, const SnapObjectParams *params, const float init_co[3], const float mval[2], const float prev_co[3], float *dist_px, float r_loc[3], float r_no[3])
static void snap_source_median_fn(TransInfo *t)
static void snap_grid_uv_apply(TransInfo *t, const float grid_dist[2], float r_out[2])
void transform_snap_grid_init(const TransInfo *t, float r_snap[3], float *r_snap_precision)
static eSnapMode snapObjectsTransform(TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
void addSnapPoint(TransInfo *t)
void transform_constraint_get_nearest(const TransInfo *t, const float3 &vec, float r_vec[3])
void drawSnapping(TransInfo *t)
static void setSnappingCallback(TransInfo *t)
bool transform_snap_is_active(const TransInfo *t)
static bool bm_edge_is_snap_target(BMEdge *e, void *)
void tranform_snap_target_median_calc(const TransInfo *t, float r_median[3])
TransSeqSnapData * snap_sequencer_data_alloc(const TransInfo *t)
SnapObjectContext * snap_object_context_create(Scene *scene, int flag)
void initSnapAngleIncrements(TransInfo *t)
static bool applyFaceProject(TransInfo *t, TransDataContainer *tc, TransData *td, TransDataExtension *td_ext)
short * transform_snap_flag_from_spacetype_ptr(TransInfo *t, const PropertyRNA **r_prop=nullptr)
void transform_data_ext_rotate(TransData *td, TransDataExtension *td_ext, float mat[3][3], bool use_drot)
static void snap_target_nla_fn(TransInfo *t, float *vec)
void freeSnapping(TransInfo *t)
static eSnapFlag snap_flag_from_spacetype(TransInfo *t)
void snap_object_context_set_editmesh_callbacks(SnapObjectContext *sctx, bool(*test_vert_fn)(BMVert *, void *user_data), bool(*test_edge_fn)(BMEdge *, void *user_data), bool(*test_face_fn)(BMFace *, void *user_data), void *user_data)
void snap_sequencer_data_free(TransSeqSnapData *data)
void constraintTransLim(const TransInfo *t, const TransDataContainer *tc, TransData *td)
static void snap_source_closest_fn(TransInfo *t)
static void snap_object_context_init(TransInfo *t)
bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float *r_val)
bool transform_snap_nla_calc(TransInfo *t, float *vec)
static eSnapTargetOP snap_target_select_from_spacetype_and_tool_settings(TransInfo *t)
bool object_project_all_view3d_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const SnapObjectParams *params, const float mval[2], float ray_depth, bool sort, ListBase *r_hit_list)
static bool snap_grid_uv(TransInfo *t, float r_val[2])
float transform_snap_increment_get(const TransInfo *t)
short tool_settings_snap_mode_get(Scene *scene)
Definition sequencer.cc:397
VecBase< float, 3 > float3
const int status
#define fabsf
void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
void * regiondata
struct BMVert * v
struct BMLoop * next
struct Object * object
void * first
float viewinv[4][4]
View3DShading shading
struct SceneDisplay display
ListBase spacedata
float snap_angle_increment_3d_precision
short snap_face_nearest_steps
float snap_angle_increment_2d_precision
float snap_angle_increment_3d
float snap_angle_increment_2d
char snap_transform_mode_flag
View3DShading shading
TransConvertTypeInfo * data_type
Definition transform.hh:810
void(* snap_apply_fn)(TransInfo *, float *)
float(* snap_distance_fn)(TransInfo *t, const float p1[3], const float p2[3])
SnapObjectContext * object_context
Definition transform.hh:565
void(* snap_source_fn)(TransInfo *)
Definition transform.hh:559
void(* snap_target_fn)(TransInfo *, float *)
Definition transform.hh:558
float xmax
float xmin
float ymax
float ymin
wmEventModifierFlag modifier
Definition WM_types.hh:774
wmEventType type
Definition WM_types.hh:757
struct PointerRNA * ptr
i
Definition text_draw.cc:230
#define TRANSFORM_SNAP_MAX_PX
Definition transform.hh:34
#define TRANSFORM_DIST_INVALID
Definition transform.hh:35
#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.
#define SNAP_MIN_DISTANCE
@ EVT_TABKEY
@ MOUSEMOVE
void wmOrtho2_region_pixelspace(const ARegion *region)