diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py new file mode 100644 index 0000000000..91bc609bf3 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/material/add_material_set.py @@ -0,0 +1,32 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"name": "Unnamed", "set_type": "IfcMaterialConstituentSet"} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + if self.settings["set_type"] == "IfcMaterialLayerSet": + return self.file.create_entity("IfcMaterialLayerSet", LayerSetName=self.settings["name"] or "Unnamed") + elif self.settings["set_type"] == "IfcMaterialList": + return self.file.create_entity("IfcMaterialList") + return self.file.create_entity(self.settings["set_type"], Name=self.settings["name"] or "Unnamed") diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py index a5db198ddd..4430c2eb63 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/assign_material.py @@ -124,7 +124,7 @@ class Usecase: ) def get_rel_associates_material(self, material): - if self.file.schema == "IFC2X3": + if self.file.schema == "IFC2X3" or material.is_a("IfcMaterialList"): rel = [ r for r in self.file.by_type("IfcRelAssociatesMaterial") diff --git a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py index 8213bdd933..42bc5c5fd9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py +++ b/src/ifcopenshell-python/ifcopenshell/api/material/remove_material.py @@ -29,8 +29,8 @@ class Usecase: def execute(self): inverse_elements = self.file.get_inverse(self.settings["material"]) self.file.remove(self.settings["material"]) - # TODO: this is probably not robust enough - # TODO: purge material definition representation + # TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set + # This can lead to invalid material sets, but we assume the user will deal with it for inverse in inverse_elements: if inverse.is_a("IfcMaterialConstituent"): self.file.remove(inverse) @@ -40,3 +40,9 @@ class Usecase: self.file.remove(inverse) elif inverse.is_a("IfcRelAssociatesMaterial"): self.file.remove(inverse) + elif inverse.is_a("IfcMaterialDefinitionRepresentation"): + for representation in inverse.Representations: + for item in representation.Items: + self.file.remove(item) + self.file.remove(representation) + self.file.remove(inverse) diff --git a/src/ifcopenshell-python/test/api/material/test_add_material_set.py b/src/ifcopenshell-python/test/api/material/test_add_material_set.py new file mode 100644 index 0000000000..7adb706f08 --- /dev/null +++ b/src/ifcopenshell-python/test/api/material/test_add_material_set.py @@ -0,0 +1,41 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api + + +class TestAddMaterialSet(test.bootstrap.IFC4): + def test_add_layer_set(self): + material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialLayerSet") + assert material.LayerSetName == "Unnamed" + assert material.is_a("IfcMaterialLayerSet") + + def test_add_profile_set(self): + material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialProfileSet") + assert material.Name == "Unnamed" + assert material.is_a("IfcMaterialProfileSet") + + def test_add_constituent_set(self): + material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialConstituentSet") + assert material.Name == "Unnamed" + assert material.is_a("IfcMaterialConstituentSet") + + def test_add_list(self): + material = ifcopenshell.api.run("material.add_material_set", self.file, set_type="IfcMaterialList") + assert material.is_a("IfcMaterialList") diff --git a/src/ifcopenshell-python/test/api/material/test_remove_material.py b/src/ifcopenshell-python/test/api/material/test_remove_material.py new file mode 100644 index 0000000000..9d24f0a04d --- /dev/null +++ b/src/ifcopenshell-python/test/api/material/test_remove_material.py @@ -0,0 +1,91 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2022 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import test.bootstrap +import ifcopenshell.api + + +class TestRemoveMaterial(test.bootstrap.IFC4): + def test_removing_material(self): + material = ifcopenshell.api.run("material.add_material", self.file) + ifcopenshell.api.run("material.remove_material", self.file, material=material) + assert len(self.file.by_type("IfcMaterial")) == 0 + + def test_removing_material_with_associations(self): + wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + material = ifcopenshell.api.run("material.add_material", self.file) + ifcopenshell.api.run("material.assign_material", self.file, product=wall, material=material) + ifcopenshell.api.run("material.remove_material", self.file, material=material) + assert len(self.file.by_type("IfcMaterial")) == 0 + assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 0 + + def test_removing_material_in_layer(self): + wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + material = ifcopenshell.api.run("material.add_material", self.file) + material_set = ifcopenshell.api.run( + "material.add_material_set", self.file, set_type="IfcMaterialLayerSet" + ) + ifcopenshell.api.run("material.add_layer", self.file, layer_set=material_set, material=material) + ifcopenshell.api.run("material.assign_material", self.file, product=wall, material=material_set) + assert len(self.file.by_type("IfcMaterialLayerSet")[0].MaterialLayers) == 1 + ifcopenshell.api.run("material.remove_material", self.file, material=material) + assert len(self.file.by_type("IfcMaterial")) == 0 + assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1 + assert len(self.file.by_type("IfcMaterialLayerSet")[0].MaterialLayers) == 0 + + def test_removing_material_in_profile(self): + wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + material = ifcopenshell.api.run("material.add_material", self.file) + material_set = ifcopenshell.api.run( + "material.add_material_set", self.file, set_type="IfcMaterialProfileSet" + ) + ifcopenshell.api.run("material.add_profile", self.file, profile_set=material_set, material=material) + ifcopenshell.api.run("material.assign_material", self.file, product=wall, material=material_set) + assert len(self.file.by_type("IfcMaterialProfileSet")[0].MaterialProfiles) == 1 + ifcopenshell.api.run("material.remove_material", self.file, material=material) + assert len(self.file.by_type("IfcMaterial")) == 0 + assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1 + assert len(self.file.by_type("IfcMaterialProfileSet")[0].MaterialProfiles) == 0 + + def test_removing_material_in_constituent(self): + wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + material = ifcopenshell.api.run("material.add_material", self.file) + material_set = ifcopenshell.api.run( + "material.add_material_set", self.file, set_type="IfcMaterialConstituentSet" + ) + ifcopenshell.api.run("material.add_constituent", self.file, constituent_set=material_set, material=material) + ifcopenshell.api.run("material.assign_material", self.file, product=wall, material=material_set) + assert len(self.file.by_type("IfcMaterialConstituentSet")[0].MaterialConstituents) == 1 + ifcopenshell.api.run("material.remove_material", self.file, material=material) + assert len(self.file.by_type("IfcMaterial")) == 0 + assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1 + assert self.file.by_type("IfcMaterialConstituentSet")[0].MaterialConstituents is None + + def test_removing_material_in_list(self): + wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + material = ifcopenshell.api.run("material.add_material", self.file) + material_set = ifcopenshell.api.run( + "material.add_material_set", self.file, set_type="IfcMaterialList" + ) + ifcopenshell.api.run("material.add_list_item", self.file, material_list=material_set, material=material) + ifcopenshell.api.run("material.assign_material", self.file, product=wall, material=material_set) + assert len(self.file.by_type("IfcMaterialList")[0].Materials) == 1 + ifcopenshell.api.run("material.remove_material", self.file, material=material) + assert len(self.file.by_type("IfcMaterial")) == 0 + assert len(self.file.by_type("IfcRelAssociatesMaterial")) == 1 + assert len(self.file.by_type("IfcMaterialList")[0].Materials) == 0