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.
This commit is contained in:
Bruno Perdigão
2025-02-25 17:12:06 -03:00
parent a20a8a68a6
commit b0efb648f2
2 changed files with 44 additions and 23 deletions
+14 -10
View File
@@ -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",
+30 -13
View File
@@ -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):