mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 10:11:46 +00:00
material.remove_material_set to remove usages
To prevent them from being invalidated. Noticed this as a possible issue investigating https://community.osarch.org/discussion/2820/error-message-in-bonsai-bim
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api.material
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +25,9 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
|
|||||||
"""Removes a material set
|
"""Removes a material set
|
||||||
|
|
||||||
All set items, such as layers, profiles, or constituents will also be
|
All set items, such as layers, profiles, or constituents will also be
|
||||||
removed. However, the materials and profile curves used by the layers,
|
removed. All set usages are also removed.
|
||||||
|
|
||||||
|
However, the materials and profile curves used by the layers,
|
||||||
profiles and constituents will not be removed.
|
profiles and constituents will not be removed.
|
||||||
|
|
||||||
:param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet,
|
:param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet,
|
||||||
@@ -53,7 +56,15 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
|
|||||||
ifcopenshell.api.material.remove_material_set(model, material=material_set)
|
ifcopenshell.api.material.remove_material_set(model, material=material_set)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
inverse_elements = file.get_inverse(material)
|
# Remove all usages for sets.
|
||||||
|
has_usages = material.is_a("IfcMaterialLayerSet") or material.is_a("IfcMaterialProfileSet")
|
||||||
|
if has_usages:
|
||||||
|
# Usage is invalid if it is not associated with some element,
|
||||||
|
# so we can remove usages through unassignment.
|
||||||
|
elements = ifcopenshell.util.element.get_elements_by_material(file, material)
|
||||||
|
if elements:
|
||||||
|
ifcopenshell.api.material.unassign_material(file, products=list(elements))
|
||||||
|
|
||||||
if material.is_a("IfcMaterialLayerSet"):
|
if material.is_a("IfcMaterialLayerSet"):
|
||||||
set_items = material.MaterialLayers or []
|
set_items = material.MaterialLayers or []
|
||||||
elif material.is_a("IfcMaterialProfileSet"):
|
elif material.is_a("IfcMaterialProfileSet"):
|
||||||
@@ -66,9 +77,13 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
|
|||||||
raise ValueError(f"Unknown material set type: {material.is_a()}")
|
raise ValueError(f"Unknown material set type: {material.is_a()}")
|
||||||
for set_item in set_items:
|
for set_item in set_items:
|
||||||
file.remove(set_item)
|
file.remove(set_item)
|
||||||
|
|
||||||
|
inverse_elements = file.get_inverse(material)
|
||||||
file.remove(material)
|
file.remove(material)
|
||||||
|
|
||||||
for inverse in inverse_elements:
|
for inverse in inverse_elements:
|
||||||
if inverse.is_a("IfcRelAssociatesMaterial"):
|
if inverse.is_a("IfcRelAssociatesMaterial"):
|
||||||
|
# NOTE: for has_usages already handled by unassign_material.
|
||||||
history = inverse.OwnerHistory
|
history = inverse.OwnerHistory
|
||||||
file.remove(inverse)
|
file.remove(inverse)
|
||||||
if history:
|
if history:
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entit
|
|||||||
|
|
||||||
If the product does not have a material, nothing happens.
|
If the product does not have a material, nothing happens.
|
||||||
|
|
||||||
|
Unassigning a LayerSet or ProfileSet from the product type will also
|
||||||
|
remove all Usages of the set.
|
||||||
|
|
||||||
:param products: The list IfcProducts that may or may not have a material
|
:param products: The list IfcProducts that may or may not have a material
|
||||||
:return: None
|
:return: None
|
||||||
|
|
||||||
@@ -76,6 +79,8 @@ class Usecase:
|
|||||||
continue
|
continue
|
||||||
if material.is_a() in ["IfcMaterialLayerSet", "IfcMaterialProfileSet"]:
|
if material.is_a() in ["IfcMaterialLayerSet", "IfcMaterialProfileSet"]:
|
||||||
# Remove set usages
|
# Remove set usages
|
||||||
|
# TODO: be more considerate and remove only usages
|
||||||
|
# associated with the set + product type, not all usages?
|
||||||
for inverse in self.file.get_inverse(material):
|
for inverse in self.file.get_inverse(material):
|
||||||
if self.file.schema == "IFC2X3":
|
if self.file.schema == "IFC2X3":
|
||||||
if not inverse.is_a("IfcMaterialLayerSetUsage"):
|
if not inverse.is_a("IfcMaterialLayerSetUsage"):
|
||||||
|
|||||||
@@ -17,9 +17,10 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import test.bootstrap
|
import test.bootstrap
|
||||||
|
import ifcopenshell.api.material
|
||||||
import ifcopenshell.api.pset
|
import ifcopenshell.api.pset
|
||||||
import ifcopenshell.api.root
|
import ifcopenshell.api.root
|
||||||
import ifcopenshell.api.material
|
import ifcopenshell.api.type
|
||||||
|
|
||||||
|
|
||||||
class TestRemoveMaterialSetIFC2X3(test.bootstrap.IFC2X3):
|
class TestRemoveMaterialSetIFC2X3(test.bootstrap.IFC2X3):
|
||||||
@@ -45,6 +46,19 @@ class TestRemoveMaterialSetIFC2X3(test.bootstrap.IFC2X3):
|
|||||||
assert len(self.file.by_type("IfcMaterialLayer")) == 0
|
assert len(self.file.by_type("IfcMaterialLayer")) == 0
|
||||||
assert len(self.file.by_type("IfcMaterial")) == 1
|
assert len(self.file.by_type("IfcMaterial")) == 1
|
||||||
|
|
||||||
|
def test_removing_a_material_set_with_usages(self):
|
||||||
|
material = ifcopenshell.api.material.add_material_set(self.file, set_type="IfcMaterialLayerSet")
|
||||||
|
element_type = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWallType")
|
||||||
|
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
|
||||||
|
ifcopenshell.api.type.assign_type(self.file, related_objects=[element], relating_type=element_type)
|
||||||
|
ifcopenshell.api.material.assign_material(self.file, products=[element_type], material=material)
|
||||||
|
ifcopenshell.api.material.assign_material(
|
||||||
|
self.file, products=[element], material=material, type="IfcMaterialLayerSetUsage"
|
||||||
|
)
|
||||||
|
ifcopenshell.api.material.remove_material_set(self.file, material=material)
|
||||||
|
assert len(self.file.by_type("IfcMaterialLayerSet")) == 0
|
||||||
|
assert len(self.file.by_type("IfcMaterialLayerSetUsage")) == 0
|
||||||
|
|
||||||
|
|
||||||
class TestRemoveMaterialSetIFC4(test.bootstrap.IFC4, TestRemoveMaterialSetIFC2X3):
|
class TestRemoveMaterialSetIFC4(test.bootstrap.IFC4, TestRemoveMaterialSetIFC2X3):
|
||||||
# IFC2X3 doesn't support adding a pset to IfcMaterialLayerSet
|
# IFC2X3 doesn't support adding a pset to IfcMaterialLayerSet
|
||||||
|
|||||||
Reference in New Issue
Block a user