mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-09 21:53:40 +00:00
Tolerate stale array child/parent GUIDs (#8177)
* Tolerate stale array child/parent GUIDs A real-world IFC project (an arrayed door whose host got deleted externally) crashed Bonsai's project load with "Instance with GlobalId not found" inside setup_arrays. tool.Blender.get_object_from_guid declared Optional return but let RuntimeError propagate; callers iterating BBIM_Array child lists then crashed instead of skipping. Honour the documented contract by returning None on miss, matching the convention used by every other by_guid lookup helper in tool/array.py, tool/ifc.py, tool/geometry.py. Sweep the four user-action sites that resolve array child/parent GUIDs without a guard - they shared the same bug class but were reachable from different operators (regenerate_array, RegenerateArray clear, duplicate_ifc_objects, process_arrays). An already-missing entity is the desired terminal state for each, so the fix is try/except RuntimeError: continue/skip. setup_arrays now also collects each parent with at least one stale child GUID into IfcImporter.broken_arrays, surfaced via a new Project panel banner mirroring the existing pending_opening_recut UX. The banner reports the count and offers "Select Elements" to navigate to the affected array parents and a Dismiss button. constrain_children_to_parent was being called once per layer inside setup_arrays' for loop even though it always iterates all layers internally - lifted out of the loop (pre-existing N x perf bug that the stale-GUID print exposed). Regression tests: - test_returns_none_when_guid_not_in_file pins the get_object_from_guid Optional contract. - test_remove_array_tolerates_stale_child_guid injects a fake child GUID into BBIM_Array.Data and asserts bim.remove_array completes cleanly. Generated with the assistance of an AI coding tool. * Black: wrap long bl_description in dismiss_pending_array_repair Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -1236,6 +1236,17 @@ class LoadProjectElements(bpy.types.Operator):
|
||||
f"Apply manually from the Project panel.",
|
||||
)
|
||||
|
||||
props.pending_array_repair.clear()
|
||||
if ifc_importer.broken_arrays:
|
||||
for element in ifc_importer.broken_arrays:
|
||||
item = props.pending_array_repair.add()
|
||||
item.ifc_definition_id = element.id()
|
||||
self.report(
|
||||
{"WARNING"},
|
||||
f"{len(ifc_importer.broken_arrays)} array parent(s) reference missing child GUIDs. "
|
||||
f"Inspect from the Project panel.",
|
||||
)
|
||||
|
||||
tool.Project.load_default_thumbnails()
|
||||
tool.Project.set_default_context()
|
||||
tool.Project.set_default_modeling_dimensions()
|
||||
@@ -3539,3 +3550,44 @@ class BIM_OT_select_pending_opening_cuts(bpy.types.Operator):
|
||||
tool.Blender.set_objects_selection(context, active_object=objects[0], selected_objects=objects)
|
||||
self.report({"INFO"}, f"Selected {len(objects)} element(s).")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIM_OT_select_pending_array_repair(bpy.types.Operator):
|
||||
bl_idname = "bim.select_pending_array_repair"
|
||||
bl_label = "Select Array Parents With Missing Children"
|
||||
bl_description = "Select the Blender objects of array parents whose BBIM_Array.Data references children that don't resolve in the file."
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> set[str]:
|
||||
ifc_file = tool.Ifc.get()
|
||||
if ifc_file is None:
|
||||
self.report({"INFO"}, "No IFC file loaded.")
|
||||
return {"CANCELLED"}
|
||||
objects: list[bpy.types.Object] = []
|
||||
for item in tool.Project.get_project_props().pending_array_repair:
|
||||
try:
|
||||
element = ifc_file.by_id(item.ifc_definition_id)
|
||||
except RuntimeError:
|
||||
continue
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj is not None:
|
||||
objects.append(obj)
|
||||
if not objects:
|
||||
self.report({"INFO"}, "No matching Blender objects found for the pending list.")
|
||||
return {"CANCELLED"}
|
||||
tool.Blender.set_objects_selection(context, active_object=objects[0], selected_objects=objects)
|
||||
self.report({"INFO"}, f"Selected {len(objects)} array parent(s).")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class BIM_OT_dismiss_pending_array_repair(bpy.types.Operator):
|
||||
bl_idname = "bim.dismiss_pending_array_repair"
|
||||
bl_label = "Dismiss Pending Array Repair"
|
||||
bl_description = (
|
||||
"Clear the pending array-repair list without acting on it. The underlying BBIM_Array.Data stays unchanged."
|
||||
)
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context: bpy.types.Context) -> set[str]:
|
||||
tool.Project.get_project_props().pending_array_repair.clear()
|
||||
return {"FINISHED"}
|
||||
|
||||
Reference in New Issue
Block a user