diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 83e6bff999..3062aa332c 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -2509,11 +2509,12 @@ class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator): return True def _execute(self, context): - obj = tool.Geometry.get_active_or_representation_obj() + assert (obj := tool.Geometry.get_active_or_representation_obj()) + assert (element := tool.Ifc.get_entity(obj)) ifc_file = tool.Ifc.get() representation_item = ifc_file.by_id(self.representation_item_id) - tool.Geometry.remove_representation_item(representation_item) + tool.Geometry.remove_representation_item(representation_item, element) tool.Geometry.reload_representation(obj) # reload items ui diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index 4fe8116b9c..e97fd5c869 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -202,6 +202,7 @@ class Geometry(bonsai.core.tool.Geometry): @classmethod def delete_ifc_item(cls, obj: bpy.types.Object) -> None: + """Delete IfcRepresentationItem's Object.""" props = tool.Geometry.get_geometry_props() if len(props.item_objs) == 1: return @@ -212,7 +213,9 @@ class Geometry(bonsai.core.tool.Geometry): mesh = obj.data assert isinstance(mesh, bpy.types.Mesh) item = tool.Ifc.get().by_id(tool.Geometry.get_mesh_props(mesh).ifc_definition_id) - cls.remove_representation_item(item) + rep_obj = props.representation_obj + assert (rep_obj := props.representation_obj) and (rep_element := tool.Ifc.get_entity(rep_obj)) + cls.remove_representation_item(item, rep_element) cls.reload_representation(props.representation_obj) bpy.data.objects.remove(obj) @@ -1344,12 +1347,19 @@ class Geometry(bonsai.core.tool.Geometry): ) @classmethod - def remove_representation_item(cls, representation_item: ifcopenshell.entity_instance) -> None: + def remove_representation_item( + cls, representation_item: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance + ) -> None: + """Remove IfcRepresentationItem. + + :param representation_item: item to remove. + :param element: item's element. Is used to unmark manual booleans. + """ # NOTE: we assume it's not the last representation item # otherwise we probably would need to remove representation too # NOTE: a lot of shared code with `geometry.remove_representation` ifc_file = tool.Ifc.get() - shape_aspects = [] + shape_aspects: list[ifcopenshell.entity_instance] = [] consider_inverses = [] styled_item, colour, texture, layer = None, None, None, None @@ -1366,7 +1376,7 @@ class Geometry(bonsai.core.tool.Geometry): [consider_inverses.append(texture := t) for t in getattr(representation_item, "HasTextures", [])] representation = None - boolean_results_to_remove = set() + boolean_results_to_remove: set[ifcopenshell.entity_instance] = set() for inverse in ifc_file.get_inverse(representation_item): if inverse.is_a("IfcShapeRepresentation"): if inverse.OfShapeAspect: @@ -1409,11 +1419,9 @@ class Geometry(bonsai.core.tool.Geometry): also_consider = list(consider_inverses) ifcopenshell.util.element.remove_deep2(ifc_file, representation_item, also_consider=also_consider) - props = tool.Geometry.get_geometry_props() - rep_element = tool.Ifc.get_entity(props.representation_obj) - tool.Model.unmark_manual_booleans(rep_element, [b.id() for b in boolean_results_to_remove]) + tool.Model.unmark_manual_booleans(element, [b.id() for b in boolean_results_to_remove]) for boolean_result in boolean_results_to_remove: - cls.remove_representation_item(boolean_result) + cls.remove_representation_item(boolean_result, element) @classmethod def create_shape_aspect( diff --git a/src/bonsai/bonsai/tool/model.py b/src/bonsai/bonsai/tool/model.py index b31dfacfb9..a3dcc47f02 100644 --- a/src/bonsai/bonsai/tool/model.py +++ b/src/bonsai/bonsai/tool/model.py @@ -687,8 +687,12 @@ class Model(bonsai.core.tool.Model): @classmethod def unmark_manual_booleans(cls, element: ifcopenshell.entity_instance, boolean_ids: list[int]) -> None: - # NOTE: we use use boolean_ids instead of boolean entities - # so it will be possible to unmark manual booleans after they already was deleted + """Remove boolean ids from ``element``'s 'BBIM_Boolean' pset. + + :param boolean_ids: List of boolean ids to remove. + Ids are used instead of entities to make it possible to unmark already removed booleans. + Provided ids may not be marked as manual booleans previously. + """ pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean") if not pset: return diff --git a/src/bonsai/test/tool/test_geometry.py b/src/bonsai/test/tool/test_geometry.py index 85a0a4c79d..a392cd2ef3 100644 --- a/src/bonsai/test/tool/test_geometry.py +++ b/src/bonsai/test/tool/test_geometry.py @@ -20,6 +20,7 @@ import bpy import math import numpy as np import ifcopenshell +import ifcopenshell.api.geometry import ifcopenshell.api.type import bonsai.core.tool import bonsai.tool as tool @@ -606,13 +607,13 @@ class TestRemoveRepresentationItem(NewFile): items = [ifc.createIfcExtrudedAreaSolid(), ifc.createIfcExtrudedAreaSolid()] representation = ifc.createIfcShapeRepresentation(Items=items, ContextOfItems=context) - tool.Ifc.run("geometry.assign_representation", product=element, representation=representation) + ifcopenshell.api.geometry.assign_representation(ifc, product=element, representation=representation) product_shape = element.Representation shape_aspect = subject.create_shape_aspect(product_shape, representation, items[:1], None) shape_aspect_id = shape_aspect.id() - subject.remove_representation_item(items[0]) + subject.remove_representation_item(items[0], element) assert tool.Ifc.get_entity_by_id(shape_aspect_id) is None assert set(representation.Items) == {items[1]}