Blender V4.3
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
9#include "BLI_math_matrix.h"
10#include "BLI_math_rotation.h"
11#include "BLI_time.h"
12
13#include "GPU_immediate.hh"
14#include "GPU_matrix.hh"
15
16#include "BKE_editmesh.hh"
17#include "BKE_layer.hh"
18#include "BKE_node_runtime.hh"
19#include "BKE_object.hh"
20#include "BKE_scene.hh"
21
22#include "RNA_access.hh"
23#include "RNA_prototypes.hh"
24
25#include "WM_api.hh"
26
27#include "ED_image.hh"
28#include "ED_node.hh"
30#include "ED_uvedit.hh"
31
32#include "UI_resources.hh"
33#include "UI_view2d.hh"
34
35#include "SEQ_sequencer.hh"
36
37#include "transform.hh"
39#include "transform_convert.hh"
40#include "transform_mode.hh"
41#include "transform_snap.hh"
42
43using namespace blender;
44
45/* Use half of flt-max so we can scale up without an exception. */
46
47/* -------------------------------------------------------------------- */
51static void setSnappingCallback(TransInfo *t);
52
53static void snap_target_view3d_fn(TransInfo *t, float *vec);
54static void snap_target_uv_fn(TransInfo *t, float *vec);
55static void snap_target_node_fn(TransInfo *t, float *vec);
56static void snap_target_sequencer_fn(TransInfo *t, float *vec);
57static void snap_target_nla_fn(TransInfo *t, float *vec);
58
59static void snap_source_median_fn(TransInfo *t);
60static void snap_source_center_fn(TransInfo *t);
61static void snap_source_closest_fn(TransInfo *t);
62static void snap_source_active_fn(TransInfo *t);
63
65 TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]);
66
69/* -------------------------------------------------------------------- */
73static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetOP snap_target_select);
74static NodeBorder snapNodeBorder(eSnapMode snap_node_mode);
75
76#if 0
77int BIF_snappingSupported(Object *obedit)
78{
79 int status = 0;
80
81 /* Only support object mesh, armature, curves. */
82 if (obedit == nullptr ||
84 {
85 status = 1;
86 }
87
88 return status;
89}
90#endif
91
93{
95 View3D *v3d = static_cast<View3D *>(t->view);
97 return true;
98 }
99 if (v3d->shading.type == OB_RENDER &&
102 {
103 return true;
104 }
106 return true;
107 }
108 return false;
109}
110
118
120{
122 /* Those space-types define their own invert behavior instead of toggling it on/off. */
123 return;
124 }
125 if (t->spacetype == SPACE_GRAPH) {
126 /* This is to stay consistent with the behavior from 3.6. */
127 if (t->modifiers & MOD_SNAP_INVERT) {
129 }
130 else {
131 t->tsnap.mode &= ~SCE_SNAP_TO_INCREMENT;
132 }
133 /* In 3.6 when snapping was disabled, pressing the invert button would turn on snapping.
134 * But it wouldn't turn it off when it was enabled. */
135 if ((t->modifiers & MOD_SNAP) || (t->modifiers & MOD_SNAP_INVERT)) {
136 t->tsnap.flag |= SCE_SNAP;
137 }
138 else {
139 t->tsnap.flag &= ~SCE_SNAP;
140 }
141 return;
142 }
144 (((t->modifiers & (MOD_SNAP | MOD_SNAP_INVERT)) == MOD_SNAP) ||
146 SCE_SNAP);
147}
148
150{
151 return (t->tsnap.flag & SCE_SNAP) != 0;
152}
153
155{
156 /* The animation editors should not depend on the snapping options of the 3D viewport. */
158 return true;
159 }
160 ToolSettings *ts = t->settings;
161 if (t->mode == TFM_TRANSLATION) {
162 /* VSE preview snapping should also not depend on the 3D viewport. */
163 if (t->spacetype == SPACE_SEQ) {
164 return true;
165 }
167 }
168 if (t->mode == TFM_ROTATION) {
170 }
171 if (t->mode == TFM_RESIZE) {
173 }
174 if (ELEM(t->mode,
180 {
181 return true;
182 }
183
184 return false;
185}
186
187static bool doForceIncrementSnap(const TransInfo *t)
188{
190 /* These spaces don't support increment snapping. */
191 return false;
192 }
193 if (t->modifiers & MOD_SNAP_FORCED) {
194 return false;
195 }
196
197 return !transformModeUseSnap(t);
198}
199
201{
202 uchar col[4], selectedCol[4], activeCol[4];
204 return;
205 }
206
207 const bool draw_source = (t->flag & T_DRAW_SNAP_SOURCE) &&
209 const bool draw_target = (t->tsnap.status & (SNAP_TARGET_FOUND | SNAP_MULTI_POINTS));
210
211 if (!(draw_source || draw_target)) {
212 return;
213 }
214
215 if (t->spacetype == SPACE_SEQ) {
217 col[3] = 128;
218 }
219 else if (t->spacetype != SPACE_IMAGE) {
221 col[3] = 128;
222
223 UI_GetThemeColor3ubv(TH_SELECT, selectedCol);
224 selectedCol[3] = 128;
225
227 activeCol[3] = 192;
228 }
229
230 if (t->spacetype == SPACE_VIEW3D) {
231 const float *source_loc = nullptr;
232 const float *target_loc = nullptr;
233
235
238 /* Draw snap points. */
239
240 float size = 2.0f * UI_GetThemeValuef(TH_VERTEX_SIZE);
241 float view_inv[4][4];
242 copy_m4_m4(view_inv, rv3d->viewinv);
243
246
248
251 if (p == t->tsnap.selectedPoint) {
252 immUniformColor4ubv(selectedCol);
253 }
254 else {
256 }
257 imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size, view_inv, pos);
258 }
259 }
260
262 }
263
264 if (draw_source) {
265 source_loc = t->tsnap.snap_source;
266 }
267
268 if (t->tsnap.status & SNAP_TARGET_FOUND) {
269 target_loc = t->tsnap.snap_target;
270 }
271
273 rv3d, source_loc, target_loc, t->tsnap.source_type, t->tsnap.target_type, col, activeCol);
274
275 /* Draw normal if needed. */
276 if (target_loc && usingSnappingNormal(t) && validSnappingNormal(t)) {
279
281 immUniformColor4ubv(activeCol);
283 immVertex3fv(pos, target_loc);
285 target_loc[0] + t->tsnap.snapNormal[0],
286 target_loc[1] + t->tsnap.snapNormal[1],
287 target_loc[2] + t->tsnap.snapNormal[2]);
288 immEnd();
290 }
291
293 }
294 else if (t->spacetype == SPACE_IMAGE) {
296
297 float x, y;
298 const float snap_point[2] = {
299 t->tsnap.snap_target[0] / t->aspect[0],
300 t->tsnap.snap_target[1] / t->aspect[1],
301 };
302 UI_view2d_view_to_region_fl(&t->region->v2d, UNPACK2(snap_point), &x, &y);
303 float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize;
304
307
309 immUniformColor3ub(255, 255, 255);
310 imm_draw_circle_wire_2d(pos, x, y, radius, 8);
312
314 }
315 else if (t->spacetype == SPACE_NODE) {
316 ARegion *region = t->region;
317 float size;
318
319 size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
320
322
324
326
328 if (p == t->tsnap.selectedPoint) {
329 immUniformColor4ubv(selectedCol);
330 }
331 else {
333 }
334
335 ED_node_draw_snap(&region->v2d, p->co, size, NodeBorder(0), pos);
336 }
337
338 if (t->tsnap.status & SNAP_TARGET_FOUND) {
339 immUniformColor4ubv(activeCol);
340
342 &region->v2d, t->tsnap.snap_target, size, NodeBorder(t->tsnap.snapNodeBorder), pos);
343 }
344
346
348 }
349 else if (t->spacetype == SPACE_SEQ) {
350 const ARegion *region = t->region;
355 float pixelx = BLI_rctf_size_x(&region->v2d.cur) / BLI_rcti_size_x(&region->v2d.mask);
356
357 if (region->regiontype == RGN_TYPE_PREVIEW) {
358 if (t->tsnap.direction & DIR_GLOBAL_X) {
360 t->tsnap.snap_target[0] - pixelx,
361 region->v2d.cur.ymax,
362 t->tsnap.snap_target[0] + pixelx,
363 region->v2d.cur.ymin);
364 }
365 if (t->tsnap.direction & DIR_GLOBAL_Y) {
367 region->v2d.cur.xmin,
368 t->tsnap.snap_target[1] - pixelx,
369 region->v2d.cur.xmax,
370 t->tsnap.snap_target[1] + pixelx);
371 }
372 }
373 else {
375 t->tsnap.snap_target[0] - pixelx,
376 region->v2d.cur.ymax,
377 t->tsnap.snap_target[0] + pixelx,
378 region->v2d.cur.ymin);
379 }
380
383 }
384}
385
387{
389
390#if 0 /* XXX: need a proper selector for all snap mode. */
391 if (BIF_snappingSupported(t->obedit) && (event->type == EVT_TABKEY) &&
392 (event->modifier & KM_SHIFT))
393 {
394 /* Toggle snap and reinitialize. */
396 initSnapping(t, nullptr);
397 status = TREDRAW_HARD;
398 }
399#endif
400 if (event->type == MOUSEMOVE) {
401 status |= updateSelectedSnapPoint(t);
402 }
403
404 return status;
405}
406
408{
409 float iloc[3], loc[3], no[3];
410 float mval_fl[2];
411
412 copy_v3_v3(iloc, td->loc);
413 if (tc->use_local_mat) {
414 mul_m4_v3(tc->mat, iloc);
415 }
416 else if (t->options & CTX_OBJECT) {
418 copy_v3_v3(iloc, td->ob->object_to_world().location());
419 }
420
423 {
424 return false;
425 }
426
427 SnapObjectParams snap_object_params{};
428 snap_object_params.snap_target_select = t->tsnap.target_operation;
429 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
430 snap_object_params.occlusion_test = SNAP_OCCLUSION_ALWAYS;
431 snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0;
432
434 t->depsgraph,
435 t->region,
436 static_cast<const View3D *>(t->view),
438 &snap_object_params,
439 nullptr,
440 mval_fl,
441 nullptr,
442 nullptr,
443 loc,
444 no);
445 if (hit != SCE_SNAP_TO_FACE) {
446 return false;
447 }
448
449 float tvec[3];
450 sub_v3_v3v3(tvec, loc, iloc);
451
452 mul_m3_v3(td->smtx, tvec);
453
454 add_v3_v3(td->loc, tvec);
455
456 if ((t->tsnap.flag & SCE_SNAP_ROTATE) && (t->options & CTX_OBJECT)) {
457 /* Handle alignment as well. */
458 const float *original_normal;
459 float mat[3][3];
460
461 /* In pose mode, we want to align normals with Y axis of bones. */
462 original_normal = td->axismtx[2];
463
464 rotation_between_vecs_to_mat3(mat, original_normal, no);
465
466 transform_data_ext_rotate(td, mat, true);
467
468 /* TODO: support constraints for rotation too? see #ElementRotation. */
469 }
470 return true;
471}
472
474{
475 float init_loc[3];
476 float prev_loc[3];
477 float snap_loc[3], snap_no[3];
478
479 copy_v3_v3(init_loc, td->iloc);
480 copy_v3_v3(prev_loc, td->loc);
481 if (tc->use_local_mat) {
482 mul_m4_v3(tc->mat, init_loc);
483 mul_m4_v3(tc->mat, prev_loc);
484 }
485 else if (t->options & CTX_OBJECT) {
487 copy_v3_v3(init_loc, td->ob->object_to_world().location());
488 }
489
490 SnapObjectParams snap_object_params{};
491 snap_object_params.snap_target_select = t->tsnap.target_operation;
492 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
493 snap_object_params.occlusion_test = SNAP_OCCLUSION_ALWAYS;
494 snap_object_params.use_backface_culling = false;
495 snap_object_params.face_nearest_steps = t->tsnap.face_nearest_steps;
496 snap_object_params.keep_on_same_target = t->tsnap.flag & SCE_SNAP_KEEP_ON_SAME_OBJECT;
497
499 t->depsgraph,
500 t->region,
501 static_cast<const View3D *>(t->view),
503 &snap_object_params,
504 init_loc,
505 nullptr,
506 prev_loc,
507 nullptr,
508 snap_loc,
509 snap_no);
510
511 if (hit != SCE_SNAP_INDIVIDUAL_NEAREST) {
512 return;
513 }
514
515 float tvec[3];
516 sub_v3_v3v3(tvec, snap_loc, prev_loc);
517 mul_m3_v3(td->smtx, tvec);
518 add_v3_v3(td->loc, tvec);
519
520 /* TODO: support snap alignment similar to #SCE_SNAP_INDIVIDUAL_PROJECT? */
521}
522
524{
525 if (!transform_snap_is_active(t)) {
526 return false;
527 }
528
530}
531
533{
535 return;
536 }
537
538 /* XXX: flickers in object mode. */
540 TransData *td = tc->data;
541 for (int i = 0; i < tc->data_len; i++, td++) {
542 if (td->flag & TD_SKIP) {
543 continue;
544 }
545
546 if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) {
547 continue;
548 }
549
550 /* If both face ray-cast and face nearest methods are enabled, start with face ray-cast and
551 * fallback to face nearest ray-cast does not hit. */
552 bool hit = false;
554 hit = applyFaceProject(t, tc, td);
555 }
556
557 if (!hit && t->tsnap.mode & SCE_SNAP_INDIVIDUAL_NEAREST) {
558 applyFaceNearest(t, tc, td);
559 }
560#if 0 /* TODO: support this? */
561 constraintTransLim(t, td);
562#endif
563 }
564 }
565}
566
577
579{
581 return;
582 }
583
584 if (t->tsnap.mode != SCE_SNAP_TO_INCREMENT) {
585 double current = BLI_time_now_seconds();
586
587 /* Time base quirky code to go around find-nearest slowness. */
588 /* TODO: add exception for object mode, no need to slow it down then. */
589 if (current - t->tsnap.last >= 0.01) {
590 if (t->tsnap.snap_target_fn) {
591 t->tsnap.snap_target_fn(t, vec);
592 }
593 if (t->tsnap.snap_source_fn) {
594 t->tsnap.snap_source_fn(t);
595 }
596
597 t->tsnap.last = current;
598 }
599
600 if (validSnap(t)) {
601 t->mode_info->snap_apply_fn(t, vec);
602 }
603 }
604}
605
622
624{
625 return (t->tsnap.flag & SCE_SNAP_ROTATE) != 0;
626}
627
629{
630 if (validSnap(t)) {
631 if (!is_zero_v3(t->tsnap.snapNormal)) {
632 return true;
633 }
634 }
635
636 return false;
637}
638
639static bool bm_edge_is_snap_target(BMEdge *e, void * /*user_data*/)
640{
643 {
644 return false;
645 }
646
647 return true;
648}
649
650static bool bm_face_is_snap_target(BMFace *f, void * /*user_data*/)
651{
653 return false;
654 }
655
656 BMLoop *l_iter, *l_first;
657 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
658 do {
659 if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
660 return false;
661 }
662 } while ((l_iter = l_iter->next) != l_first);
663
664 return true;
665}
666
668{
669 ToolSettings *ts = t->settings;
670 switch (t->spacetype) {
671 case SPACE_VIEW3D:
672 if (r_prop) {
673 *r_prop = &rna_ToolSettings_use_snap;
674 }
675 return &ts->snap_flag;
676 case SPACE_NODE:
677 if (r_prop) {
678 *r_prop = &rna_ToolSettings_use_snap_node;
679 }
680 return &ts->snap_flag_node;
681 case SPACE_IMAGE:
682 if (r_prop) {
683 *r_prop = &rna_ToolSettings_use_snap_uv;
684 }
685 return &ts->snap_uv_flag;
686 case SPACE_SEQ:
687 if (r_prop) {
688 *r_prop = &rna_ToolSettings_use_snap_sequencer;
689 }
690 return &ts->snap_flag_seq;
691 case SPACE_GRAPH:
692 case SPACE_ACTION:
693 case SPACE_NLA:
694 if (r_prop) {
695 *r_prop = &rna_ToolSettings_use_snap_anim;
696 }
697 return &ts->snap_flag_anim;
698 }
699 /* #SPACE_EMPTY.
700 * It can happen when the operator is called via a handle in `bpy.app.handlers`. */
701 return nullptr;
702}
703
705{
706 if (short *snap_flag = transform_snap_flag_from_spacetype_ptr(t)) {
707 return eSnapFlag(*snap_flag);
708 }
709
710 /* #SPACE_EMPTY.
711 * It can happen when the operator is called via a handle in `bpy.app.handlers`. */
712 return eSnapFlag(0);
713}
714
716{
717 ToolSettings *ts = t->settings;
718
719 if (t->spacetype == SPACE_NODE) {
720 return eSnapMode(ts->snap_node_mode);
721 }
722
723 if (t->spacetype == SPACE_IMAGE) {
724 return eSnapMode(ts->snap_uv_mode);
725 }
726
727 if (t->spacetype == SPACE_SEQ) {
729 }
730
731 if (t->spacetype == SPACE_VIEW3D) {
734 }
735
736 return eSnapMode(ts->snap_mode);
737 }
738
740 return eSnapMode(ts->snap_anim_mode);
741 }
742
744}
745
747{
750
752
753 /* `t->tsnap.target_operation` not initialized yet. */
755
757 if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
758 /* Particles edit mode. */
759 return ret;
760 }
761
763 /* In "Edit Strokes" mode,
764 * snap tool can perform snap to selected or active objects (see #49632)
765 * TODO: perform self snap in gpencil_strokes.
766 *
767 * When we're moving the origins, allow snapping onto our own geometry (see #69132). */
768 return ret;
769 }
770
771 const int obedit_type = t->obedit_type;
772 if (obedit_type != -1) {
773 /* Edit mode. */
774 if (obedit_type == OB_MESH) {
775 /* Editing a mesh. */
776 if ((t->flag & T_PROP_EDIT) != 0) {
777 /* Exclude editmesh when using proportional edit. */
779 }
780 /* UV editing must never snap to the selection as this is what is transformed. */
781 if (t->spacetype == SPACE_IMAGE) {
783 }
784 }
785 else if (ELEM(obedit_type, OB_ARMATURE, OB_CURVES_LEGACY, OB_SURF, OB_LATTICE, OB_MBALL)) {
786 /* Temporary limited to edit mode armature, curves, surfaces, lattices, and metaballs. */
788 }
789 }
790 else {
791 /* Object or pose mode. */
793 }
794 }
795 else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
797 }
798
799 return ret;
800}
801
803{
804 if (t->data_type == &TransConvertType_Mesh) {
805 /* Ignore elements being transformed. */
808 (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
812 }
813 else {
814 /* Ignore hidden geometry in the general case. */
817 (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
818 (bool (*)(BMEdge *, void *))BM_elem_cb_check_hflag_disabled,
819 (bool (*)(BMFace *, void *))BM_elem_cb_check_hflag_disabled,
821 }
822}
823
825{
826 if (!transformModeUseSnap(t)) {
827 /* In this case, snapping is always disabled by default. */
828 t->modifiers &= ~MOD_SNAP;
829 }
830
831 if (doForceIncrementSnap(t)) {
833 }
834
835 if ((t->spacetype != SPACE_VIEW3D) || (t->flag & T_NO_PROJECT)) {
836 /* Force project off when not supported. */
838 }
839
842 }
843
845
846 if (t->spacetype == SPACE_VIEW3D) {
847 if (t->tsnap.object_context == nullptr) {
851 }
852 }
853 else if (t->spacetype == SPACE_SEQ) {
854 if (t->tsnap.seq_context == nullptr) {
856 }
857 }
858}
859
860void transform_snap_grid_init(const TransInfo *t, float r_snap[3], float *r_snap_precision)
861{
862 /* Default values. */
863 r_snap[0] = r_snap[1] = 1.0f;
864 r_snap[2] = 0.0f;
865 *r_snap_precision = 0.1f;
866
867 if (t->spacetype == SPACE_VIEW3D) {
868 /* Used by incremental snap. */
869 if (t->region->regiondata) {
870 View3D *v3d = static_cast<View3D *>(t->area->spacedata.first);
871 r_snap[0] = r_snap[1] = r_snap[2] = ED_view3d_grid_view_scale(
872 t->scene, v3d, t->region, nullptr);
873 }
874 }
875 else if (t->spacetype == SPACE_IMAGE) {
876 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
877 const View2D *v2d = &t->region->v2d;
878 int grid_size = SI_GRID_STEPS_LEN;
879 float zoom_factor = ED_space_image_zoom_level(v2d, grid_size);
880 float grid_steps_x[SI_GRID_STEPS_LEN];
881 float grid_steps_y[SI_GRID_STEPS_LEN];
882
883 ED_space_image_grid_steps(sima, grid_steps_x, grid_steps_y, grid_size);
884 /* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */
885 r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps_x, zoom_factor);
886 r_snap[1] = ED_space_image_increment_snap_value(grid_size, grid_steps_y, zoom_factor);
887 *r_snap_precision = 0.5f;
888 }
889 else if (t->spacetype == SPACE_CLIP) {
890 r_snap[0] = r_snap[1] = 0.125f;
891 *r_snap_precision = 0.5f;
892 }
893 else if (t->spacetype == SPACE_NODE) {
894 r_snap[0] = r_snap[1] = ED_node_grid_size();
895 }
896}
897
899{
900 ToolSettings *ts = t->settings;
901 eSnapSourceOP snap_source = eSnapSourceOP(ts->snap_target);
902
903 resetSnapping(t);
908
909 /* If snap property exists. */
910 PropertyRNA *prop;
911 if (op && (prop = RNA_struct_find_property(op->ptr, "snap")) &&
912 RNA_property_is_set(op->ptr, prop))
913 {
914 if (RNA_property_boolean_get(op->ptr, prop)) {
915 t->modifiers |= MOD_SNAP;
916
917 if ((prop = RNA_struct_find_property(op->ptr, "snap_elements")) &&
918 RNA_property_is_set(op->ptr, prop))
919 {
921 }
922
923 /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of
924 * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved
925 * geometry is snapped). */
926 if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) &&
927 RNA_property_is_set(op->ptr, prop))
928 {
929 snap_source = eSnapSourceOP(RNA_property_enum_get(op->ptr, prop));
930 }
931
932 if ((prop = RNA_struct_find_property(op->ptr, "snap_point")) &&
933 RNA_property_is_set(op->ptr, prop))
934 {
938 }
939
940 /* Snap align only defined in specific cases. */
941 if ((prop = RNA_struct_find_property(op->ptr, "snap_align")) &&
942 RNA_property_is_set(op->ptr, prop))
943 {
946
947 RNA_float_get_array(op->ptr, "snap_normal", t->tsnap.snapNormal);
949 }
950
951 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_project")) &&
952 RNA_property_is_set(op->ptr, prop))
953 {
956 }
957
958 /* Use_snap_self is misnamed and should be use_snap_active. */
959 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_self")) &&
960 RNA_property_is_set(op->ptr, prop))
961 {
963 !RNA_property_boolean_get(op->ptr, prop),
965 }
966
967 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_edit")) &&
968 RNA_property_is_set(op->ptr, prop))
969 {
971 !RNA_property_boolean_get(op->ptr, prop),
973 }
974
975 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonedit")) &&
976 RNA_property_is_set(op->ptr, prop))
977 {
979 !RNA_property_boolean_get(op->ptr, prop),
981 }
982
983 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) &&
984 RNA_property_is_set(op->ptr, prop))
985 {
987 RNA_property_boolean_get(op->ptr, prop),
989 }
990 }
991 }
992 /* Use scene defaults only when transform is modal. */
993 else if (t->flag & T_MODAL) {
994 if (t->tsnap.flag & SCE_SNAP) {
995 t->modifiers |= MOD_SNAP;
996 }
997
1010 }
1011
1012 t->tsnap.source_operation = snap_source;
1013
1017}
1018
1032
1034{
1035 if (t->spacetype == SPACE_VIEW3D) {
1038 }
1039 else {
1042 }
1043}
1044
1046{
1047 if (t->spacetype == SPACE_VIEW3D) {
1048 if (t->options & CTX_CAMERA) {
1049 /* Not with camera selected in camera view. */
1050 return;
1051 }
1053 }
1054 else if (t->spacetype == SPACE_IMAGE) {
1055 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
1058
1059 const bool is_uv_editor = sima->mode == SI_MODE_UV;
1060 const bool has_edit_object = obact && BKE_object_is_in_editmode(obact);
1061 if (is_uv_editor && has_edit_object) {
1063 }
1064 }
1065 else if (t->spacetype == SPACE_NODE) {
1067 }
1068 else if (t->spacetype == SPACE_SEQ) {
1070 /* The target is calculated along with the snap point. */
1071 return;
1072 }
1073 else if (t->spacetype == SPACE_NLA) {
1075 /* The target is calculated along with the snap point. */
1076 return;
1077 }
1078 else {
1079 return;
1080 }
1081
1082 switch (t->tsnap.source_operation) {
1085 break;
1087 if (!ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
1089 break;
1090 }
1091 /* Can't do TARGET_CENTER with these modes,
1092 * use TARGET_MEDIAN instead. */
1096 break;
1099
1100 /* XXX, workaround: active needs to be calculated before transforming, otherwise
1101 * `t->tsnap.snap_source` will be calculated with the transformed data since we're not
1102 * reading from 'td->center' in this case. (See: #40241 and #40348). */
1104 break;
1105 }
1106}
1107
1109{
1110 /* Currently only 3D viewport works for snapping points. */
1112 TransSnapPoint *p = MEM_cnew<TransSnapPoint>("SnapPoint");
1113
1114 t->tsnap.selectedPoint = p;
1115
1117
1118 BLI_addtail(&t->tsnap.points, p);
1119
1121 }
1122}
1123
1125{
1127
1128 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1129 TransSnapPoint *closest_p = nullptr;
1130 float dist_min_sq = TRANSFORM_SNAP_MAX_PX;
1131 float screen_loc[2];
1132
1134 float dist_sq;
1135
1136 if (ED_view3d_project_float_global(t->region, p->co, screen_loc, V3D_PROJ_TEST_NOP) !=
1138 {
1139 continue;
1140 }
1141
1142 dist_sq = len_squared_v2v2(t->mval, screen_loc);
1143
1144 if (dist_sq < dist_min_sq) {
1145 closest_p = p;
1146 dist_min_sq = dist_sq;
1147 }
1148 }
1149
1150 if (closest_p) {
1151 if (t->tsnap.selectedPoint != closest_p) {
1152 status = TREDRAW_HARD;
1153 }
1154
1155 t->tsnap.selectedPoint = closest_p;
1156 }
1157 }
1158
1159 return status;
1160}
1161
1163{
1164 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1166
1167 if (t->tsnap.selectedPoint) {
1169
1171 t->tsnap.status &= ~SNAP_MULTI_POINTS;
1172 }
1173
1174 t->tsnap.selectedPoint = nullptr;
1175 }
1176 }
1177}
1178
1179void getSnapPoint(const TransInfo *t, float vec[3])
1180{
1181 if (t->tsnap.points.first) {
1182 TransSnapPoint *p;
1183 int total = 0;
1184
1185 vec[0] = vec[1] = vec[2] = 0;
1186
1187 for (p = static_cast<TransSnapPoint *>(t->tsnap.points.first); p; p = p->next, total++) {
1188 add_v3_v3(vec, p->co);
1189 }
1190
1191 if (t->tsnap.status & SNAP_TARGET_FOUND) {
1192 add_v3_v3(vec, t->tsnap.snap_target);
1193 total++;
1194 }
1195
1196 mul_v3_fl(vec, 1.0f / total);
1197 }
1198 else {
1199 copy_v3_v3(vec, t->tsnap.snap_target);
1200 }
1201}
1202
1204{
1205 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1207 t->tsnap.status &= ~SNAP_MULTI_POINTS;
1208 t->tsnap.selectedPoint = nullptr;
1209 }
1210}
1211
1214/* -------------------------------------------------------------------- */
1218static void snap_grid_uv_apply(TransInfo *t, const float grid_dist[2], float r_out[2])
1219{
1220 float3 in;
1221 convertViewVec(t, in, t->mval[0] - t->center2d[0], t->mval[1] - t->center2d[1]);
1222
1223 if (t->con.mode & CON_APPLY) {
1224 /* We need to clear the previous Snap to Grid result,
1225 * otherwise #t->con.applyVec will have no effect. */
1227 t->tsnap.status &= ~SNAP_TARGET_FOUND;
1229 }
1230
1231 const float *center_global = t->center_global;
1232 for (int i = 0; i < 2; i++) {
1233 const float iter_fac = grid_dist[i];
1234 r_out[i] = iter_fac * roundf((in[i] + center_global[i]) / iter_fac);
1235 }
1236}
1237
1238static bool snap_grid_uv(TransInfo *t, float r_val[2])
1239{
1240 float grid_dist[2];
1241 mul_v2_v2v2(grid_dist, t->snap_spatial, t->aspect);
1242 if (t->modifiers & MOD_PRECISION) {
1243 mul_v2_fl(grid_dist, t->snap_spatial_precision);
1244 }
1245
1246 /* Early bailing out if no need to snap */
1247 if (is_zero_v2(grid_dist)) {
1248 return false;
1249 }
1250
1251 snap_grid_uv_apply(t, grid_dist, r_val);
1253 return true;
1254}
1255
1258/* -------------------------------------------------------------------- */
1262static void snap_target_view3d_fn(TransInfo *t, float * /*vec*/)
1263{
1265 float loc[3];
1266 float no[3];
1267 bool found = false;
1268 eSnapMode snap_elem = SCE_SNAP_TO_NONE;
1269 float dist_px = SNAP_MIN_DISTANCE; /* Use a user defined value here. */
1270
1272 zero_v3(no); /* objects won't set this */
1273 snap_elem = snapObjectsTransform(t, t->mval, &dist_px, loc, no);
1274 found = (snap_elem != SCE_SNAP_TO_NONE);
1275 }
1276 if ((found == false) && (t->tsnap.mode & SCE_SNAP_TO_VOLUME)) {
1277 bool use_peel = (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0;
1278 found = peelObjectsTransform(t, t->mval, use_peel, loc, no, nullptr);
1279
1280 if (found) {
1281 snap_elem = SCE_SNAP_TO_VOLUME;
1282 }
1283 }
1284
1285 if (found == true) {
1286 copy_v3_v3(t->tsnap.snap_target, loc);
1287 copy_v3_v3(t->tsnap.snapNormal, no);
1288
1290
1291 if (snap_elem == SCE_SNAP_TO_GRID && t->mode_info != &TransMode_translate) {
1292 /* Change it to #SCE_SNAP_TO_POINT so we can see the symbol for other modes. */
1293 snap_elem = SCE_SNAP_TO_POINT;
1294 }
1295 }
1296 else {
1297 t->tsnap.status &= ~SNAP_TARGET_FOUND;
1298 }
1299
1300 t->tsnap.target_type = snap_elem;
1301}
1302
1303static void snap_target_uv_fn(TransInfo *t, float * /*vec*/)
1304{
1306 bool found = false;
1307 if (t->tsnap.mode & SCE_SNAP_TO_VERTEX) {
1308 const Vector<Object *> objects =
1310 t->scene, t->view_layer, nullptr);
1311
1312 float dist_sq = square_f(float(SNAP_MIN_DISTANCE));
1314 t->scene,
1315 objects,
1316 t->mval,
1318 &dist_sq,
1319 t->tsnap.snap_target))
1320 {
1321 t->tsnap.snap_target[0] *= t->aspect[0];
1322 t->tsnap.snap_target[1] *= t->aspect[1];
1324 found = true;
1325 }
1326 }
1327
1328 if (!found && (t->tsnap.mode & SCE_SNAP_TO_GRID)) {
1329 found = snap_grid_uv(t, t->tsnap.snap_target);
1330 }
1331
1333}
1334
1335static void snap_target_node_fn(TransInfo *t, float * /*vec*/)
1336{
1339 float loc[2];
1340 float dist_px = SNAP_MIN_DISTANCE; /* Use a user defined value here. */
1341 char node_border;
1342
1343 if (snapNodesTransform(t, t->mval, loc, &dist_px, &node_border)) {
1344 copy_v2_v2(t->tsnap.snap_target, loc);
1345 t->tsnap.snapNodeBorder = node_border;
1346
1348 }
1349 else {
1350 t->tsnap.status &= ~SNAP_TARGET_FOUND;
1351 }
1352 }
1353}
1354
1355static void snap_target_sequencer_fn(TransInfo *t, float * /*vec*/)
1356{
1360 }
1361 else {
1363 }
1364}
1365
1366static void snap_target_nla_fn(TransInfo *t, float *vec)
1367{
1369 if (transform_snap_nla_calc(t, vec)) {
1371 }
1372 else {
1374 }
1375}
1376
1379/* -------------------------------------------------------------------- */
1383void tranform_snap_target_median_calc(const TransInfo *t, float r_median[3])
1384{
1385 int i_accum = 0;
1386
1387 zero_v3(r_median);
1388
1390 TransData *td = tc->data;
1391 int i;
1392 float v[3];
1393 zero_v3(v);
1394
1395 for (i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
1396 add_v3_v3(v, td->center);
1397 }
1398
1399 if (i == 0) {
1400 /* Is this possible? */
1401 continue;
1402 }
1403
1404 mul_v3_fl(v, 1.0 / i);
1405
1406 if (tc->use_local_mat) {
1407 mul_m4_v3(tc->mat, v);
1408 }
1409
1410 add_v3_v3(r_median, v);
1411 i_accum++;
1412 }
1413
1414 mul_v3_fl(r_median, 1.0 / i_accum);
1415
1416 // TargetSnapOffset(t, nullptr);
1417}
1418
1420{
1421 if (t->spacetype == SPACE_NODE && td != nullptr) {
1422 bNode *node = static_cast<bNode *>(td->extra);
1423 char border = t->tsnap.snapNodeBorder;
1424
1425 if (border & NODE_LEFT) {
1426 t->tsnap.snap_source[0] -= 0.0f;
1427 }
1428 if (border & NODE_RIGHT) {
1429 t->tsnap.snap_source[0] += BLI_rctf_size_x(&node->runtime->totr);
1430 }
1431 if (border & NODE_BOTTOM) {
1432 t->tsnap.snap_source[1] -= BLI_rctf_size_y(&node->runtime->totr);
1433 }
1434 if (border & NODE_TOP) {
1435 t->tsnap.snap_source[1] += 0.0f;
1436 }
1437 }
1438}
1439
1441{
1442 /* Only need to calculate once. */
1443 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1445 TargetSnapOffset(t, nullptr);
1446
1449 }
1450}
1451
1453{
1454 /* Only need to calculate once. */
1455 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1456 if (calculateCenterActive(t, true, t->tsnap.snap_source)) {
1457 TargetSnapOffset(t, nullptr);
1460 }
1461 else {
1462 /* No active, default to median, */
1466 }
1467 }
1468}
1469
1471{
1472 /* Only need to calculate once. */
1473 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1477 }
1478}
1479
1481{
1482 /* Only valid if a snap point has been selected. */
1483 if (!(t->tsnap.status & SNAP_TARGET_FOUND)) {
1484 return;
1485 }
1486
1488 /* Previously Snap to Grid had its own snap source which was always the result of
1489 * #snap_source_median_fn. Now this mode shares the same code, so to not change the behavior
1490 * too much when using Closest, use the transform pivot as the snap source in this case. */
1493 /* Use #SCE_SNAP_TO_POINT to differentiate from 'Closest' bounds and thus avoid recalculating
1494 * the median center. */
1496 }
1497 }
1498 else {
1499 float dist_closest = 0.0f;
1500 TransData *closest = nullptr;
1501
1502 /* Object mode. */
1503 if (t->options & CTX_OBJECT) {
1504 int i;
1506 TransData *td;
1507 for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
1508 std::optional<blender::Bounds<blender::float3>> bounds;
1509
1510 if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
1512 }
1513
1514 /* Use bound-box if possible. */
1515 if (bounds) {
1516 BoundBox bb;
1517 BKE_boundbox_init_from_minmax(&bb, bounds->min, bounds->max);
1518 int j;
1519
1520 for (j = 0; j < 8; j++) {
1521 float loc[3];
1522 float dist;
1523
1524 copy_v3_v3(loc, bb.vec[j]);
1525 mul_m4_v3(td->ext->obmat, loc);
1526
1527 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1528
1529 if ((dist != TRANSFORM_DIST_INVALID) &&
1530 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1531 {
1532 copy_v3_v3(t->tsnap.snap_source, loc);
1533 closest = td;
1534 dist_closest = dist;
1535 }
1536 }
1537 }
1538 /* Use element center otherwise. */
1539 else {
1540 float loc[3];
1541 float dist;
1542
1543 copy_v3_v3(loc, td->center);
1544
1545 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1546
1547 if ((dist != TRANSFORM_DIST_INVALID) &&
1548 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1549 {
1550 copy_v3_v3(t->tsnap.snap_source, loc);
1551 closest = td;
1552 }
1553 }
1554 }
1555 }
1556 }
1557 else {
1559 TransData *td = tc->data;
1560 int i;
1561 for (i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) {
1562 float loc[3];
1563 float dist;
1564
1565 copy_v3_v3(loc, td->center);
1566
1567 if (tc->use_local_mat) {
1568 mul_m4_v3(tc->mat, loc);
1569 }
1570
1571 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1572
1573 if ((dist != TRANSFORM_DIST_INVALID) &&
1574 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1575 {
1576 copy_v3_v3(t->tsnap.snap_source, loc);
1577 closest = td;
1578 dist_closest = dist;
1579 }
1580 }
1581 }
1582 }
1583
1584 TargetSnapOffset(t, closest);
1586 }
1587
1589}
1590
1593/* -------------------------------------------------------------------- */
1598 TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
1599{
1600 SnapObjectParams snap_object_params{};
1601 snap_object_params.snap_target_select = t->tsnap.target_operation;
1602 snap_object_params.grid_size = (t->modifiers & MOD_PRECISION) ?
1604 t->snap_spatial[0];
1605 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
1606 snap_object_params.occlusion_test = SNAP_OCCLUSION_AS_SEEM;
1607 snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0;
1608
1609 float *prev_co = (t->tsnap.status & SNAP_SOURCE_FOUND) ? t->tsnap.snap_source : t->center_global;
1610 float *grid_co = nullptr, grid_co_stack[3];
1611 if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && (t->con.mode & CON_APPLY) && t->mode != TFM_ROTATION) {
1612 /* Without this position adjustment, the snap may be far from the expected constraint point. */
1613 grid_co = grid_co_stack;
1614 convertViewVec(t, grid_co, mval[0] - t->center2d[0], mval[1] - t->center2d[1]);
1615 t->tsnap.status &= ~SNAP_TARGET_FOUND;
1616 transform_constraint_get_nearest(t, grid_co, grid_co);
1617 add_v3_v3(grid_co, t->center_global);
1618 }
1619
1621 t->depsgraph,
1622 t->region,
1623 static_cast<const View3D *>(t->view),
1624 t->tsnap.mode,
1625 &snap_object_params,
1626 grid_co,
1627 mval,
1628 prev_co,
1629 dist_px,
1630 r_loc,
1631 r_no);
1632}
1633
1636/* -------------------------------------------------------------------- */
1641 const float mval[2],
1642 const bool use_peel_object,
1643 /* Return args. */
1644 float r_loc[3],
1645 float r_no[3],
1646 float *r_thickness)
1647{
1648 SnapObjectParams snap_object_params{};
1649 snap_object_params.snap_target_select = t->tsnap.target_operation;
1650 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
1651
1652 ListBase depths_peel = {nullptr};
1654 t->depsgraph,
1655 t->region,
1656 static_cast<const View3D *>(t->view),
1657 &snap_object_params,
1658 mval,
1659 -1.0f,
1660 false,
1661 &depths_peel);
1662
1663 if (!BLI_listbase_is_empty(&depths_peel)) {
1664 /* At the moment we only use the hits of the first object. */
1665 SnapObjectHitDepth *hit_min = static_cast<SnapObjectHitDepth *>(depths_peel.first);
1666 for (SnapObjectHitDepth *iter = hit_min->next; iter; iter = iter->next) {
1667 if (iter->depth < hit_min->depth) {
1668 hit_min = iter;
1669 }
1670 }
1671 SnapObjectHitDepth *hit_max = nullptr;
1672
1673 if (use_peel_object) {
1674 /* If peeling objects, take the first and last from each object. */
1675 hit_max = hit_min;
1676 LISTBASE_FOREACH (SnapObjectHitDepth *, iter, &depths_peel) {
1677 if ((iter->depth > hit_max->depth) && (iter->ob_uuid == hit_min->ob_uuid)) {
1678 hit_max = iter;
1679 }
1680 }
1681 }
1682 else {
1683 /* Otherwise, pair first with second and so on. */
1684 LISTBASE_FOREACH (SnapObjectHitDepth *, iter, &depths_peel) {
1685 if ((iter != hit_min) && (iter->ob_uuid == hit_min->ob_uuid)) {
1686 if (hit_max == nullptr) {
1687 hit_max = iter;
1688 }
1689 else if (iter->depth < hit_max->depth) {
1690 hit_max = iter;
1691 }
1692 }
1693 }
1694 /* In this case has only one hit. treat as ray-cast. */
1695 if (hit_max == nullptr) {
1696 hit_max = hit_min;
1697 }
1698 }
1699
1700 mid_v3_v3v3(r_loc, hit_min->co, hit_max->co);
1701
1702 if (r_thickness) {
1703 *r_thickness = hit_max->depth - hit_min->depth;
1704 }
1705
1706 /* XXX, is there a correct normal in this case ???, for now just z up. */
1707 r_no[0] = 0.0;
1708 r_no[1] = 0.0;
1709 r_no[2] = 1.0;
1710
1711 BLI_freelistN(&depths_peel);
1712 return true;
1713 }
1714 return false;
1715}
1716
1719/* -------------------------------------------------------------------- */
1723static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetOP snap_target_select)
1724{
1725 /* Node is use for snapping only if a) snap mode matches and b) node is inside the view. */
1726 return (((snap_target_select & SCE_SNAP_TARGET_NOT_SELECTED) && !(node->flag & NODE_SELECT)) ||
1727 (snap_target_select == SCE_SNAP_TARGET_ALL && !(node->flag & NODE_ACTIVE))) &&
1728 (node->runtime->totr.xmin < v2d->cur.xmax && node->runtime->totr.xmax > v2d->cur.xmin &&
1729 node->runtime->totr.ymin < v2d->cur.ymax && node->runtime->totr.ymax > v2d->cur.ymin);
1730}
1731
1733{
1735 if (snap_node_mode & SCE_SNAP_TO_NODE_X) {
1737 }
1738 if (snap_node_mode & SCE_SNAP_TO_NODE_Y) {
1740 }
1741 return flag;
1742}
1743
1744static bool snapNode(ToolSettings *ts,
1745 SpaceNode * /*snode*/,
1746 ARegion *region,
1747 bNode *node,
1748 const float2 &mval,
1749 float r_loc[2],
1750 float *r_dist_px,
1751 char *r_node_border)
1752{
1753 View2D *v2d = &region->v2d;
1755 bool retval = false;
1756 rcti totr;
1757 int new_dist;
1758
1759 UI_view2d_view_to_region_rcti(v2d, &node->runtime->totr, &totr);
1760
1761 if (border & NODE_LEFT) {
1762 new_dist = abs(totr.xmin - mval[0]);
1763 if (new_dist < *r_dist_px) {
1764 UI_view2d_region_to_view(v2d, totr.xmin, mval[1], &r_loc[0], &r_loc[1]);
1765 *r_dist_px = new_dist;
1766 *r_node_border = NODE_LEFT;
1767 retval = true;
1768 }
1769 }
1770
1771 if (border & NODE_RIGHT) {
1772 new_dist = abs(totr.xmax - mval[0]);
1773 if (new_dist < *r_dist_px) {
1774 UI_view2d_region_to_view(v2d, totr.xmax, mval[1], &r_loc[0], &r_loc[1]);
1775 *r_dist_px = new_dist;
1776 *r_node_border = NODE_RIGHT;
1777 retval = true;
1778 }
1779 }
1780
1781 if (border & NODE_BOTTOM) {
1782 new_dist = abs(totr.ymin - mval[1]);
1783 if (new_dist < *r_dist_px) {
1784 UI_view2d_region_to_view(v2d, mval[0], totr.ymin, &r_loc[0], &r_loc[1]);
1785 *r_dist_px = new_dist;
1786 *r_node_border = NODE_BOTTOM;
1787 retval = true;
1788 }
1789 }
1790
1791 if (border & NODE_TOP) {
1792 new_dist = abs(totr.ymax - mval[1]);
1793 if (new_dist < *r_dist_px) {
1794 UI_view2d_region_to_view(v2d, mval[0], totr.ymax, &r_loc[0], &r_loc[1]);
1795 *r_dist_px = new_dist;
1796 *r_node_border = NODE_TOP;
1797 retval = true;
1798 }
1799 }
1800
1801 return retval;
1802}
1803
1804static bool snapNodes(ToolSettings *ts,
1805 SpaceNode *snode,
1806 ARegion *region,
1807 const float2 &mval,
1808 eSnapTargetOP snap_target_select,
1809 float r_loc[2],
1810 float *r_dist_px,
1811 char *r_node_border)
1812{
1813 bNodeTree *ntree = snode->edittree;
1814 bool retval = false;
1815
1816 *r_node_border = 0;
1817
1818 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1819 if (snapNodeTest(&region->v2d, node, snap_target_select)) {
1820 retval |= snapNode(ts, snode, region, node, mval, r_loc, r_dist_px, r_node_border);
1821 }
1822 }
1823
1824 return retval;
1825}
1826
1828 TransInfo *t, const float2 &mval, float r_loc[2], float *r_dist_px, char *r_node_border)
1829{
1830 return snapNodes(t->settings,
1831 static_cast<SpaceNode *>(t->area->spacedata.first),
1832 t->region,
1833 mval,
1835 r_loc,
1836 r_dist_px,
1837 r_node_border);
1838}
1839
1842/* -------------------------------------------------------------------- */
1846static void snap_increment_apply_ex(const TransInfo * /*t*/,
1847 const int max_index,
1848 const float increment_val,
1849 const float aspect[3],
1850 const float loc[3],
1851 float r_out[3])
1852{
1853 /* Relative snapping in fixed increments. */
1854 for (int i = 0; i <= max_index; i++) {
1855 const float iter_fac = increment_val * aspect[i];
1856 r_out[i] = iter_fac * roundf(loc[i] / iter_fac);
1857 }
1858}
1859
1860static void snap_increment_apply(const TransInfo *t,
1861 const int max_index,
1862 const float increment_dist,
1863 float *r_val)
1864{
1866 BLI_assert(max_index <= 2);
1867
1868 /* Early bailing out if no need to snap. */
1869 if (increment_dist == 0.0f) {
1870 return;
1871 }
1872
1873 float asp_local[3] = {1, 1, 1};
1874 const bool use_aspect = ELEM(t->mode, TFM_TRANSLATION);
1875 const float *asp = use_aspect ? t->aspect : asp_local;
1876
1877 if (use_aspect) {
1878 /* Custom aspect for fcurve. */
1879 if (t->spacetype == SPACE_GRAPH) {
1880 View2D *v2d = &t->region->v2d;
1881 Scene *scene = t->scene;
1882 SpaceGraph *sipo = static_cast<SpaceGraph *>(t->area->spacedata.first);
1884 v2d, scene, sipo->flag & SIPO_DRAWTIME);
1885 asp_local[1] = UI_view2d_grid_resolution_y__values(v2d);
1886 asp = asp_local;
1887 }
1888 }
1889
1890 snap_increment_apply_ex(t, max_index, increment_dist, asp, r_val, r_val);
1891}
1892
1893bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float *r_val)
1894{
1895 if (!transform_snap_is_active(t)) {
1896 return false;
1897 }
1898
1899 if (!(t->tsnap.mode & SCE_SNAP_TO_INCREMENT)) {
1900 return false;
1901 }
1902
1903 if (t->spacetype != SPACE_VIEW3D && validSnap(t)) {
1904 /* Only do something if using absolute or incremental grid snapping
1905 * and there is no valid snap point. */
1906 return false;
1907 }
1908
1909 if (use_local_space) {
1910 mul_m3_v3(t->spacemtx_inv, r_val);
1911 }
1912
1913 float increment_dist = (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0];
1914 snap_increment_apply(t, t->idx_max, increment_dist, r_val);
1915
1916 if (use_local_space) {
1917 mul_m3_v3(t->spacemtx, r_val);
1918 }
1919
1920 return true;
1921}
1922
1923bool transform_snap_increment(const TransInfo *t, float *r_val)
1924{
1925 return transform_snap_increment_ex(t, false, r_val);
1926}
1927
1929{
1931 {
1932 return (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0];
1933 }
1934
1935 return 0.0f;
1936}
1937
1945
1948/* -------------------------------------------------------------------- */
1953 const float p1[3],
1954 const float p2[3])
1955{
1956 return len_squared_v3v3(p1, p2);
1957}
1958
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_boundbox_init_from_minmax(BoundBox *bb, const float min[3], const float max[3])
void BKE_object_eval_transform_all(Depsgraph *depsgraph, Scene *scene, Object *object)
bool BKE_scene_uses_blender_workbench(const Scene *scene)
Definition scene.cc:2777
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ATTR_FALLTHROUGH
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
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 copy_v2_v2(float r[2], const float a[2])
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:189
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
unsigned char uchar
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define UNPACK2(a)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define POINTER_FROM_UINT(i)
@ NODE_ACTIVE
@ NODE_SELECT
@ OB_SOLID
@ OB_RENDER
@ OB_MODE_PARTICLE_EDIT
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_ARMATURE
@ OB_MESH
@ OB_CURVES_LEGACY
#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_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_NODE_X
@ 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_VOLUME
@ SCE_SNAP_TO_EDGE_PERPENDICULAR
@ SCE_SNAP_TO_NODE_Y
@ SCE_SNAP_TO_POINT
@ SCE_SNAP_TO_NONE
@ RGN_TYPE_PREVIEW
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_NODE
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ SIPO_DRAWTIME
#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)
void ED_node_draw_snap(View2D *v2d, const float cent[2], float size, NodeBorder border, unsigned int pos)
Definition drawnode.cc:2412
float ED_node_grid_size()
Definition node_draw.cc:142
NodeBorder
Definition ED_node_c.hh:27
@ NODE_LEFT
Definition ED_node_c.hh:30
@ NODE_RIGHT
Definition ED_node_c.hh:31
@ NODE_BOTTOM
Definition ED_node_c.hh:29
@ NODE_TOP
Definition ED_node_c.hh:28
@ TFM_RESIZE
@ TFM_EDGE_SLIDE
@ TFM_TIME_TRANSLATE
@ TFM_VERT_SLIDE
@ TFM_ROTATION
@ TFM_TIME_EXTEND
@ TFM_SEQ_SLIDE
@ TFM_TRANSLATION
void ED_transform_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)
bool ED_transform_snap_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)
#define ED_transform_snap_object_time_average_print()
SnapObjectContext * ED_transform_snap_object_context_create(Scene *scene, int flag)
void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx)
eSnapMode ED_transform_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])
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:266
float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3])
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:243
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 immUniformColor3ub(unsigned char r, unsigned char g, unsigned char b)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
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_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
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:111
@ GPU_DEPTH_NONE
Definition GPU_state.hh:108
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ 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)
float UI_view2d_grid_resolution_x__frames_or_seconds(const View2D *v2d, const Scene *scene, bool display_seconds)
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:1734
float UI_view2d_grid_resolution_y__values(const View2D *v2d)
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1781
@ KM_SHIFT
Definition WM_types.hh:255
bool BM_elem_cb_check_hflag_disabled(BMElem *ele, void *user_data)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
#define BM_FACE_FIRST_LOOP(p)
#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
unsigned int U
Definition btGjkEpa3.h:78
#define fabsf(x)
void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy)
uint col
return ret
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)
short SEQ_tool_settings_snap_mode_get(Scene *scene)
Definition sequencer.cc:380
void * regiondata
struct BMVert * v
struct BMLoop * next
struct Object * object
float vec[8][3]
float3 min
Definition boundbox.h:21
void * first
float viewinv[4][4]
View3DShading shading
struct SceneDisplay display
ListBase spacedata
struct bNodeTree * edittree
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
eTConstraint mode
Definition transform.hh:351
float mat[4][4]
Definition transform.hh:462
float smtx[3][3]
float axismtx[3][3]
TransDataExtension * ext
float snap_spatial_precision
Definition transform.hh:568
eTfmMode mode
Definition transform.hh:517
TransModeInfo * mode_info
Definition transform.hh:518
void * view
Definition transform.hh:647
char spacetype
Definition transform.hh:582
float snap[2]
Definition transform.hh:561
float spacemtx_inv[3][3]
Definition transform.hh:593
float snap_spatial[3]
Definition transform.hh:563
float center2d[2]
Definition transform.hh:557
TransSnap tsnap
Definition transform.hh:537
short idx_max
Definition transform.hh:559
ToolSettings * settings
Definition transform.hh:656
eTModifier modifiers
Definition transform.hh:525
float aspect[3]
Definition transform.hh:553
Scene * scene
Definition transform.hh:654
ViewLayer * view_layer
Definition transform.hh:655
eTFlag flag
Definition transform.hh:523
ARegion * region
Definition transform.hh:652
short obedit_type
Definition transform.hh:584
Depsgraph * depsgraph
Definition transform.hh:653
TransCon con
Definition transform.hh:534
float center_global[3]
Definition transform.hh:555
blender::float2 mval
Definition transform.hh:663
float spacemtx[3][3]
Definition transform.hh:592
TransConvertTypeInfo * data_type
Definition transform.hh:513
eTContext options
Definition transform.hh:521
ScrArea * area
Definition transform.hh:651
float(* snap_distance_fn)(TransInfo *t, const float p1[3], const float p2[3])
void(* snap_apply_fn)(TransInfo *, float *)
TransSnapPoint * next
Definition transform.hh:304
TransSeqSnapData * seq_context
Definition transform.hh:341
void(* snap_target_fn)(TransInfo *, float *)
Definition transform.hh:333
float snapNormal[3]
Definition transform.hh:328
eSnapFlag flag
Definition transform.hh:310
TransSnapPoint * selectedPoint
Definition transform.hh:331
eTSnap status
Definition transform.hh:318
ListBase points
Definition transform.hh:330
SnapObjectContext * object_context
Definition transform.hh:340
float snap_target[3]
Definition transform.hh:327
eSnapSourceOP source_operation
Definition transform.hh:314
char snapNodeBorder
Definition transform.hh:329
float snap_source[3]
Definition transform.hh:325
void(* snap_source_fn)(TransInfo *)
Definition transform.hh:334
eSnapMode mode
Definition transform.hh:312
double last
Definition transform.hh:332
short face_nearest_steps
Definition transform.hh:317
eSnapMode target_type
Definition transform.hh:321
eSnapMode source_type
Definition transform.hh:320
eSnapTargetOP target_operation
Definition transform.hh:316
eSnapDir direction
Definition transform.hh:323
View3DShading shading
ListBase nodes
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
uint8_t modifier
Definition WM_types.hh:739
short type
Definition WM_types.hh:722
struct PointerRNA * ptr
bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3])
void transform_data_ext_rotate(TransData *td, float mat[3][3], bool use_drot)
@ SNAP_TARGET_FOUND
Definition transform.hh:177
@ SNAP_RESETTED
Definition transform.hh:174
@ SNAP_SOURCE_FOUND
Definition transform.hh:175
@ SNAP_MULTI_POINTS
Definition transform.hh:178
@ CON_APPLY
Definition transform.hh:193
@ CTX_PAINT_CURVE
Definition transform.hh:72
@ CTX_CURSOR
Definition transform.hh:66
@ CTX_OBMODE_XFORM_OBDATA
Definition transform.hh:80
@ CTX_OBJECT
Definition transform.hh:71
@ CTX_EDGE_DATA
Definition transform.hh:67
@ CTX_GPENCIL_STROKES
Definition transform.hh:68
@ CTX_CAMERA
Definition transform.hh:65
@ MOD_SNAP_INVERT
Definition transform.hh:164
@ MOD_EDIT_SNAP_SOURCE
Definition transform.hh:168
@ MOD_PRECISION
Definition transform.hh:162
@ MOD_SNAP_FORCED
Definition transform.hh:167
@ MOD_SNAP
Definition transform.hh:163
eRedrawFlag
Definition transform.hh:214
@ TREDRAW_NOTHING
Definition transform.hh:215
@ TREDRAW_HARD
Definition transform.hh:217
@ DIR_GLOBAL_Y
Definition transform.hh:185
@ DIR_GLOBAL_X
Definition transform.hh:184
@ T_MODAL
Definition transform.hh:118
@ T_NO_PROJECT
Definition transform.hh:121
@ T_PROP_EDIT
Definition transform.hh:98
@ T_DRAW_SNAP_SOURCE
Definition transform.hh:149
@ T_EDIT
Definition transform.hh:91
#define TRANSFORM_SNAP_MAX_PX
Definition transform.hh:844
#define TRANSFORM_DIST_INVALID
Definition transform.hh:845
#define FOREACH_TRANS_DATA_CONTAINER(t, th)
Definition transform.hh:854
void transform_constraint_get_nearest(const TransInfo *t, const float3 &vec, float r_vec[3])
conversion and adaptation of different datablocks to a common struct.
TransConvertTypeInfo TransConvertType_Mesh
@ TD_SELECTED
@ TD_SKIP
void constraintTransLim(const TransInfo *t, const TransDataContainer *tc, TransData *td)
transform modes used by different operators.
TransModeInfo TransMode_translate
bool transformModeUseSnap(const TransInfo *t)
bool usingSnappingNormal(const TransInfo *t)
static void snap_multipoints_free(TransInfo *t)
void transform_snap_project_individual_apply(TransInfo *t)
void tranform_snap_source_restore_context(TransInfo *t)
void transform_snap_mixed_apply(TransInfo *t, float *vec)
static void snap_source_closest_fn(TransInfo *t)
void addSnapPoint(TransInfo *t)
bool validSnap(const TransInfo *t)
bool transform_snap_increment(const TransInfo *t, float *r_val)
void freeSnapping(TransInfo *t)
static void snap_increment_apply(const TransInfo *t, const int max_index, const float increment_dist, float *r_val)
static eSnapMode snap_mode_from_spacetype(TransInfo *t)
static void snap_target_nla_fn(TransInfo *t, float *vec)
bool transform_snap_is_active(const TransInfo *t)
static bool transform_snap_mixed_is_active(const TransInfo *t)
eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event)
static void setSnappingCallback(TransInfo *t)
static bool bm_edge_is_snap_target(BMEdge *e, void *)
bool snapNodesTransform(TransInfo *t, const float2 &mval, float r_loc[2], float *r_dist_px, char *r_node_border)
void removeSnapPoint(TransInfo *t)
void initSnapAngleIncrements(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)
static void snap_target_view3d_fn(TransInfo *t, float *vec)
float transform_snap_distance_len_squared_fn(TransInfo *, const float p1[3], const float p2[3])
static void snap_source_center_fn(TransInfo *t)
static bool snapNodeTest(View2D *v2d, bNode *node, eSnapTargetOP snap_target_select)
static void snap_target_node_fn(TransInfo *t, float *vec)
static void TargetSnapOffset(TransInfo *t, TransData *td)
static void applyFaceNearest(TransInfo *t, TransDataContainer *tc, TransData *td)
void getSnapPoint(const TransInfo *t, float vec[3])
static void snap_increment_apply_ex(const TransInfo *, const int max_index, const float increment_val, const float aspect[3], const float loc[3], float r_out[3])
static NodeBorder snapNodeBorder(eSnapMode snap_node_mode)
void transform_snap_flag_from_modifiers_set(TransInfo *t)
static bool doForceIncrementSnap(const TransInfo *t)
short * transform_snap_flag_from_spacetype_ptr(TransInfo *t, const PropertyRNA **r_prop=nullptr)
eRedrawFlag updateSelectedSnapPoint(TransInfo *t)
float transform_snap_increment_get(const TransInfo *t)
bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float *r_val)
static void snap_target_uv_fn(TransInfo *t, float *vec)
static void snap_source_median_fn(TransInfo *t)
static bool snap_use_backface_culling(const TransInfo *t)
static bool bm_face_is_snap_target(BMFace *f, void *)
static bool applyFaceProject(TransInfo *t, TransDataContainer *tc, TransData *td)
static void snap_grid_uv_apply(TransInfo *t, const float grid_dist[2], float r_out[2])
void initSnapping(TransInfo *t, wmOperator *op)
bool transform_snap_project_individual_is_active(const TransInfo *t)
static void snap_target_sequencer_fn(TransInfo *t, float *vec)
static bool snapNode(ToolSettings *ts, SpaceNode *, ARegion *region, bNode *node, const float2 &mval, float r_loc[2], float *r_dist_px, char *r_node_border)
bool validSnappingNormal(const TransInfo *t)
static void initSnappingMode(TransInfo *t)
void tranform_snap_target_median_calc(const TransInfo *t, float r_median[3])
static eSnapTargetOP snap_target_select_from_spacetype(TransInfo *t)
static eSnapFlag snap_flag_from_spacetype(TransInfo *t)
void resetSnapping(TransInfo *t)
void drawSnapping(TransInfo *t)
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])
static void snap_object_context_init(TransInfo *t)
static bool snapNodes(ToolSettings *ts, SpaceNode *snode, ARegion *region, const float2 &mval, eSnapTargetOP snap_target_select, float r_loc[2], float *r_dist_px, char *r_node_border)
static void snap_source_active_fn(TransInfo *t)
static bool snap_grid_uv(TransInfo *t, float r_val[2])
void transform_snap_sequencer_data_free(TransSeqSnapData *data)
TransSeqSnapData * transform_snap_sequencer_data_alloc(const TransInfo *t)
#define SNAP_MIN_DISTANCE
bool transform_snap_sequencer_calc(TransInfo *t)
bool transform_snap_nla_calc(TransInfo *t, float *vec)
ccl_device_inline int abs(int x)
Definition util/math.h:120
@ EVT_TABKEY
@ MOUSEMOVE
void wmOrtho2_region_pixelspace(const ARegion *region)
uint8_t flag
Definition wm_window.cc:138