From fbcb19fdbbee9a7dad0dca31ade6b1aefdc9d510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Wed, 28 Aug 2024 12:06:37 -0300 Subject: [PATCH] Improved snap "stickiness". Added more control on how the snapping system selects the snapping point, and "M" key now updates the input panel. Added `intersect_edge_2` to Cad tool Revert "Improved snap "stickiness"." This reverts commit 85e67d3996ab8002b4cef7c0b859e9da251d39bf. A few snap and raycast changes to improve the snap feel --- src/bonsai/bonsai/tool/cad.py | 38 +++++++++++++++++++++++++++++++ src/bonsai/bonsai/tool/raycast.py | 17 ++++++++++---- src/bonsai/bonsai/tool/snap.py | 10 ++++---- 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/bonsai/bonsai/tool/cad.py b/src/bonsai/bonsai/tool/cad.py index 6c8d1fa4c6..c83efb755a 100644 --- a/src/bonsai/bonsai/tool/cad.py +++ b/src/bonsai/bonsai/tool/cad.py @@ -171,6 +171,44 @@ class Cad: return r1.to_2d() if r1 else r1, r2.to_2d() if r2 else r2 return results + @classmethod + def intersect_edges_2(cls, edge1, edge2): + """ + Calculate the closest points on two line segments. + Note: This function doesn't use intersect_line_line + + > edge1: tuple of two vectors (v1, v2) representing the first segment + > edge2: tuple of two vectors (v3, v4) representing the second segment + < returns: tuple of two vectors (C1, C2) or (None, None) if lines are parallel + """ + # This function seems to work better then intersect_line_line + # in orthogonal view + # https://en.wikipedia.org/wiki/Skew_lines#Nearest_points + + # Starting and ending points + P1, P1_end = edge1 + P2, P2_end = edge2 + + # Directions + d1 = (P1_end - P1).normalized() + d2 = (P2_end - P2).normalized() + + n = d1.cross(d2) + + # if n is zero, lines are parallel + if n.length == 0: + return None, None + + n2 = d2.cross(n) + + C1 = P1 + ((P2 - P1).dot(n2) / (d1.dot(n2))) * d1 + + n1 = d1.cross(n) + + C2 = P2 + ((P1 - P2).dot(n1) / (d2.dot(n1))) * d2 + + return C1, C2 + @classmethod def get_intersection(cls, edge1, edge2): """ diff --git a/src/bonsai/bonsai/tool/raycast.py b/src/bonsai/bonsai/tool/raycast.py index bb855decec..deefed7183 100644 --- a/src/bonsai/bonsai/tool/raycast.py +++ b/src/bonsai/bonsai/tool/raycast.py @@ -98,8 +98,13 @@ class Raycast(bonsai.core.tool.Raycast): return ray_origin_obj, ray_target_obj, ray_direction_obj @classmethod - def obj_ray_cast(cls, context, event, obj): - ray_origin_obj, _, ray_direction_obj = cls.get_object_ray_data(context, event, obj.matrix_world.copy()) + def obj_ray_cast(cls, context, event, obj, mouse_pos=None): + if mouse_pos: + ray_origin_obj, _, ray_direction_obj = cls.get_object_ray_data( + context, event, obj.matrix_world.copy(), mouse_pos + ) + else: + ray_origin_obj, _, ray_direction_obj = cls.get_object_ray_data(context, event, obj.matrix_world.copy()) success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj) if success: return location, normal, face_index @@ -114,6 +119,10 @@ 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 + # It changes the distance and affects how the snapping point is sorted + sticky_factor = 0.02 + try: loc = view3d_utils.region_2d_to_location_3d(region, rv3d, mouse_pos, ray_direction) except: @@ -145,12 +154,12 @@ class Raycast(bonsai.core.tool.Raycast): if distance < 0.2: points.append((division_point, "Edge Center")) - intersection = tool.Cad.intersect_edges((ray_target, loc), (world_v1, world_v2)) + intersection = tool.Cad.intersect_edges_2((ray_target, loc), (world_v1, world_v2)) if intersection: if tool.Cad.is_point_on_edge(intersection[1], (world_v1, world_v2)): distance = (intersection[1] - intersection[0]).length if distance < 0.8: - points.append((intersection[1], "Edge")) + points.append([distance + 4 * sticky_factor, (intersection[1], "Edge")]) bm.free() return points diff --git a/src/bonsai/bonsai/tool/snap.py b/src/bonsai/bonsai/tool/snap.py index 6d8f88ca9a..26fc2b28b4 100644 --- a/src/bonsai/bonsai/tool/snap.py +++ b/src/bonsai/bonsai/tool/snap.py @@ -297,8 +297,8 @@ class Snap(bonsai.core.tool.Snap): cls.mouse_pos = event.mouse_region_x, event.mouse_region_y detected_snaps = [] - offset = 15 snap_threshold = 0.3 + offset = 10 mouse_offset = ( (-offset, offset), (0, offset), @@ -487,6 +487,10 @@ class Snap(bonsai.core.tool.Snap): snapping_points.append(op) break + if "Plane" in list(origin.keys()): + intersection = origin["Plane"] + snapping_points.append((intersection, "Plane")) + for origin in detected_snaps: if "Axis" in list(origin.keys()): intersection = origin["Axis"] @@ -494,10 +498,6 @@ class Snap(bonsai.core.tool.Snap): axis_end = intersection[2] snapping_points.append((intersection[0], "Axis")) - if "Plane" in list(origin.keys()): - intersection = origin["Plane"] - snapping_points.append((intersection, "Plane")) - # Make Axis first priority if event.shift or cls.snap_axis_method in {"X", "Y", "Z"}: cls.update_snapping_ref(snapping_points[0][0], snapping_points[0][1])