From b6aa5c57c0e195c43dff877f4fe843b4d01e7b96 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Mon, 20 Mar 2023 20:40:11 +1100 Subject: [PATCH] Fix bug where copying types with parametric materials could lead to invalidly reused material set items. --- .../ifcopenshell/api/root/copy_class.py | 6 ++++-- .../test/api/root/test_copy_class.py | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py index c5cf71a6eb..0e2a88c317 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py @@ -131,9 +131,11 @@ class Usecase: inverse = ifcopenshell.util.element.copy(self.file, inverse) inverse.RelatingMaterial = ifcopenshell.util.element.copy(self.file, inverse.RelatingMaterial) inverse.RelatedObjects = [to_element] - elif inverse.is_a("IfcRelAssociatesMaterial") and from_element.is_a("IfcTypeProduct"): + elif inverse.is_a("IfcRelAssociatesMaterial") and "Set" in inverse.RelatingMaterial.is_a(): inverse = ifcopenshell.util.element.copy(self.file, inverse) - inverse.RelatingMaterial = ifcopenshell.util.element.copy(self.file, inverse.RelatingMaterial) + inverse.RelatingMaterial = ifcopenshell.util.element.copy_deep( + self.file, inverse.RelatingMaterial, exclude=["IfcMaterial"] + ) inverse.RelatedObjects = [to_element] else: for i, value in enumerate(inverse): diff --git a/src/ifcopenshell-python/test/api/root/test_copy_class.py b/src/ifcopenshell-python/test/api/root/test_copy_class.py index 0afe8c2133..3c7e7a0c45 100644 --- a/src/ifcopenshell-python/test/api/root/test_copy_class.py +++ b/src/ifcopenshell-python/test/api/root/test_copy_class.py @@ -146,6 +146,14 @@ class TestCopyClass(test.bootstrap.IFC4): new = ifcopenshell.api.run("root.copy_class", self.file, product=door) assert not new.FillsVoids + def test_retaining_a_single_material(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + material = self.file.createIfcMaterial() + self.file.createIfcRelAssociatesMaterial(RelatedObjects=[element], RelatingMaterial=material) + new = ifcopenshell.api.run("root.copy_class", self.file, product=element) + assert new.HasAssociations[0].RelatingMaterial == element.HasAssociations[0].RelatingMaterial + assert new.HasAssociations[0].RelatingMaterial.is_a("IfcMaterial") + def test_copying_material_set_usages(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") material = self.file.createIfcMaterialLayerSetUsage() @@ -156,11 +164,15 @@ class TestCopyClass(test.bootstrap.IFC4): def test_copying_material_sets_for_type_elements_only(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") - material = self.file.createIfcMaterialLayerSet() + single_material = self.file.createIfcMaterial() + layer = self.file.createIfcMaterialLayer(Material=single_material) + material = self.file.createIfcMaterialLayerSet(MaterialLayers=[layer]) self.file.createIfcRelAssociatesMaterial(RelatedObjects=[element], RelatingMaterial=material) new = ifcopenshell.api.run("root.copy_class", self.file, product=element) - assert new.HasAssociations[0].RelatingMaterial != element.HasAssociations[0].RelatingMaterial assert new.HasAssociations[0].RelatingMaterial.is_a("IfcMaterialLayerSet") + assert new.HasAssociations[0].RelatingMaterial != element.HasAssociations[0].RelatingMaterial + assert new.HasAssociations[0].RelatingMaterial.MaterialLayers[0] != element.HasAssociations[0].RelatingMaterial.MaterialLayers[0] + assert new.HasAssociations[0].RelatingMaterial.MaterialLayers[0].Material == element.HasAssociations[0].RelatingMaterial.MaterialLayers[0].Material def test_copying_a_type_and_purging_type_relationships(self): type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")