diff --git a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py index 66b374ca6d..0618b407ec 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py +++ b/src/ifcopenshell-python/ifcopenshell/api/geometry/remove_representation.py @@ -39,7 +39,8 @@ def remove_representation( """ is_ifc2x3 = file.schema == "IFC2X3" styled_items = set() - presentation_layer_assignments = set() + presentation_layer_assignments_items: set[ifcopenshell.entity_instance] = set() + presentation_layer_assignments_reps: set[ifcopenshell.entity_instance] = set() textures = set() colours = set() named_profiles = set() @@ -48,13 +49,13 @@ def remove_representation( [styled_items.add(s) for s in subelement.StyledByItem or []] # IFC2X3 is using LayerAssignments for s in subelement.LayerAssignment if not is_ifc2x3 else subelement.LayerAssignments: - presentation_layer_assignments.add(s) + presentation_layer_assignments_items.add(s) # IfcTessellatedFaceSet inverses [textures.add(t) for t in getattr(subelement, "HasTextures", []) or []] [colours.add(t) for t in getattr(subelement, "HasColours", []) or []] elif subelement.is_a("IfcRepresentation"): for layer in subelement.LayerAssignments: - presentation_layer_assignments.add(layer) + presentation_layer_assignments_reps.add(layer) elif subelement.is_a("IfcProfileDef") and subelement.ProfileName: named_profiles.add(subelement) @@ -62,11 +63,16 @@ def remove_representation( if should_keep_named_profiles: do_not_delete += named_profiles + # Order matters - layer assignments may reference representation directly. + also_consider = list(presentation_layer_assignments_reps) + also_consider.extend(presentation_layer_assignments_items - presentation_layer_assignments_reps) + also_consider.extend(styled_items) + also_consider.extend(textures) ifcopenshell.util.element.remove_deep2( file, representation, - also_consider=list(styled_items | presentation_layer_assignments | colours), - do_not_delete=do_not_delete, + also_consider=also_consider, + do_not_delete=set(do_not_delete), ) for texture in textures: @@ -79,6 +85,7 @@ def remove_representation( item = element.Item if not item or item in to_delete: file.remove(element) + presentation_layer_assignments = presentation_layer_assignments_reps | presentation_layer_assignments_items for element in presentation_layer_assignments: if all(item in to_delete for item in element.AssignedItems): file.remove(element) diff --git a/src/ifcopenshell-python/ifcopenshell/util/element.py b/src/ifcopenshell-python/ifcopenshell/util/element.py index 5e011b525f..fd86a09158 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/element.py +++ b/src/ifcopenshell-python/ifcopenshell/util/element.py @@ -1434,7 +1434,7 @@ def remove_deep2( ifc_file: ifcopenshell.file, element: ifcopenshell.entity_instance, also_consider: list[ifcopenshell.entity_instance] = [], - do_not_delete: list[ifcopenshell.entity_instance] = [], + do_not_delete: set[ifcopenshell.entity_instance] = set(), ) -> None: """Recursively purges a subgraph safely, starting at an element @@ -1462,6 +1462,8 @@ def remove_deep2( :param ifc_file: The IFC file object :param also_consider: elements to also consider as a part of a subgraph + Order could matter for perfomance - elements that reference `element` + directly should go first for the better performance. :param do_not_delete: elements to protect from deletion :param element: The starting element that defines the subgraph """