diff --git a/src/blenderbim/blenderbim/core/geometry.py b/src/blenderbim/blenderbim/core/geometry.py index f5f081a57e..d0689bb4fd 100644 --- a/src/blenderbim/blenderbim/core/geometry.py +++ b/src/blenderbim/blenderbim/core/geometry.py @@ -96,3 +96,27 @@ def switch_representation( if enable_dynamic_voids and geometry.is_body_representation(representation): geometry.create_dynamic_voids(obj) + + +def remove_representation(ifc, geometry, obj=None, representation=None): + element = ifc.get_entity(obj) + if geometry.is_mapped_representation(representation) or geometry.is_type_product(element): + type = geometry.get_element_type(element) + representation = geometry.resolve_mapped_representation(representation) + data = geometry.get_representation_data(representation) + if data and geometry.has_data_users(data): + for element in geometry.get_elements_of_type(type): + obj = ifc.get_object(element) + if obj: + obj = geometry.replace_object_with_empty(obj) + obj = ifc.get_object(type) + if obj: + obj = geometry.replace_object_with_empty(obj) + ifc.run("geometry.unassign_representation", product=type, representation=representation) + ifc.run("geometry.remove_representation", representation=representation) + else: + data = geometry.get_representation_data(representation) + if data and geometry.has_data_users(data): + geometry.replace_object_with_empty(obj) + ifc.run("geometry.unassign_representation", product=element, representation=representation) + ifc.run("geometry.remove_representation", representation=representation) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 698629b3a9..8ab8a3c730 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -78,6 +78,8 @@ class Geometry: def does_object_have_mesh_with_faces(cls, obj): pass def duplicate_object_data(cls, obj): pass def get_cartesian_point_coordinate_offset(cls, obj): pass + def get_element_type(cls, element): pass + def get_elements_of_type(cls, type): pass def get_ifc_representation_class(cls, element, representation): pass def get_object_data(cls, obj): pass def get_object_materials_without_styles(cls, obj): pass @@ -85,10 +87,14 @@ class Geometry: def get_representation_data(cls, representation): pass def get_representation_name(cls, representation): pass def get_total_representation_items(cls, obj): pass + def has_data_users(cls, data): pass def import_representation(cls, obj, representation, enable_dynamic_voids=False): pass def is_body_representation(cls, representation): pass + def is_mapped_representation(cls, representation): pass + def is_type_product(cls, element): pass def link(cls, element, obj): pass def rename_object(cls, obj, name): pass + def replace_object_with_empty(cls, obj): pass def resolve_mapped_representation(cls, representation): pass def should_force_faceted_brep(cls): pass def should_force_triangulation(cls): pass diff --git a/src/blenderbim/test/core/test_geometry.py b/src/blenderbim/test/core/test_geometry.py index 9d83184153..db39002b8f 100644 --- a/src/blenderbim/test/core/test_geometry.py +++ b/src/blenderbim/test/core/test_geometry.py @@ -255,3 +255,56 @@ class TestSwitchRepresentation: enable_dynamic_voids=False, is_global=False, ) + + +class TestRemoveRepresentation: + def test_removing_an_actively_used_mapped_representation_by_remapping_usages_to_an_empty(self, ifc, geometry): + ifc.get_entity("obj").should_be_called().will_return("element") + geometry.is_mapped_representation("mapped_rep").should_be_called().will_return(False) + geometry.is_type_product("element").should_be_called().will_return(True) + geometry.get_element_type("element").should_be_called().will_return("type") + geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation") + geometry.get_representation_data("representation").should_be_called().will_return("data") + geometry.has_data_users("data").should_be_called().will_return(True) + geometry.get_elements_of_type("type").should_be_called().will_return(["element"]) + ifc.get_object("element").should_be_called().will_return("obj") + geometry.replace_object_with_empty("obj").should_be_called() + ifc.get_object("type").should_be_called().will_return("type_obj") + geometry.replace_object_with_empty("type_obj").should_be_called() + ifc.run("geometry.unassign_representation", product="type", representation="representation").should_be_called() + ifc.run("geometry.remove_representation", representation="representation").should_be_called() + subject.remove_representation(ifc, geometry, obj="obj", representation="mapped_rep") + + def test_removing_an_unused_mapped_representation(self, ifc, geometry): + ifc.get_entity("obj").should_be_called().will_return("element") + geometry.is_mapped_representation("mapped_rep").should_be_called().will_return(True) + geometry.get_element_type("element").should_be_called().will_return("type") + geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation") + geometry.get_representation_data("representation").should_be_called().will_return(None) + ifc.run("geometry.unassign_representation", product="type", representation="representation").should_be_called() + ifc.run("geometry.remove_representation", representation="representation").should_be_called() + subject.remove_representation(ifc, geometry, obj="obj", representation="mapped_rep") + + def test_removing_an_actively_used_representation(self, ifc, geometry): + ifc.get_entity("obj").should_be_called().will_return("element") + geometry.is_mapped_representation("representation").should_be_called().will_return(False) + geometry.is_type_product("element").should_be_called().will_return(False) + geometry.get_representation_data("representation").should_be_called().will_return("data") + geometry.has_data_users("data").should_be_called().will_return(True) + geometry.replace_object_with_empty("obj").should_be_called() + ifc.run( + "geometry.unassign_representation", product="element", representation="representation" + ).should_be_called() + ifc.run("geometry.remove_representation", representation="representation").should_be_called() + subject.remove_representation(ifc, geometry, obj="obj", representation="representation") + + def test_removing_an_unused_representation(self, ifc, geometry): + ifc.get_entity("obj").should_be_called().will_return("element") + geometry.is_mapped_representation("representation").should_be_called().will_return(False) + geometry.is_type_product("element").should_be_called().will_return(False) + geometry.get_representation_data("representation").should_be_called().will_return(None) + ifc.run( + "geometry.unassign_representation", product="element", representation="representation" + ).should_be_called() + ifc.run("geometry.remove_representation", representation="representation").should_be_called() + subject.remove_representation(ifc, geometry, obj="obj", representation="representation") diff --git a/src/ifcopenshell-python/test/api/geometry/test_unassign_representation.py b/src/ifcopenshell-python/test/api/geometry/test_unassign_representation.py new file mode 100644 index 0000000000..eae59ea724 --- /dev/null +++ b/src/ifcopenshell-python/test/api/geometry/test_unassign_representation.py @@ -0,0 +1,54 @@ +import numpy +import pytest +import test.bootstrap +import ifcopenshell.api +import ifcopenshell.util.placement + + +class TestUnassignRepresentation(test.bootstrap.IFC4): + def test_unassigning_a_product_representation(self): + representation = self.file.createIfcShapeRepresentation() + representation2 = self.file.createIfcShapeRepresentation() + wall = self.file.createIfcWall( + Representation=self.file.createIfcProductDefinitionShape(Representations=[representation, representation2]) + ) + ifcopenshell.api.run("geometry.unassign_representation", self.file, product=wall, representation=representation) + assert representation not in wall.Representation.Representations + ifcopenshell.api.run( + "geometry.unassign_representation", self.file, product=wall, representation=representation2 + ) + assert not wall.Representation + assert len(self.file.by_type("IfcShapeRepresentation")) == 2 + assert len(self.file.by_type("IfcProductDefinitionShape")) == 0 + + def test_unassigning_a_type_product_representation(self): + representation = self.file.createIfcShapeRepresentation() + origin = self.file.createIfcAxis2Placement3D() + repmap = self.file.createIfcRepresentationMap(MappedRepresentation=representation, MappingOrigin=origin) + walltype = self.file.createIfcWallType(RepresentationMaps=[repmap]) + ifcopenshell.api.run( + "geometry.unassign_representation", self.file, product=walltype, representation=representation + ) + assert not walltype.RepresentationMaps + assert len(self.file.by_type("IfcAxis2Placement3D")) == 0 + assert len(self.file.by_type("IfcRepresentationMap")) == 0 + assert len(self.file.by_type("IfcShapeRepresentation")) == 1 + + def test_unassigning_a_type_product_representation_used_by_instances(self): + representation = self.file.createIfcShapeRepresentation() + origin = self.file.createIfcAxis2Placement3D() + repmap = self.file.createIfcRepresentationMap(MappedRepresentation=representation, MappingOrigin=origin) + walltype = self.file.createIfcWallType(RepresentationMaps=[repmap]) + mapped_item = self.file.createIfcMappedItem(MappingSource=repmap) + rep = self.file.createIfcShapeRepresentation(Items=[mapped_item]) + prodrep = self.file.createIfcProductDefinitionShape(Representations=[rep]) + wall = self.file.createIfcWall(Representation=prodrep) + ifcopenshell.api.run( + "geometry.unassign_representation", self.file, product=walltype, representation=representation + ) + assert not walltype.RepresentationMaps + assert len(self.file.by_type("IfcAxis2Placement3D")) == 0 + assert len(self.file.by_type("IfcRepresentationMap")) == 0 + assert len(self.file.by_type("IfcShapeRepresentation")) == 1 + assert not wall.Representation + assert len(self.file.by_type("IfcProductDefinitionShape")) == 0