From 732436946ea142a62e945a231272652d38ff69b7 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Sat, 19 Jul 2025 22:45:20 +0200 Subject: [PATCH] Fix #142338: Edge center snapping interferes with face snapping The snapping system could return a regular 'Snap to Edge' result when only derived edge snap types (midpoint, endpoint, perpendicular) were enabled, even if 'Snap to Edge' itself was not included among the selected snap modes. This led to unintended snapping behavior. To address this, a backup of the previous snap result is stored before edge snapping is attempted. If the resulting snap mode is not among the explicitly selected types, the previous state is restored. Additionally, the `hit_list` assignment was moved to the runtime context to separate intermediate data from the final snap result. Pull Request: https://projects.blender.org/blender/blender/pulls/142512 --- .../transform/transform_snap_object.cc | 29 +++++++++++++++---- .../transform/transform_snap_object.hh | 6 ++-- .../transform/transform_snap_object_mesh.cc | 4 +-- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index a6c9fa60eee..37aa7dbfa25 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -1165,10 +1165,10 @@ static bool snap_object_context_runtime_init(SnapObjectContext *sctx, } } } + sctx->runtime.hit_list = hit_list; sctx->ret.ray_depth_max = sctx->ret.ray_depth_max_in_front = ray_depth; sctx->ret.index = -1; - sctx->ret.hit_list = hit_list; sctx->ret.ob = nullptr; sctx->ret.data = nullptr; sctx->ret.dist_px_sq = dist_px_sq; @@ -1424,6 +1424,16 @@ eSnapMode snap_object_project_view3d_ex(SnapObjectContext *sctx, /* Remove what has already been computed. */ sctx->runtime.snap_to_flag &= ~(SCE_SNAP_TO_FACE | SCE_SNAP_INDIVIDUAL_NEAREST); + SnapObjectContext::Output ret_bak{}; + if (!(sctx->runtime.snap_to_flag & SCE_SNAP_TO_EDGE) && + (sctx->runtime.snap_to_flag & + (SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_ENDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR))) + { + /* 'Snap to Edge' may occur even if it is not included among the selected snap types. + * Save a backup to restore the previous result if needed. */ + ret_bak = sctx->ret; + } + if (use_occlusion_plane && has_hit) { /* Compute the new clip_pane but do not add it yet. */ BLI_ASSERT_UNIT_V3(sctx->ret.no); @@ -1454,11 +1464,18 @@ eSnapMode snap_object_project_view3d_ex(SnapObjectContext *sctx, elem = elem_test; } - if ((elem == SCE_SNAP_TO_EDGE) && - (snap_to_flag & - (SCE_SNAP_TO_EDGE_ENDPOINT | SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR))) - { - elem = snap_edge_points(sctx, square_f(*dist_px)); + if (elem == SCE_SNAP_TO_EDGE) { + if (snap_to_flag & + (SCE_SNAP_TO_EDGE_ENDPOINT | SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR)) + { + elem = snap_edge_points(sctx, square_f(*dist_px)); + } + + if (!(elem & snap_to_flag)) { + /* Restore the previous snap. */ + elem = SCE_SNAP_TO_NONE; + sctx->ret = ret_bak; + } } if (elem != SCE_SNAP_TO_NONE) { diff --git a/source/blender/editors/transform/transform_snap_object.hh b/source/blender/editors/transform/transform_snap_object.hh index b196d53d391..b472a7e3c50 100644 --- a/source/blender/editors/transform/transform_snap_object.hh +++ b/source/blender/editors/transform/transform_snap_object.hh @@ -86,6 +86,8 @@ struct SnapObjectContext { /* Read/write. */ uint object_index; + /* List of #SnapObjectHitDepth (caller must free). */ + ListBase *hit_list; eSnapOcclusionTest occlusion_test_edit; @@ -94,7 +96,7 @@ struct SnapObjectContext { } runtime; /* Output. */ - struct { + struct Output { /* Location of snapped point on target surface. */ float3 loc; /* Normal of snapped point on target surface. */ @@ -103,8 +105,6 @@ struct SnapObjectContext { int index; /* Matrix of target object (may not be #Object.object_to_world with dupli-instances). */ float4x4 obmat; - /* List of #SnapObjectHitDepth (caller must free). */ - ListBase *hit_list; /* Snapped object. */ const Object *ob; /* Snapped data. */ diff --git a/source/blender/editors/transform/transform_snap_object_mesh.cc b/source/blender/editors/transform/transform_snap_object_mesh.cc index a927afc18ee..eaa94f890a1 100644 --- a/source/blender/editors/transform/transform_snap_object_mesh.cc +++ b/source/blender/editors/transform/transform_snap_object_mesh.cc @@ -140,7 +140,7 @@ static bool raycastMesh(SnapObjectContext *sctx, } BLI_assert(treedata.raycast_callback != nullptr); - if (sctx->ret.hit_list) { + if (sctx->runtime.hit_list) { RayCastAll_Data data; data.bvhdata = &treedata; @@ -149,7 +149,7 @@ static bool raycastMesh(SnapObjectContext *sctx, data.len_diff = len_diff; data.local_scale = local_scale; data.ob_uuid = ob_index; - data.hit_list = sctx->ret.hit_list; + data.hit_list = sctx->runtime.hit_list; void *hit_last_prev = data.hit_list->last; BLI_bvhtree_ray_cast_all(