Blender V4.5
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_listbase.h"
10#include "BLI_math_matrix.h"
11#include "BLI_math_rotation.h"
12#include "BLI_math_vector.h"
13#include "BLI_time.h"
14
15#include "DNA_userdef_types.h"
16
17#include "GPU_immediate.hh"
18#include "GPU_matrix.hh"
19#include "GPU_state.hh"
20
21#include "BKE_editmesh.hh"
22#include "BKE_layer.hh"
23#include "BKE_object.hh"
24#include "BKE_scene.hh"
25
26#include "RNA_access.hh"
27#include "RNA_prototypes.hh"
28
29#include "WM_api.hh"
30
31#include "ED_image.hh"
32#include "ED_node.hh"
34#include "ED_uvedit.hh"
35
36#include "UI_resources.hh"
37#include "UI_view2d.hh"
38
39#include "SEQ_sequencer.hh"
40
41#include "transform.hh"
43#include "transform_convert.hh"
44#include "transform_mode.hh"
45#include "transform_snap.hh"
46
47namespace blender::ed::transform {
48
49/* Use half of flt-max so we can scale up without an exception. */
50
51/* -------------------------------------------------------------------- */
54
55static void setSnappingCallback(TransInfo *t);
56
57static void snap_target_view3d_fn(TransInfo *t, float *vec);
58static void snap_target_uv_fn(TransInfo *t, float *vec);
59static void snap_target_sequencer_fn(TransInfo *t, float *vec);
60static void snap_target_nla_fn(TransInfo *t, float *vec);
61
62static void snap_source_median_fn(TransInfo *t);
63static void snap_source_center_fn(TransInfo *t);
64static void snap_source_closest_fn(TransInfo *t);
65static void snap_source_active_fn(TransInfo *t);
66
68 TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]);
69
71
72/* -------------------------------------------------------------------- */
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 {
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 VSE and 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) {
163 }
164 if (t->mode == TFM_ROTATION) {
166 }
167 if (t->mode == TFM_RESIZE) {
169 }
170 if (ELEM(t->mode,
176 {
177 return true;
178 }
179
180 return false;
181}
182
183static bool doForceIncrementSnap(const TransInfo *t)
184{
186 /* These spaces don't support increment snapping. */
187 return false;
188 }
189
190 if (t->spacetype == SPACE_SEQ && ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
191 return true;
192 }
193
194 if (t->modifiers & MOD_SNAP_FORCED) {
195 return false;
196 }
197
198 return !transformModeUseSnap(t);
199}
200
202{
203 uchar col[4], selectedCol[4], activeCol[4];
205 return;
206 }
207
208 const bool draw_source = (t->flag & T_DRAW_SNAP_SOURCE) &&
210 const bool draw_target = (t->tsnap.status & (SNAP_TARGET_FOUND | SNAP_MULTI_POINTS));
211
212 if (!(draw_source || draw_target)) {
213 return;
214 }
215
216 if (t->spacetype == SPACE_SEQ) {
218 col[3] = 128;
219 }
220 else if (t->spacetype != SPACE_IMAGE) {
222 col[3] = 128;
223
224 UI_GetThemeColor3ubv(TH_SELECT, selectedCol);
225 selectedCol[3] = 128;
226
228 activeCol[3] = 192;
229 }
230
231 if (t->spacetype == SPACE_VIEW3D) {
232 const float *source_loc = nullptr;
233 const float *target_loc = nullptr;
234
236
239 /* Draw snap points. */
240
241 float size = 2.0f * UI_GetThemeValuef(TH_VERTEX_SIZE);
242 float view_inv[4][4];
243 copy_m4_m4(view_inv, rv3d->viewinv);
244
247
249
252 if (p == t->tsnap.selectedPoint) {
253 immUniformColor4ubv(selectedCol);
254 }
255 else {
257 }
258 imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size, view_inv, pos);
259 }
260 }
261
263 }
264
265 if (draw_source) {
266 source_loc = t->tsnap.snap_source;
267 }
268
269 if (t->tsnap.status & SNAP_TARGET_FOUND) {
270 target_loc = t->tsnap.snap_target;
271 }
272
274 rv3d, source_loc, target_loc, t->tsnap.source_type, t->tsnap.target_type, col, activeCol);
275
276 /* Draw normal if needed. */
277 if (target_loc && usingSnappingNormal(t) && validSnappingNormal(t)) {
280
282 immUniformColor4ubv(activeCol);
284 immVertex3fv(pos, target_loc);
286 target_loc[0] + t->tsnap.snapNormal[0],
287 target_loc[1] + t->tsnap.snapNormal[1],
288 target_loc[2] + t->tsnap.snapNormal[2]);
289 immEnd();
291 }
292
294 }
295 else if (t->spacetype == SPACE_IMAGE) {
297
298 float x, y;
299 const float snap_point[2] = {
300 t->tsnap.snap_target[0] / t->aspect[0],
301 t->tsnap.snap_target[1] / t->aspect[1],
302 };
303 UI_view2d_view_to_region_fl(&t->region->v2d, UNPACK2(snap_point), &x, &y);
304 float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize;
305
308
310 immUniformColor3ub(255, 255, 255);
311 imm_draw_circle_wire_2d(pos, x, y, radius, 8);
313
315 }
316 else if (t->spacetype == SPACE_SEQ) {
317 const ARegion *region = t->region;
322 float pixelx = BLI_rctf_size_x(&region->v2d.cur) / BLI_rcti_size_x(&region->v2d.mask);
323
324 if (region->regiontype == RGN_TYPE_PREVIEW) {
325 if (t->tsnap.direction & DIR_GLOBAL_X) {
327 t->tsnap.snap_target[0] - pixelx,
328 region->v2d.cur.ymax,
329 t->tsnap.snap_target[0] + pixelx,
330 region->v2d.cur.ymin);
331 }
332 if (t->tsnap.direction & DIR_GLOBAL_Y) {
334 region->v2d.cur.xmin,
335 t->tsnap.snap_target[1] - pixelx,
336 region->v2d.cur.xmax,
337 t->tsnap.snap_target[1] + pixelx);
338 }
339 }
340 else {
342 t->tsnap.snap_target[0] - pixelx,
343 region->v2d.cur.ymax,
344 t->tsnap.snap_target[0] + pixelx,
345 region->v2d.cur.ymin);
346 }
347
350 }
351}
352
354{
356
357#if 0 /* XXX: need a proper selector for all snap mode. */
358 if (BIF_snappingSupported(t->obedit) && (event->type == EVT_TABKEY) &&
359 (event->modifier & KM_SHIFT))
360 {
361 /* Toggle snap and reinitialize. */
363 initSnapping(t, nullptr);
364 status = TREDRAW_HARD;
365 }
366#endif
367 if (event->type == MOUSEMOVE) {
368 status |= updateSelectedSnapPoint(t);
369 }
370
371 return status;
372}
373
375{
376 float iloc[3], loc[3], no[3];
377 float mval_fl[2];
378
379 copy_v3_v3(iloc, td->loc);
380 if (tc->use_local_mat) {
381 mul_m4_v3(tc->mat, iloc);
382 }
383 else if (t->options & CTX_OBJECT) {
384 Object *ob = static_cast<Object *>(td->extra);
386 copy_v3_v3(iloc, ob->object_to_world().location());
387 }
388
391 {
392 return false;
393 }
394
395 SnapObjectParams snap_object_params{};
396 snap_object_params.snap_target_select = t->tsnap.target_operation;
397 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
398 snap_object_params.occlusion_test = SNAP_OCCLUSION_ALWAYS;
399 snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0;
400
403 t->depsgraph,
404 t->region,
405 static_cast<const View3D *>(t->view),
407 &snap_object_params,
408 nullptr,
409 mval_fl,
410 nullptr,
411 nullptr,
412 loc,
413 no);
414 if (hit != SCE_SNAP_TO_FACE) {
415 return false;
416 }
417
418 float tvec[3];
419 sub_v3_v3v3(tvec, loc, iloc);
420
421 mul_m3_v3(td->smtx, tvec);
422
423 add_v3_v3(td->loc, tvec);
424
425 if ((t->tsnap.flag & SCE_SNAP_ROTATE) && (t->options & CTX_OBJECT)) {
426 /* Handle alignment as well. */
427 const float *original_normal;
428 float mat[3][3];
429
430 /* In pose mode, we want to align normals with Y axis of bones. */
431 original_normal = td->axismtx[2];
432
433 rotation_between_vecs_to_mat3(mat, original_normal, no);
434
435 transform_data_ext_rotate(td, mat, true);
436
437 /* TODO: support constraints for rotation too? see #ElementRotation. */
438 }
439 return true;
440}
441
443{
444 float init_loc[3];
445 float prev_loc[3];
446 float snap_loc[3], snap_no[3];
447
448 copy_v3_v3(init_loc, td->iloc);
449 copy_v3_v3(prev_loc, td->loc);
450 if (tc->use_local_mat) {
451 mul_m4_v3(tc->mat, init_loc);
452 mul_m4_v3(tc->mat, prev_loc);
453 }
454 else if (t->options & CTX_OBJECT) {
455 Object *ob = static_cast<Object *>(td->extra);
457 copy_v3_v3(init_loc, ob->object_to_world().location());
458 }
459
460 SnapObjectParams snap_object_params{};
461 snap_object_params.snap_target_select = t->tsnap.target_operation;
462 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
463 snap_object_params.occlusion_test = SNAP_OCCLUSION_ALWAYS;
464 snap_object_params.use_backface_culling = false;
465 snap_object_params.face_nearest_steps = t->tsnap.face_nearest_steps;
467
470 t->depsgraph,
471 t->region,
472 static_cast<const View3D *>(t->view),
474 &snap_object_params,
475 init_loc,
476 nullptr,
477 prev_loc,
478 nullptr,
479 snap_loc,
480 snap_no);
481
482 if (hit != SCE_SNAP_INDIVIDUAL_NEAREST) {
483 return;
484 }
485
486 float tvec[3];
487 sub_v3_v3v3(tvec, snap_loc, prev_loc);
488 mul_m3_v3(td->smtx, tvec);
489 add_v3_v3(td->loc, tvec);
490
491 /* TODO: support snap alignment similar to #SCE_SNAP_INDIVIDUAL_PROJECT? */
492}
493
495{
496 if (!transform_snap_is_active(t)) {
497 return false;
498 }
499
501}
502
504{
506 return;
507 }
508
509 /* XXX: flickers in object mode. */
511 TransData *td = tc->data;
512 for (int i = 0; i < tc->data_len; i++, td++) {
513 if (td->flag & TD_SKIP) {
514 continue;
515 }
516
517 if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) {
518 continue;
519 }
520
521 /* If both face ray-cast and face nearest methods are enabled, start with face ray-cast and
522 * fall back to face nearest ray-cast does not hit. */
523 bool hit = false;
525 hit = applyFaceProject(t, tc, td);
526 }
527
528 if (!hit && t->tsnap.mode & SCE_SNAP_INDIVIDUAL_NEAREST) {
529 applyFaceNearest(t, tc, td);
530 }
531#if 0 /* TODO: support this? */
532 constraintTransLim(t, td);
533#endif
534 }
535 }
536}
537
548
550{
552 return;
553 }
554
555 if (t->tsnap.mode != SCE_SNAP_TO_INCREMENT) {
556 double current = BLI_time_now_seconds();
557
558 /* Time base quirky code to go around find-nearest slowness. */
559 /* TODO: add exception for object mode, no need to slow it down then. */
560 if (current - t->tsnap.last >= 0.01) {
561 if (t->tsnap.snap_target_fn) {
562 t->tsnap.snap_target_fn(t, vec);
563 }
564 if (t->tsnap.snap_source_fn) {
565 t->tsnap.snap_source_fn(t);
566 }
567
568 t->tsnap.last = current;
569 }
570
571 if (validSnap(t)) {
572 t->mode_info->snap_apply_fn(t, vec);
573 }
574 }
575}
576
591
593{
594 return (t->tsnap.flag & SCE_SNAP_ROTATE) != 0;
595}
596
598{
599 if (validSnap(t)) {
600 if (!is_zero_v3(t->tsnap.snapNormal)) {
601 return true;
602 }
603 }
604
605 return false;
606}
607
608static bool bm_edge_is_snap_target(BMEdge *e, void * /*user_data*/)
609{
612 {
613 return false;
614 }
615
616 return true;
617}
618
619static bool bm_face_is_snap_target(BMFace *f, void * /*user_data*/)
620{
622 return false;
623 }
624
625 BMLoop *l_iter, *l_first;
626 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
627 do {
628 if (BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
629 return false;
630 }
631 } while ((l_iter = l_iter->next) != l_first);
632
633 return true;
634}
635
637{
638 ToolSettings *ts = t->settings;
639 switch (t->spacetype) {
640 case SPACE_VIEW3D:
641 if (r_prop) {
642 *r_prop = &rna_ToolSettings_use_snap;
643 }
644 return &ts->snap_flag;
645 case SPACE_NODE:
646 if (r_prop) {
647 *r_prop = &rna_ToolSettings_use_snap_node;
648 }
649 return &ts->snap_flag_node;
650 case SPACE_IMAGE:
651 if (r_prop) {
652 *r_prop = &rna_ToolSettings_use_snap_uv;
653 }
654 return &ts->snap_uv_flag;
655 case SPACE_SEQ:
656 if (r_prop) {
657 *r_prop = &rna_ToolSettings_use_snap_sequencer;
658 }
659 return &ts->snap_flag_seq;
660 case SPACE_ACTION:
661 case SPACE_NLA:
662 if (r_prop) {
663 *r_prop = &rna_ToolSettings_use_snap_anim;
664 }
665 return &ts->snap_flag_anim;
666 case SPACE_GRAPH: {
667 SpaceGraph *graph_editor = static_cast<SpaceGraph *>(t->area->spacedata.first);
668 switch (graph_editor->mode) {
670 /* The driver editor has a separate snapping flag so it can be kept disabled while
671 * keeping it enabled in the Graph Editor. */
672 return &ts->snap_flag_driver;
673
674 case SIPO_MODE_ANIMATION: {
675 if (r_prop) {
676 *r_prop = &rna_ToolSettings_use_snap_anim;
677 }
678 return &ts->snap_flag_anim;
679 }
680 default:
682 break;
683 }
684 }
685 }
686 /* #SPACE_EMPTY.
687 * It can happen when the operator is called via a handle in `bpy.app.handlers`. */
688 return nullptr;
689}
690
692{
693 if (short *snap_flag = transform_snap_flag_from_spacetype_ptr(t)) {
694 return eSnapFlag(*snap_flag);
695 }
696
697 /* #SPACE_EMPTY.
698 * It can happen when the operator is called via a handle in `bpy.app.handlers`. */
699 return eSnapFlag(0);
700}
701
703{
704 ToolSettings *ts = t->settings;
705
706 if (t->spacetype == SPACE_NODE) {
707 return eSnapMode(ts->snap_node_mode);
708 }
709
710 if (t->spacetype == SPACE_IMAGE) {
711 return eSnapMode(ts->snap_uv_mode);
712 }
713
714 if (t->spacetype == SPACE_SEQ) {
716 }
717
718 if (t->spacetype == SPACE_VIEW3D) {
721 }
722
723 return eSnapMode(ts->snap_mode);
724 }
725
727 return eSnapMode(ts->snap_anim_mode);
728 }
729
730 if (t->spacetype == SPACE_GRAPH) {
731 SpaceGraph *graph_editor = static_cast<SpaceGraph *>(t->area->spacedata.first);
732 switch (graph_editor->mode) {
734 /* Snapping to full values is the only mode that currently makes
735 * sense for the driver editor. */
736 return SCE_SNAP_TO_FRAME;
737
739 return eSnapMode(ts->snap_anim_mode);
740
741 default:
743 break;
744 }
745 }
746
748}
749
751{
754
756
757 /* `t->tsnap.target_operation` not initialized yet. */
759
761 if (base_act && (base_act->object->mode & OB_MODE_PARTICLE_EDIT)) {
762 /* Particles edit mode. */
763 return ret;
764 }
765
767 /* In "Edit Strokes" mode,
768 * snap tool can perform snap to selected or active objects (see #49632)
769 * TODO: perform self snap in gpencil_strokes.
770 *
771 * When we're moving the origins, allow snapping onto our own geometry (see #69132). */
772 return ret;
773 }
774
775 const int obedit_type = t->obedit_type;
776 if (obedit_type != -1) {
777 /* Edit mode. */
778 if (obedit_type == OB_MESH) {
779 /* Editing a mesh. */
780 if ((t->flag & T_PROP_EDIT) != 0) {
781 /* Exclude editmesh when using proportional edit. */
783 }
784 /* UV editing must never snap to the selection as this is what is transformed. */
785 if (t->spacetype == SPACE_IMAGE) {
787 }
788 }
789 else if (ELEM(obedit_type, OB_ARMATURE, OB_CURVES_LEGACY, OB_SURF, OB_LATTICE, OB_MBALL)) {
790 /* Temporary limited to edit mode armature, curves, surfaces, lattices, and meta-balls. */
792 }
793 }
794 else {
795 /* Object or pose mode. */
797 }
798 }
799 else if (ELEM(t->spacetype, SPACE_NODE, SPACE_SEQ)) {
801 }
802
803 return ret;
804}
805
807{
808 if (t->data_type == &TransConvertType_Mesh) {
809 /* Ignore elements being transformed. */
812 (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
816 }
817 else {
818 /* Ignore hidden geometry in the general case. */
821 (bool (*)(BMVert *, void *))BM_elem_cb_check_hflag_disabled,
822 (bool (*)(BMEdge *, void *))BM_elem_cb_check_hflag_disabled,
823 (bool (*)(BMFace *, void *))BM_elem_cb_check_hflag_disabled,
825 }
826}
827
829{
830 if (!transformModeUseSnap(t)) {
831 /* In this case, snapping is always disabled by default. */
832 t->modifiers &= ~MOD_SNAP;
833 }
834
835 if (doForceIncrementSnap(t)) {
837 }
838
839 if ((t->spacetype != SPACE_VIEW3D) || (t->flag & T_NO_PROJECT)) {
840 /* Force project off when not supported. */
842 }
843
846 }
847
849
850 if (t->spacetype == SPACE_VIEW3D) {
851 if (t->tsnap.object_context == nullptr) {
855 }
856 }
857 else if (t->spacetype == SPACE_SEQ) {
858 if (t->tsnap.seq_context == nullptr) {
860 }
861 }
862}
863
864void transform_snap_grid_init(const TransInfo *t, float r_snap[3], float *r_snap_precision)
865{
866 /* Default values. */
867 r_snap[0] = r_snap[1] = 1.0f;
868 r_snap[2] = 0.0f;
869 *r_snap_precision = 0.1f;
870
871 if (t->spacetype == SPACE_VIEW3D) {
872 /* Used by incremental snap. */
873 if (t->region->regiontype == RGN_TYPE_WINDOW) {
874 View3D *v3d = static_cast<View3D *>(t->area->spacedata.first);
875 r_snap[0] = r_snap[1] = r_snap[2] = ED_view3d_grid_view_scale(
876 t->scene, v3d, t->region, nullptr);
877 }
878 }
879 else if (t->spacetype == SPACE_IMAGE) {
880 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
881 const View2D *v2d = &t->region->v2d;
882 int grid_size = SI_GRID_STEPS_LEN;
883 float zoom_factor = ED_space_image_zoom_level(v2d, grid_size);
884 float grid_steps_x[SI_GRID_STEPS_LEN];
885 float grid_steps_y[SI_GRID_STEPS_LEN];
886
887 ED_space_image_grid_steps(sima, grid_steps_x, grid_steps_y, grid_size);
888 /* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */
889 r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps_x, zoom_factor);
890 r_snap[1] = ED_space_image_increment_snap_value(grid_size, grid_steps_y, zoom_factor);
891 *r_snap_precision = 0.5f;
892 }
893 else if (t->spacetype == SPACE_CLIP) {
894 r_snap[0] = r_snap[1] = 0.125f;
895 *r_snap_precision = 0.5f;
896 }
897 else if (t->spacetype == SPACE_NODE) {
898 r_snap[0] = r_snap[1] = space_node::grid_size_get();
899 }
900}
901
903{
904 ToolSettings *ts = t->settings;
905 eSnapSourceOP snap_source = eSnapSourceOP(ts->snap_target);
906
907 resetSnapping(t);
912
913 /* If snap property exists. */
914 PropertyRNA *prop;
915 if (op && (prop = RNA_struct_find_property(op->ptr, "snap")) &&
916 RNA_property_is_set(op->ptr, prop))
917 {
918 if (RNA_property_boolean_get(op->ptr, prop)) {
919 t->modifiers |= MOD_SNAP;
920
921 if ((prop = RNA_struct_find_property(op->ptr, "snap_elements")) &&
922 RNA_property_is_set(op->ptr, prop))
923 {
925 }
926
927 /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of
928 * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved
929 * geometry is snapped). */
930 if ((prop = RNA_struct_find_property(op->ptr, "snap_target")) &&
931 RNA_property_is_set(op->ptr, prop))
932 {
933 snap_source = eSnapSourceOP(RNA_property_enum_get(op->ptr, prop));
934 }
935
936 if ((prop = RNA_struct_find_property(op->ptr, "snap_point")) &&
937 RNA_property_is_set(op->ptr, prop))
938 {
942 }
943
944 /* Snap align only defined in specific cases. */
945 if ((prop = RNA_struct_find_property(op->ptr, "snap_align")) &&
946 RNA_property_is_set(op->ptr, prop))
947 {
950
951 RNA_float_get_array(op->ptr, "snap_normal", t->tsnap.snapNormal);
953 }
954
955 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_project")) &&
956 RNA_property_is_set(op->ptr, prop))
957 {
960 }
961
962 /* Use_snap_self is misnamed and should be use_snap_active. */
963 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_self")) &&
964 RNA_property_is_set(op->ptr, prop))
965 {
967 !RNA_property_boolean_get(op->ptr, prop),
969 }
970
971 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_edit")) &&
972 RNA_property_is_set(op->ptr, prop))
973 {
975 !RNA_property_boolean_get(op->ptr, prop),
977 }
978
979 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_nonedit")) &&
980 RNA_property_is_set(op->ptr, prop))
981 {
983 !RNA_property_boolean_get(op->ptr, prop),
985 }
986
987 if ((prop = RNA_struct_find_property(op->ptr, "use_snap_selectable")) &&
988 RNA_property_is_set(op->ptr, prop))
989 {
991 RNA_property_boolean_get(op->ptr, prop),
993 }
994 }
995 }
996 /* Use scene defaults only when transform is modal. */
997 else if (t->flag & T_MODAL) {
998 if (t->tsnap.flag & SCE_SNAP) {
999 t->modifiers |= MOD_SNAP;
1000 }
1001
1014 }
1015
1016 t->tsnap.source_operation = snap_source;
1017
1021}
1022
1036
1038{
1039 if (t->spacetype == SPACE_VIEW3D) {
1042 }
1043 else {
1046 }
1047}
1048
1050{
1051 if (t->spacetype == SPACE_VIEW3D) {
1052 if (t->options & CTX_CAMERA) {
1053 /* Not with camera selected in camera view. */
1054 return;
1055 }
1057 }
1058 else if (t->spacetype == SPACE_IMAGE) {
1059 SpaceImage *sima = static_cast<SpaceImage *>(t->area->spacedata.first);
1062
1063 const bool is_uv_editor = sima->mode == SI_MODE_UV;
1064 const bool has_edit_object = obact && BKE_object_is_in_editmode(obact);
1065 if (is_uv_editor && has_edit_object) {
1067 }
1068 }
1069 else if (t->spacetype == SPACE_NODE) {
1070 /* Pass. */
1071 }
1072 else if (t->spacetype == SPACE_SEQ) {
1074 /* The target is calculated along with the snap point. */
1075 return;
1076 }
1077 else if (t->spacetype == SPACE_NLA) {
1079 /* The target is calculated along with the snap point. */
1080 return;
1081 }
1082 else {
1083 return;
1084 }
1085
1086 switch (t->tsnap.source_operation) {
1089 break;
1091 if (!ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
1093 break;
1094 }
1095 /* Can't do TARGET_CENTER with these modes,
1096 * use TARGET_MEDIAN instead. */
1100 break;
1103
1104 /* XXX, workaround: active needs to be calculated before transforming, otherwise
1105 * `t->tsnap.snap_source` will be calculated with the transformed data since we're not
1106 * reading from 'td->center' in this case. (See: #40241 and #40348). */
1108 break;
1109 }
1110}
1111
1113{
1114 /* Currently only 3D viewport works for snapping points. */
1117
1118 t->tsnap.selectedPoint = p;
1119
1121
1122 BLI_addtail(&t->tsnap.points, p);
1123
1125 }
1126}
1127
1129{
1131
1132 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1133 TransSnapPoint *closest_p = nullptr;
1134 float dist_min_sq = TRANSFORM_SNAP_MAX_PX;
1135 float screen_loc[2];
1136
1138 float dist_sq;
1139
1140 if (ED_view3d_project_float_global(t->region, p->co, screen_loc, V3D_PROJ_TEST_NOP) !=
1142 {
1143 continue;
1144 }
1145
1146 dist_sq = len_squared_v2v2(t->mval, screen_loc);
1147
1148 if (dist_sq < dist_min_sq) {
1149 closest_p = p;
1150 dist_min_sq = dist_sq;
1151 }
1152 }
1153
1154 if (closest_p) {
1155 if (t->tsnap.selectedPoint != closest_p) {
1156 status = TREDRAW_HARD;
1157 }
1158
1159 t->tsnap.selectedPoint = closest_p;
1160 }
1161 }
1162
1163 return status;
1164}
1165
1167{
1168 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1170
1171 if (t->tsnap.selectedPoint) {
1173
1176 }
1177
1178 t->tsnap.selectedPoint = nullptr;
1179 }
1180 }
1181}
1182
1183void getSnapPoint(const TransInfo *t, float vec[3])
1184{
1185 if (t->tsnap.points.first) {
1186 TransSnapPoint *p;
1187 int total = 0;
1188
1189 vec[0] = vec[1] = vec[2] = 0;
1190
1191 for (p = static_cast<TransSnapPoint *>(t->tsnap.points.first); p; p = p->next, total++) {
1192 add_v3_v3(vec, p->co);
1193 }
1194
1195 if (t->tsnap.status & SNAP_TARGET_FOUND) {
1196 add_v3_v3(vec, t->tsnap.snap_target);
1197 total++;
1198 }
1199
1200 mul_v3_fl(vec, 1.0f / total);
1201 }
1202 else {
1203 copy_v3_v3(vec, t->tsnap.snap_target);
1204 }
1205}
1206
1208{
1209 if (t->tsnap.status & SNAP_MULTI_POINTS) {
1212 t->tsnap.selectedPoint = nullptr;
1213 }
1214}
1215
1217
1218/* -------------------------------------------------------------------- */
1221
1222static void snap_grid_uv_apply(TransInfo *t, const float grid_dist[2], float r_out[2])
1223{
1224 float3 in;
1225 convertViewVec(t, in, t->mval[0] - t->center2d[0], t->mval[1] - t->center2d[1]);
1226
1227 if (t->con.mode & CON_APPLY) {
1228 /* We need to clear the previous Snap to Grid result,
1229 * otherwise #t->con.applyVec will have no effect. */
1233 }
1234
1235 const float *center_global = t->center_global;
1236 for (int i = 0; i < 2; i++) {
1237 const float iter_fac = grid_dist[i];
1238 r_out[i] = iter_fac * roundf((in[i] + center_global[i]) / iter_fac);
1239 }
1240}
1241
1242static bool snap_grid_uv(TransInfo *t, float r_val[2])
1243{
1244 float grid_dist[2];
1245 mul_v2_v2v2(grid_dist, t->snap_spatial, t->aspect);
1246 if (t->modifiers & MOD_PRECISION) {
1247 mul_v2_fl(grid_dist, t->snap_spatial_precision);
1248 }
1249
1250 /* Early bailing out if no need to snap */
1251 if (is_zero_v2(grid_dist)) {
1252 return false;
1253 }
1254
1255 snap_grid_uv_apply(t, grid_dist, r_val);
1257 return true;
1258}
1259
1261
1262/* -------------------------------------------------------------------- */
1265
1266static void snap_target_view3d_fn(TransInfo *t, float * /*vec*/)
1267{
1269 float loc[3];
1270 float no[3];
1271 bool found = false;
1272 eSnapMode snap_elem = SCE_SNAP_TO_NONE;
1273 float dist_px = SNAP_MIN_DISTANCE; /* Use a user defined value here. */
1274
1276 zero_v3(no); /* objects won't set this */
1277 snap_elem = snapObjectsTransform(t, t->mval, &dist_px, loc, no);
1278 found = (snap_elem != SCE_SNAP_TO_NONE);
1279 }
1280 if ((found == false) && (t->tsnap.mode & SCE_SNAP_TO_VOLUME)) {
1281 bool use_peel = (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0;
1282 found = peelObjectsTransform(t, t->mval, use_peel, loc, no, nullptr);
1283
1284 if (found) {
1285 snap_elem = SCE_SNAP_TO_VOLUME;
1286 }
1287 }
1288
1289 if (found == true) {
1290 copy_v3_v3(t->tsnap.snap_target, loc);
1291 copy_v3_v3(t->tsnap.snapNormal, no);
1292
1294
1295 if (snap_elem == SCE_SNAP_TO_GRID && t->mode_info != &TransMode_translate) {
1296 /* Change it to #SCE_SNAP_TO_POINT so we can see the symbol for other modes. */
1297 snap_elem = SCE_SNAP_TO_POINT;
1298 }
1299 }
1300 else {
1302 }
1303
1304 t->tsnap.target_type = snap_elem;
1305}
1306
1307static void snap_target_uv_fn(TransInfo *t, float * /*vec*/)
1308{
1310 bool found = false;
1311 if (t->tsnap.mode & SCE_SNAP_TO_VERTEX) {
1312 const Vector<Object *> objects =
1314 t->scene, t->view_layer, nullptr);
1315
1316 float dist_sq = square_f(float(SNAP_MIN_DISTANCE));
1318 t->scene,
1319 objects,
1320 t->mval,
1322 &dist_sq,
1323 t->tsnap.snap_target))
1324 {
1325 t->tsnap.snap_target[0] *= t->aspect[0];
1326 t->tsnap.snap_target[1] *= t->aspect[1];
1328 found = true;
1329 }
1330 }
1331
1332 if (!found && (t->tsnap.mode & SCE_SNAP_TO_GRID)) {
1333 found = snap_grid_uv(t, t->tsnap.snap_target);
1334 }
1335
1337}
1338
1339static void snap_target_sequencer_fn(TransInfo *t, float * /*vec*/)
1340{
1342 if (snap_sequencer_calc(t)) {
1344 }
1345 else {
1347 }
1348}
1349
1350static void snap_target_nla_fn(TransInfo *t, float *vec)
1351{
1353 if (transform_snap_nla_calc(t, vec)) {
1355 }
1356 else {
1358 }
1359}
1360
1362
1363/* -------------------------------------------------------------------- */
1366
1367void tranform_snap_target_median_calc(const TransInfo *t, float r_median[3])
1368{
1369 int i_accum = 0;
1370
1371 zero_v3(r_median);
1372
1374 float v[3];
1375 zero_v3(v);
1376
1377 int num_selected = 0;
1378 tc->foreach_index_selected([&](const int i) {
1379 add_v3_v3(v, tc->data[i].center);
1380 num_selected++;
1381 });
1382
1383 if (num_selected == 0) {
1384 /* Is this possible? */
1385 continue;
1386 }
1387
1388 mul_v3_fl(v, 1.0 / num_selected);
1389
1390 if (tc->use_local_mat) {
1391 mul_m4_v3(tc->mat, v);
1392 }
1393
1394 add_v3_v3(r_median, v);
1395 i_accum++;
1396 }
1397
1398 mul_v3_fl(r_median, 1.0 / i_accum);
1399}
1400
1402{
1403 /* Only need to calculate once. */
1404 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1406
1409 }
1410}
1411
1413{
1414 /* Only need to calculate once. */
1415 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1416 if (calculateCenterActive(t, true, t->tsnap.snap_source)) {
1419 }
1420 else {
1421 /* No active, default to median, */
1425 }
1426 }
1427}
1428
1430{
1431 /* Only need to calculate once. */
1432 if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
1436 }
1437}
1438
1440{
1441 /* Only valid if a snap point has been selected. */
1442 if (!(t->tsnap.status & SNAP_TARGET_FOUND)) {
1443 return;
1444 }
1445
1447 /* Previously Snap to Grid had its own snap source which was always the result of
1448 * #snap_source_median_fn. Now this mode shares the same code, so to not change the behavior
1449 * too much when using Closest, use the transform pivot as the snap source in this case. */
1452 /* Use #SCE_SNAP_TO_POINT to differentiate from 'Closest' bounds and thus avoid recalculating
1453 * the median center. */
1455 }
1456 }
1457 else {
1458 float dist_closest = 0.0f;
1459 TransData *closest = nullptr;
1460
1461 /* Object mode. */
1462 if (t->options & CTX_OBJECT) {
1464 tc->foreach_index_selected([&](const int i) {
1465 TransData *td = &tc->data[i];
1466
1467 std::optional<Bounds<float3>> bounds;
1468
1469 if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
1470 Object *ob = static_cast<Object *>(td->extra);
1472 }
1473
1474 /* Use bound-box if possible. */
1475 if (bounds) {
1476 BoundBox bb;
1478 int j;
1479
1480 for (j = 0; j < 8; j++) {
1481 float loc[3];
1482 float dist;
1483
1484 copy_v3_v3(loc, bb.vec[j]);
1485 mul_m4_v3(td->ext->obmat, loc);
1486
1487 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1488
1489 if ((dist != TRANSFORM_DIST_INVALID) &&
1490 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1491 {
1492 copy_v3_v3(t->tsnap.snap_source, loc);
1493 closest = td;
1494 dist_closest = dist;
1495 }
1496 }
1497 }
1498 /* Use element center otherwise. */
1499 else {
1500 float loc[3];
1501 float dist;
1502
1503 copy_v3_v3(loc, td->center);
1504
1505 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1506
1507 if ((dist != TRANSFORM_DIST_INVALID) &&
1508 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1509 {
1510 copy_v3_v3(t->tsnap.snap_source, loc);
1511 closest = td;
1512 }
1513 }
1514 });
1515 }
1516 }
1517 else {
1519 tc->foreach_index_selected([&](const int i) {
1520 TransData *td = &tc->data[i];
1521
1522 float loc[3];
1523 float dist;
1524
1525 copy_v3_v3(loc, td->center);
1526
1527 if (tc->use_local_mat) {
1528 mul_m4_v3(tc->mat, loc);
1529 }
1530
1531 dist = t->mode_info->snap_distance_fn(t, loc, t->tsnap.snap_target);
1532
1533 if ((dist != TRANSFORM_DIST_INVALID) &&
1534 (closest == nullptr || fabsf(dist) < fabsf(dist_closest)))
1535 {
1536 copy_v3_v3(t->tsnap.snap_source, loc);
1537 closest = td;
1538 dist_closest = dist;
1539 }
1540 });
1541 }
1542 }
1543 }
1544
1546}
1547
1549
1550/* -------------------------------------------------------------------- */
1553
1555 TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3])
1556{
1557 SnapObjectParams snap_object_params{};
1558 snap_object_params.snap_target_select = t->tsnap.target_operation;
1559 snap_object_params.grid_size = (t->modifiers & MOD_PRECISION) ?
1561 t->snap_spatial[0];
1562 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
1563 snap_object_params.occlusion_test = SNAP_OCCLUSION_AS_SEEM;
1564 snap_object_params.use_backface_culling = (t->tsnap.flag & SCE_SNAP_BACKFACE_CULLING) != 0;
1565
1566 float *prev_co = (t->tsnap.status & SNAP_SOURCE_FOUND) ? t->tsnap.snap_source : t->center_global;
1567 float *grid_co = nullptr, grid_co_stack[3];
1568 if ((t->tsnap.mode & SCE_SNAP_TO_GRID) && (t->con.mode & CON_APPLY) && t->mode != TFM_ROTATION) {
1569 /* Without this position adjustment, the snap may be far from the expected constraint point. */
1570 grid_co = grid_co_stack;
1571 convertViewVec(t, grid_co, mval[0] - t->center2d[0], mval[1] - t->center2d[1]);
1573 transform_constraint_get_nearest(t, grid_co, grid_co);
1574 add_v3_v3(grid_co, t->center_global);
1575 }
1576
1578 t->depsgraph,
1579 t->region,
1580 static_cast<const View3D *>(t->view),
1581 t->tsnap.mode,
1582 &snap_object_params,
1583 grid_co,
1584 mval,
1585 prev_co,
1586 dist_px,
1587 r_loc,
1588 r_no);
1589}
1590
1592
1593/* -------------------------------------------------------------------- */
1596
1598 const float mval[2],
1599 const bool use_peel_object,
1600 /* Return args. */
1601 float r_loc[3],
1602 float r_no[3],
1603 float *r_thickness)
1604{
1605 SnapObjectParams snap_object_params{};
1606 snap_object_params.snap_target_select = t->tsnap.target_operation;
1607 snap_object_params.edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL;
1608
1609 ListBase depths_peel = {nullptr};
1611 t->depsgraph,
1612 t->region,
1613 static_cast<const View3D *>(t->view),
1614 &snap_object_params,
1615 mval,
1616 -1.0f,
1617 false,
1618 &depths_peel);
1619
1620 if (!BLI_listbase_is_empty(&depths_peel)) {
1621 /* At the moment we only use the hits of the first object. */
1622 SnapObjectHitDepth *hit_min = static_cast<SnapObjectHitDepth *>(depths_peel.first);
1623 for (SnapObjectHitDepth *iter = hit_min->next; iter; iter = iter->next) {
1624 if (iter->depth < hit_min->depth) {
1625 hit_min = iter;
1626 }
1627 }
1628 SnapObjectHitDepth *hit_max = nullptr;
1629
1630 if (use_peel_object) {
1631 /* If peeling objects, take the first and last from each object. */
1632 hit_max = hit_min;
1633 LISTBASE_FOREACH (SnapObjectHitDepth *, iter, &depths_peel) {
1634 if ((iter->depth > hit_max->depth) && (iter->ob_uuid == hit_min->ob_uuid)) {
1635 hit_max = iter;
1636 }
1637 }
1638 }
1639 else {
1640 /* Otherwise, pair first with second and so on. */
1641 LISTBASE_FOREACH (SnapObjectHitDepth *, iter, &depths_peel) {
1642 if ((iter != hit_min) && (iter->ob_uuid == hit_min->ob_uuid)) {
1643 if (hit_max == nullptr) {
1644 hit_max = iter;
1645 }
1646 else if (iter->depth < hit_max->depth) {
1647 hit_max = iter;
1648 }
1649 }
1650 }
1651 /* In this case has only one hit. treat as ray-cast. */
1652 if (hit_max == nullptr) {
1653 hit_max = hit_min;
1654 }
1655 }
1656
1657 mid_v3_v3v3(r_loc, hit_min->co, hit_max->co);
1658
1659 if (r_thickness) {
1660 *r_thickness = hit_max->depth - hit_min->depth;
1661 }
1662
1663 /* XXX, is there a correct normal in this case ???, for now just z up. */
1664 r_no[0] = 0.0;
1665 r_no[1] = 0.0;
1666 r_no[2] = 1.0;
1667
1668 LISTBASE_FOREACH_MUTABLE (SnapObjectHitDepth *, link, &depths_peel) {
1669 MEM_delete(link);
1670 }
1671 return true;
1672 }
1673 return false;
1674}
1675
1677
1678/* -------------------------------------------------------------------- */
1681
1682static void snap_increment_apply_ex(const TransInfo * /*t*/,
1683 const int max_index,
1684 const float increment_val,
1685 const float aspect[3],
1686 const float loc[3],
1687 float r_out[3])
1688{
1689 /* Relative snapping in fixed increments. */
1690 for (int i = 0; i <= max_index; i++) {
1691 const float iter_fac = increment_val * aspect[i];
1692 r_out[i] = iter_fac * roundf(loc[i] / iter_fac);
1693 }
1694}
1695
1696static void snap_increment_apply(const TransInfo *t,
1697 const int max_index,
1698 const float increment_dist,
1699 float *r_val)
1700{
1702 BLI_assert(max_index <= 2);
1703
1704 /* Early bailing out if no need to snap. */
1705 if (increment_dist == 0.0f) {
1706 return;
1707 }
1708
1709 float asp_local[3] = {1, 1, 1};
1710 const bool use_aspect = ELEM(t->mode, TFM_TRANSLATION);
1711 const float *asp = use_aspect ? t->aspect : asp_local;
1712
1713 if (use_aspect) {
1714 /* Custom aspect for fcurve. */
1715 if (t->spacetype == SPACE_GRAPH) {
1716 View2D *v2d = &t->region->v2d;
1717 Scene *scene = t->scene;
1718 SpaceGraph *sipo = static_cast<SpaceGraph *>(t->area->spacedata.first);
1720 v2d, scene, sipo->flag & SIPO_DRAWTIME);
1721 asp_local[1] = UI_view2d_grid_resolution_y__values(v2d);
1722 asp = asp_local;
1723 }
1724 }
1725
1726 snap_increment_apply_ex(t, max_index, increment_dist, asp, r_val, r_val);
1727}
1728
1729bool transform_snap_increment_ex(const TransInfo *t, bool use_local_space, float *r_val)
1730{
1731 if (!transform_snap_is_active(t)) {
1732 return false;
1733 }
1734
1735 if (t->spacetype == SPACE_SEQ) {
1736 /* Sequencer has its own dedicated enum for snap_mode with increment snap bit overridden. */
1737 return false;
1738 }
1739
1740 if (!(t->tsnap.mode & SCE_SNAP_TO_INCREMENT)) {
1741 return false;
1742 }
1743
1744 if (t->spacetype != SPACE_VIEW3D && validSnap(t)) {
1745 /* Only do something if using absolute or incremental grid snapping
1746 * and there is no valid snap point. */
1747 return false;
1748 }
1749
1750 if (use_local_space) {
1751 mul_m3_v3(t->spacemtx_inv, r_val);
1752 }
1753
1754 float increment_dist = (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0];
1755 snap_increment_apply(t, t->idx_max, increment_dist, r_val);
1756
1757 if (use_local_space) {
1758 mul_m3_v3(t->spacemtx, r_val);
1759 }
1760
1761 return true;
1762}
1763
1764bool transform_snap_increment(const TransInfo *t, float *r_val)
1765{
1766 return transform_snap_increment_ex(t, false, r_val);
1767}
1768
1770{
1772 {
1773 return (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0];
1774 }
1775
1776 return 0.0f;
1777}
1778
1786
1788
1789/* -------------------------------------------------------------------- */
1792
1794 const float p1[3],
1795 const float p2[3])
1796{
1797 return len_squared_v3v3(p1, p2);
1798}
1799
1801
1802} // 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_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:2827
#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:65
#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_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
@ 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)
#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 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:114
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef 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:1738
float UI_view2d_grid_resolution_y__values(const View2D *v2d)
@ KM_SHIFT
Definition WM_types.hh:275
#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)
#define fabsf(x)
uint pos
uint col
#define in
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static bool applyFaceProject(TransInfo *t, TransDataContainer *tc, TransData *td)
TransConvertTypeInfo TransConvertType_Mesh
static void snap_source_active_fn(TransInfo *t)
static bool bm_face_is_snap_target(BMFace *f, void *)
static void snap_increment_apply(const TransInfo *t, const int max_index, const float increment_dist, float *r_val)
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)
static eSnapTargetOP snap_target_select_from_spacetype(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 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 transform_data_ext_rotate(TransData *td, float mat[3][3], bool use_drot)
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)
short * transform_snap_flag_from_spacetype_ptr(TransInfo *t, const PropertyRNA **r_prop=nullptr)
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 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])
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:384
VecBase< float, 3 > float3
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)
void * regiondata
struct BMVert * v
struct BMLoop * next
struct Object * object
float vec[8][3]
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:805
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:559
void(* snap_source_fn)(TransInfo *)
Definition transform.hh:553
void(* snap_target_fn)(TransInfo *, float *)
Definition transform.hh:552
float xmax
float xmin
float ymax
float ymin
wmEventModifierFlag modifier
Definition WM_types.hh:771
wmEventType type
Definition WM_types.hh:754
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)