mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 10:51:13 +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:
|
else:
|
||||||
return None, None, None
|
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
|
@classmethod
|
||||||
def calculate_snap_threshold(cls, view_distance):
|
def calculate_snap_threshold(cls, view_distance):
|
||||||
snap_threshold = view_distance / 100
|
snap_threshold = view_distance / 100
|
||||||
|
|||||||
@@ -360,6 +360,16 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
plane_normal = tool.Polyline.use_transform_orientations(plane_normal)
|
plane_normal = tool.Polyline.use_transform_orientations(plane_normal)
|
||||||
return plane_origin, 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
|
||||||
polyline_props = tool.Model.get_polyline_props()
|
polyline_props = tool.Model.get_polyline_props()
|
||||||
try:
|
try:
|
||||||
@@ -388,57 +398,19 @@ class Snap(bonsai.core.tool.Snap):
|
|||||||
|
|
||||||
# Objects
|
# Objects
|
||||||
objs_to_raycast = tool.Raycast.filter_objects_to_raycast(context, event, objs_2d_bbox)
|
objs_to_raycast = tool.Raycast.filter_objects_to_raycast(context, event, objs_2d_bbox)
|
||||||
# Wireframes
|
closest_snaps = tool.Raycast.ray_cast_and_get_closest_to_camera_snaps(context, event, objs_to_raycast)
|
||||||
# For wireframe we have to get all the objects so we can further calculate edge intersection
|
detected_snaps.extend(closest_snaps)
|
||||||
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)
|
|
||||||
|
|
||||||
if (space.shading.type == "SOLID" and space.shading.show_xray) or (
|
xray_conditions = (space.shading.type == "SOLID" and space.shading.show_xray) or (space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe)
|
||||||
space.shading.type == "WIREFRAME" and space.shading.show_xray_wireframe
|
|
||||||
):
|
if xray_conditions:
|
||||||
results = []
|
for snap_obj in objs_to_raycast:
|
||||||
for obj in objs_to_raycast:
|
detected_snaps = handle_snap_points_in_xray_mode(closest_snaps, detected_snaps, snap_obj, is_xray=True)
|
||||||
results.append(tool.Raycast.cast_rays_to_single_object(context, event, snap_obj.obj))
|
|
||||||
else:
|
else:
|
||||||
results = []
|
for snap_obj in objs_to_raycast:
|
||||||
results.append(tool.Raycast.cast_rays_and_get_best_object(context, event, objs_to_raycast))
|
for snap in closest_snaps:
|
||||||
|
if "is_closest_to_camera" in snap and snap["is_closest_to_camera"]:
|
||||||
for result in results:
|
detected_snaps = handle_snap_points_in_xray_mode([snap], detected_snaps, snap_obj, is_xray=False)
|
||||||
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)
|
|
||||||
|
|
||||||
# snap to cut geometry (e.g. in plan view)
|
# snap to cut geometry (e.g. in plan view)
|
||||||
if CutDecorator.installed:
|
if CutDecorator.installed:
|
||||||
|
|||||||
Reference in New Issue
Block a user