From b0efb648f2d6ce38585cd185228a09a1beda6481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Tue, 25 Feb 2025 17:12:06 -0300 Subject: [PATCH] Make snapping system zoom-dependent. See #6162 Refactor the snapping system to better organize the weighting and ordering of snap distances. This change enhances the "stickiness" of snapping points, allowing for prioritized control over different types of snapping points. --- src/bonsai/bonsai/tool/raycast.py | 24 ++++++++++------- src/bonsai/bonsai/tool/snap.py | 43 +++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/bonsai/bonsai/tool/raycast.py b/src/bonsai/bonsai/tool/raycast.py index f519462361..8e1997abf2 100644 --- a/src/bonsai/bonsai/tool/raycast.py +++ b/src/bonsai/bonsai/tool/raycast.py @@ -140,10 +140,12 @@ class Raycast(bonsai.core.tool.Raycast): ray_origin, ray_target, ray_direction = cls.get_viewport_ray_data(context, event) points = [] - # Makes the snapping point more or less sticky then others + # Makes the snapping point more or less sticky than others # It changes the distance and affects how the snapping point are sorted - reference = 0.2 - stick_factor = 0.02 + # We multiply by the increment snap which is based on the viewport zoom + snap_threshold = 10 * tool.Snap.get_increment_snap_value(bpy.context) + if face: + snap_threshold = tool.Snap.get_increment_snap_value(bpy.context) try: loc = view3d_utils.region_2d_to_location_3d(region, rv3d, mouse_pos, ray_direction) @@ -167,12 +169,12 @@ class Raycast(bonsai.core.tool.Raycast): v = obj.matrix_world.copy() @ v intersection = tool.Cad.point_on_edge(v, (ray_target, loc)) distance = (v - intersection).length - if distance < 0.2: + if distance < snap_threshold: snap_point = { "object": obj, "type": "Vertex", "point": v.copy(), - "distance": distance - stick_factor, + "distance": distance, } points.append(snap_point) @@ -186,7 +188,7 @@ class Raycast(bonsai.core.tool.Raycast): intersection = tool.Cad.point_on_edge(division_point, (ray_target, loc)) distance = (division_point - intersection).length - if distance < 0.2: + if distance < snap_threshold: snap_point = { "object": obj, "type": "Edge Center", @@ -199,13 +201,13 @@ class Raycast(bonsai.core.tool.Raycast): if intersection[0]: if tool.Cad.is_point_on_edge(intersection[1], (v1, v2)): distance = (intersection[1] - intersection[0]).length - if distance < 0.2: + if distance < snap_threshold: snap_point = { "object": obj, "type": "Edge", "point": intersection[1].copy(), "edge_verts": (v1, v2), - "distance": distance + 2 * stick_factor, + "distance": distance, } points.append(snap_point) bm.free() @@ -218,6 +220,7 @@ class Raycast(bonsai.core.tool.Raycast): rv3d = context.region_data mouse_pos = event.mouse_region_x, event.mouse_region_y ray_origin, ray_target, ray_direction = cls.get_viewport_ray_data(context, event) + snap_threshold = tool.Snap.get_increment_snap_value(bpy.context) try: loc = view3d_utils.region_2d_to_location_3d(region, rv3d, mouse_pos, ray_direction) @@ -235,7 +238,7 @@ class Raycast(bonsai.core.tool.Raycast): intersection, _ = mathutils.geometry.intersect_point_line(vertex, ray_target, loc) distance = (vertex - intersection).length - if distance < 0.2: + if distance < snap_threshold: snap_point = { "type": "Vertex", "point": vertex, @@ -289,6 +292,7 @@ class Raycast(bonsai.core.tool.Raycast): rv3d = context.region_data mouse_pos = event.mouse_region_x, event.mouse_region_y ray_origin, ray_target, ray_direction = cls.get_viewport_ray_data(context, event) + snap_threshold = tool.Snap.get_increment_snap_value(bpy.context) try: loc = view3d_utils.region_2d_to_location_3d(region, rv3d, mouse_pos, ray_direction) @@ -303,7 +307,7 @@ class Raycast(bonsai.core.tool.Raycast): edge_intersection[1], ray_target, loc ) distance = (edge_intersection[1] - mouse_intersection).length - if distance < 0.2: + if distance < snap_threshold: snap_point = { "object": None, "type": "Edge Intersection", diff --git a/src/bonsai/bonsai/tool/snap.py b/src/bonsai/bonsai/tool/snap.py index 8f89088e63..d2cf4c397c 100644 --- a/src/bonsai/bonsai/tool/snap.py +++ b/src/bonsai/bonsai/tool/snap.py @@ -155,7 +155,8 @@ class Snap(bonsai.core.tool.Snap): # Makes the snapping point more or less sticky than others # It changes the distance and affects how the snapping point is sorted - stick_factor = 0.15 + # We multiply by the increment snap which is based on the viewport zoom + snap_threshold = 1 * cls.get_increment_snap_value(bpy.context) default_container_elevation = tool.Ifc.get_object(tool.Root.get_default_container()).location.z polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline @@ -195,7 +196,7 @@ class Snap(bonsai.core.tool.Snap): if tool_state.plane_method == "XZ": proximity = rot_intersection.x - is_on_rot_axis = abs(proximity) <= stick_factor + is_on_rot_axis = abs(proximity) <= snap_threshold if is_on_rot_axis: elegible_axis.append((abs(proximity), axis)) @@ -543,6 +544,19 @@ class Snap(bonsai.core.tool.Snap): filtered_groups = [group for group in detected_snaps if group["group"] in options] return filtered_groups + def sort_points_by_weighted_distance(snapping_points): + for snap in snapping_points: + weight_factor = 100 * cls.get_increment_snap_value(context) + if snap["type"] == "Vertex": + snap["distance"] *= weight_factor / 12 + if snap["type"] == "Edge": + snap["distance"] *= weight_factor + if snap["type"] == "Edge Center": + snap["distance"] *= weight_factor / 5 + if snap["type"] == "Edge Intersection": + snap["distance"] *= weight_factor / 10 + return sorted(snapping_points, key=lambda x: x["distance"]) + snaps_by_group = filter_snapping_points_by_group(detected_snaps) edges = [] # Get edges to create edge-intersection snap for snapping_point in snaps_by_group: @@ -560,30 +574,33 @@ class Snap(bonsai.core.tool.Snap): snaps_by_group.insert(0, snap_point) snaps_by_type = filter_snapping_points_by_type(snaps_by_group) - snaps_by_type = sorted(snaps_by_type, key=lambda x: x["distance"]) + ordered_snaps = sort_points_by_weighted_distance(snaps_by_type) + + for snap in ordered_snaps: + print("\n", snap["type"], snap["distance"]) # Make Axis first priority if tool_state.lock_axis or tool_state.axis_method in {"X", "Y", "Z"}: - cls.update_snapping_ref(snaps_by_type[0]["point"], snaps_by_type[0]["type"]) - for point in snaps_by_type: + cls.update_snapping_ref(ordered_snaps[0]["point"], ordered_snaps[0]["type"]) + for point in ordered_snaps: if point["type"] == "Axis": - if snaps_by_type[0]["type"] not in {"Axis", "Plane"}: - obj = snaps_by_type[0]["object"] - mixed_snap = cls.mix_snap_and_axis(snaps_by_type[0]["point"], axis_start, axis_end) + if ordered_snaps[0]["type"] not in {"Axis", "Plane"}: + obj = ordered_snaps[0]["object"] + mixed_snap = cls.mix_snap_and_axis(ordered_snaps[0]["point"], axis_start, axis_end) for mixed_point in mixed_snap: snap_point = { "point": mixed_point, "type": "Mix", "object": obj, } - snaps_by_type.insert(0, snap_point) + ordered_snaps.insert(0, snap_point) cls.update_snapping_point(snap_point["point"], snap_point["type"]) - return snaps_by_type + return ordered_snaps cls.update_snapping_point(point["point"], point["type"]) - return snaps_by_type + return ordered_snaps - cls.update_snapping_point(snaps_by_type[0]["point"], snaps_by_type[0]["type"], snaps_by_type[0]["object"]) - return snaps_by_type + cls.update_snapping_point(ordered_snaps[0]["point"], ordered_snaps[0]["type"], ordered_snaps[0]["object"]) + return ordered_snaps @classmethod def modify_snapping_point_selection(cls, snapping_points, lock_axis=False):