The API can now copy material sets too

This commit is contained in:
Dion Moult
2024-05-25 14:42:45 +10:00
parent 2f071bd809
commit d85f9af790
2 changed files with 125 additions and 33 deletions
@@ -21,12 +21,15 @@ import ifcopenshell.util.element
def copy_material(file: ifcopenshell.file, material: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: def copy_material(file: ifcopenshell.file, material: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
"""Copies a material """Copies a material or material set
All material psets and styles are copied. The copied material is not All material psets and styles are copied. The copied material is not
associated to any elements. associated to any elements.
:param material: The IfcMaterial to copy If a material set is copied, the set items are also copied. However the
underlying materials (and profiles) used within the set items are reused.
:param material: The IfcMaterialDefinition to copy
:type material: ifcopenshell.entity_instance :type material: ifcopenshell.entity_instance
:return: The new copy of the material :return: The new copy of the material
:rtype: ifcopenshell.entity_instance :rtype: ifcopenshell.entity_instance
@@ -40,34 +43,54 @@ def copy_material(file: ifcopenshell.file, material: ifcopenshell.entity_instanc
# Let's duplicate the concrete material # Let's duplicate the concrete material
concrete_copy = ifcopenshell.api.run("material.copy_material", model, material=concrete) concrete_copy = ifcopenshell.api.run("material.copy_material", model, material=concrete)
""" """
settings = {"material": material} if material.is_a("IfcMaterial"):
return _copy_material_with_inverses(file, material)
if settings["material"].is_a("IfcMaterial"): elif material.is_a("IfcMaterialConstituentSet"):
new = ifcopenshell.util.element.copy(file, settings["material"]) new = _copy_material_with_inverses(file, material)
for inverse in file.get_inverse(settings["material"]): new.MaterialConstituents = [copy_material(file, i) for i in material.MaterialConstituents]
if inverse.is_a("IfcMaterialProperties"):
# Properties must not be shared between objects for convenience of authoring
inverse = ifcopenshell.util.element.copy(file, inverse)
inverse.Material = new
props_attribute = "Properties"
if file.schema == "IFC2X3":
if not inverse.is_a("IfcExtendedMaterialProperties"):
continue
props_attribute = "ExtendedProperties"
props = getattr(inverse, props_attribute)
if not props:
continue
copied_props = []
for pset in props:
copied_props.append(ifcopenshell.util.element.copy_deep(file, pset))
setattr(inverse, props_attribute, copied_props)
elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
inverse = ifcopenshell.util.element.copy_deep(
file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"]
)
inverse.RepresentedMaterial = new
return new return new
elif material.is_a("IfcMaterialConstituent"):
return _copy_material_with_inverses(file, material)
elif material.is_a("IfcMaterialLayerSet"):
new = _copy_material_with_inverses(file, material)
new.MaterialLayers = [copy_material(file, i) for i in material.MaterialLayers]
return new
elif material.is_a("IfcMaterialLayer"):
return _copy_material_with_inverses(file, material)
elif material.is_a("IfcMaterialProfileSet"):
new = _copy_material_with_inverses(file, material)
new.MaterialProfiles = [copy_material(file, i) for i in material.MaterialProfiles]
return new
elif material.is_a("IfcMaterialProfile"):
return _copy_material_with_inverses(file, material)
def _copy_material_with_inverses(file, material):
new = ifcopenshell.util.element.copy(file, material)
for inverse in file.get_inverse(material):
if inverse.is_a("IfcMaterialProperties"):
# Properties must not be shared between objects for convenience of authoring
inverse = ifcopenshell.util.element.copy(file, inverse)
inverse.Material = new
props_attribute = "Properties"
if file.schema == "IFC2X3":
if not inverse.is_a("IfcExtendedMaterialProperties"):
continue
props_attribute = "ExtendedProperties"
props = getattr(inverse, props_attribute)
if not props:
continue
copied_props = []
for pset in props:
copied_props.append(ifcopenshell.util.element.copy_deep(file, pset))
setattr(inverse, props_attribute, copied_props)
elif inverse.is_a("IfcMaterialDefinitionRepresentation"):
inverse = ifcopenshell.util.element.copy_deep(
file, inverse, exclude=["IfcRepresentationContext", "IfcMaterial"]
)
inverse.RepresentedMaterial = new
return new
@@ -72,5 +72,74 @@ class TestCopyMaterial(test.bootstrap.IFC4):
assert new.HasRepresentation[0].Representations[0] != material.HasRepresentation[0].Representations[0] assert new.HasRepresentation[0].Representations[0] != material.HasRepresentation[0].Representations[0]
assert new.HasRepresentation[0].Representations[0].ContextOfItems == context assert new.HasRepresentation[0].Representations[0].ContextOfItems == context
def test_copy_a_material_constituent_set(self):
material = ifcopenshell.api.run("material.add_material", self.file, name="CON01")
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, name="Foo", set_type="IfcMaterialConstituentSet"
)
item = ifcopenshell.api.run(
"material.add_constituent", self.file, constituent_set=material_set, material=material
)
new = ifcopenshell.api.run("material.copy_material", self.file, material=material_set)
assert new != material_set
assert new.Name == "Foo"
assert new.MaterialConstituents[0] != item
assert new.MaterialConstituents[0].Material == material
assert len(self.file.by_type("IfcMaterialConstituentSet")) == 2
assert len(self.file.by_type("IfcMaterialConstituent")) == 2
assert len(self.file.by_type("IfcMaterial")) == 1
def test_copy_a_material_layer_set(self):
material = ifcopenshell.api.run("material.add_material", self.file, name="CON01")
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, name="Foo", set_type="IfcMaterialLayerSet"
)
item = ifcopenshell.api.run("material.add_layer", self.file, layer_set=material_set, material=material)
new = ifcopenshell.api.run("material.copy_material", self.file, material=material_set)
assert new != material_set
assert new.LayerSetName == "Foo"
assert new.MaterialLayers[0] != item
assert new.MaterialLayers[0].Material == material
assert len(self.file.by_type("IfcMaterialLayerSet")) == 2
assert len(self.file.by_type("IfcMaterialLayer")) == 2
assert len(self.file.by_type("IfcMaterial")) == 1
def test_copy_a_material_profile_set(self):
material = ifcopenshell.api.run("material.add_material", self.file, name="CON01")
profile = self.file.create_entity(
"IfcIShapeProfileDef",
ProfileName="HEA100",
ProfileType="AREA",
OverallWidth=100,
OverallDepth=96,
WebThickness=5,
FlangeThickness=8,
FilletRadius=12,
)
material_set = ifcopenshell.api.run(
"material.add_material_set", self.file, name="Foo", set_type="IfcMaterialProfileSet"
)
item = ifcopenshell.api.run(
"material.add_profile", self.file, profile_set=material_set, material=material, profile=profile
)
new = ifcopenshell.api.run("material.copy_material", self.file, material=material_set)
assert new != material_set
assert new.Name == "Foo"
assert new.MaterialProfiles[0] != item
assert new.MaterialProfiles[0].Material == material
assert new.MaterialProfiles[0].Profile == profile
assert len(self.file.by_type("IfcMaterialProfileSet")) == 2
assert len(self.file.by_type("IfcMaterialProfile")) == 2
assert len(self.file.by_type("IfcMaterial")) == 1
assert len(self.file.by_type("IfcProfileDef")) == 1
class TestCopyMaterialIFC2X3(test.bootstrap.IFC2X3, TestCopyMaterial): class TestCopyMaterialIFC2X3(test.bootstrap.IFC2X3, TestCopyMaterial):
pass def test_copy_a_material_constituent_set(self):
return
def test_copy_a_material_profile_set(self):
return