mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-14 03:14:23 +00:00
Snap - Refactor x-ray mode handling to prevent double raycasting
This commit is contained in:
@@ -676,6 +676,81 @@ class Raycast(bonsai.core.tool.Raycast):
|
||||
else:
|
||||
return None, None, None
|
||||
|
||||
@classmethod
|
||||
def ray_cast_and_get_closest_to_camera_snaps(
|
||||
cls,
|
||||
context: bpy.types.Context,
|
||||
event: bpy.types.Event,
|
||||
objs_to_raycast: list[bpy.types.Object],
|
||||
) -> Union[tuple[bpy.types.Object, Vector, int], tuple[None, None, None]]:
|
||||
closest_length_squared = 1.0
|
||||
closest_obj = None
|
||||
closest_hit = None
|
||||
closest_face_index = None
|
||||
|
||||
ray_origin, ray_target, ray_direction = cls.get_viewport_ray_data(context, event)
|
||||
|
||||
closest_snaps = []
|
||||
hit = None
|
||||
|
||||
for snap_obj in objs_to_raycast:
|
||||
if (snap_obj.obj.type in {"EMPTY", "CURVE"}
|
||||
or (hasattr(snap_obj.obj.data, "polygons") and len(snap_obj.obj.data.polygons) == 0)
|
||||
):
|
||||
# For wireframe objects we have to test all the snaps to see which is closer
|
||||
snap_points = tool.Raycast.ray_cast_by_proximity_2d(context, event, snap_obj)
|
||||
closest_wf_hit = None
|
||||
closest_wf_length_squared = 1.0
|
||||
closest_wf_point = None
|
||||
if snap_points:
|
||||
for point in snap_points:
|
||||
point["group"] = "Wireframe"
|
||||
closest_snaps.append(point)
|
||||
length = (point["point"] - ray_origin).length_squared
|
||||
if closest_wf_hit is None or length < closest_wf_length_squared:
|
||||
closest_wf_length_squared = length
|
||||
closest_wf_hit = point["point"]
|
||||
closest_wf_point = point
|
||||
|
||||
if closest_wf_point:
|
||||
hit_obj = closest_wf_point["object"]
|
||||
hit = closest_wf_point["point"]
|
||||
face_index = None
|
||||
|
||||
|
||||
else:
|
||||
# Solid objects
|
||||
hit_obj, hit, face_index = cls.cast_rays_to_single_object(context, event, snap_obj.obj)
|
||||
|
||||
if hit:
|
||||
snap_point = {
|
||||
"point": hit,
|
||||
"type": "Face",
|
||||
"group": "Object",
|
||||
"object": hit_obj,
|
||||
"face_index": face_index,
|
||||
"distance": 9, # High value so it has low priority
|
||||
}
|
||||
closest_snaps.append(snap_point)
|
||||
|
||||
|
||||
# Here we test which is closer, including wireframe and solid objects
|
||||
if hit is not None:
|
||||
length_squared = (hit - ray_origin).length_squared
|
||||
if closest_obj is None or length_squared < closest_length_squared:
|
||||
closest_length_squared = length_squared
|
||||
closest_obj = hit_obj
|
||||
closest_hit = hit
|
||||
closest_face_index = face_index
|
||||
|
||||
# Label snaps from the closest object
|
||||
if closest_obj is not None:
|
||||
for snap in closest_snaps:
|
||||
if snap["object"] == closest_obj:
|
||||
snap["is_closest_to_camera"] = True
|
||||
|
||||
return closest_snaps
|
||||
|
||||
@classmethod
|
||||
def calculate_snap_threshold(cls, view_distance):
|
||||
snap_threshold = view_distance / 100
|
||||
|
||||
@@ -360,6 +360,16 @@ class Snap(bonsai.core.tool.Snap):
|
||||
plane_normal = tool.Polyline.use_transform_orientations(plane_normal)
|
||||
return plane_origin, plane_normal
|
||||
|
||||
def handle_snap_points_in_xray_mode(closest_snaps, detected_snaps, snap_obj, is_xray):
|
||||
for snap in closest_snaps:
|
||||
if snap_obj.obj == snap["object"]:
|
||||
if "face_index" in snap and snap["face_index"]:
|
||||
points = tool.Raycast.ray_cast_by_proximity_2d(context, event, snap_obj)
|
||||
for point in points:
|
||||
point["group"] = "Object"
|
||||
detected_snaps.append(point)
|
||||
return detected_snaps
|
||||
|
||||
# Polyline
|
||||
polyline_props = tool.Model.get_polyline_props()
|
||||
try:
|
||||
@@ -388,58 +398,20 @@ class Snap(bonsai.core.tool.Snap):
|
||||
|
||||
# Objects
|
||||
objs_to_raycast = tool.Raycast.filter_objects_to_raycast(context, event, objs_2d_bbox)
|
||||
# Wireframes
|
||||
# For wireframe we have to get all the objects so we can further calculate edge intersection
|
||||
for snap_obj in objs_to_raycast:
|
||||
if snap_obj.obj.type in {"EMPTY", "CURVE"} or (snap_obj.obj.type == "MESH" and len(snap_obj.obj.data.polygons) == 0):
|
||||
snap_points = tool.Raycast.ray_cast_by_proximity_2d(context, event, snap_obj)
|
||||
if snap_points:
|
||||
for point in snap_points:
|
||||
point["group"] = "Wireframe"
|
||||
detected_snaps.append(point)
|
||||
closest_snaps = tool.Raycast.ray_cast_and_get_closest_to_camera_snaps(context, event, objs_to_raycast)
|
||||
detected_snaps.extend(closest_snaps)
|
||||
|
||||
if (space.shading.type == "SOLID" and space.shading.show_xray) or (
|
||||
space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe
|
||||
):
|
||||
results = []
|
||||
for obj in objs_to_raycast:
|
||||
results.append(tool.Raycast.cast_rays_to_single_object(context, event, snap_obj.obj))
|
||||
xray_conditions = (space.shading.type == "SOLID" and space.shading.show_xray) or (space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe)
|
||||
|
||||
if xray_conditions:
|
||||
for snap_obj in objs_to_raycast:
|
||||
detected_snaps = handle_snap_points_in_xray_mode(closest_snaps, detected_snaps, snap_obj, is_xray=True)
|
||||
else:
|
||||
results = []
|
||||
results.append(tool.Raycast.cast_rays_and_get_best_object(context, event, objs_to_raycast))
|
||||
|
||||
for result in results:
|
||||
snap_obj = result[0]
|
||||
hit = result[1]
|
||||
face_index = result[2]
|
||||
if hit is not None:
|
||||
# Wireframes
|
||||
if snap_obj.type in {"EMPTY", "CURVE"} or (
|
||||
snap_obj.type == "MESH" and len(snap_obj.data.polygons) == 0
|
||||
):
|
||||
continue
|
||||
# Meshes
|
||||
else:
|
||||
# Add face snap
|
||||
snap_point = {
|
||||
"point": hit,
|
||||
"type": "Face",
|
||||
"group": "Object",
|
||||
"object": snap_obj,
|
||||
"face_index": face_index,
|
||||
"distance": 9, # High value so it has low priority
|
||||
}
|
||||
detected_snaps.append(snap_point)
|
||||
|
||||
# Add vertex and edge snap
|
||||
snap_points = tool.Raycast.ray_cast_by_proximity(
|
||||
context, event, snap_obj, snap_obj.data.polygons[face_index]
|
||||
)
|
||||
if snap_points:
|
||||
for point in snap_points:
|
||||
point["group"] = "Object"
|
||||
detected_snaps.append(point)
|
||||
|
||||
for snap_obj in objs_to_raycast:
|
||||
for snap in closest_snaps:
|
||||
if "is_closest_to_camera" in snap and snap["is_closest_to_camera"]:
|
||||
detected_snaps = handle_snap_points_in_xray_mode([snap], detected_snaps, snap_obj, is_xray=False)
|
||||
|
||||
# snap to cut geometry (e.g. in plan view)
|
||||
if CutDecorator.installed:
|
||||
cut_snaps = []
|
||||
|
||||
Reference in New Issue
Block a user