Copy deep can now exclude things, useful for copying representation graphs where you don't want to duplicate the context

This commit is contained in:
Dion Moult
2022-10-09 17:15:39 +11:00
parent 3ee4b1f0e3
commit 232252f61d
2 changed files with 56 additions and 11 deletions
@@ -190,13 +190,17 @@ def get_elements_by_material(ifc_file, material):
""" """
Retrieves the elements related to a material. Retrieves the elements related to a material.
This includes elements using the material as part of a material set or set
usage.
:param ifc_file: The IFC file :param ifc_file: The IFC file
:param material: The IFC Material entity :param material: The IFC Material entity
:return: The elements related to the material :return: A list of elements using the to the material
Example:: Example::
material = file.by_type("IfcMaterial")[0]
elements = ifcopenshell.util.element.get_elements_by_material(file, material) material = file.by_type("IfcMaterial")[0]
elements = ifcopenshell.util.element.get_elements_by_material(file, material)
""" """
results = set() results = set()
for inverse in ifc_file.get_inverse(material): for inverse in ifc_file.get_inverse(material):
@@ -229,9 +233,9 @@ def get_elements_by_style(ifc_file, style):
:return: The elements related to the style :return: The elements related to the style
Example:: Example::
style = file.by_type("IfcSurfaceStyle")[0]
elements = ifcopenshell.util.element.get_elements_by_style(file, style)
style = file.by_type("IfcSurfaceStyle")[0]
elements = ifcopenshell.util.element.get_elements_by_style(file, style)
""" """
results = set() results = set()
inverses = list(ifc_file.get_inverse(style)) inverses = list(ifc_file.get_inverse(style))
@@ -482,6 +486,15 @@ def remove_deep2(ifc_file, element, also_consider=[], do_not_delete=[]):
def copy(ifc_file, element): def copy(ifc_file, element):
"""
Copy a single element. Any referenced elements are not copied.
GlobalIds are regenerated.
:param ifc_file: The IFC file object
:param element: The IFC element to copy
:return: The newly copied element
"""
new = ifc_file.create_entity(element.is_a()) new = ifc_file.create_entity(element.is_a())
for i, attribute in enumerate(element): for i, attribute in enumerate(element):
if attribute is None: if attribute is None:
@@ -493,16 +506,33 @@ def copy(ifc_file, element):
return new return new
def copy_deep(ifc_file, element): def copy_deep(ifc_file, element, exclude=None):
"""
Recursively copy an element and all of its directly related subelements.
GlobalIds are regenerated.
:param ifc_file: The IFC file object
:param element: The IFC element to copy
:param exclude: An optional list of strings of IFC class names to not copy.
If any of the subelement is this class, it will not be copied and the
original instance will be referenced.
:return: The newly copied element
"""
new = ifc_file.create_entity(element.is_a()) new = ifc_file.create_entity(element.is_a())
for i, attribute in enumerate(element): for i, attribute in enumerate(element):
if attribute is None: if attribute is None:
continue continue
if isinstance(attribute, ifcopenshell.entity_instance): if isinstance(attribute, ifcopenshell.entity_instance):
attribute = copy_deep(ifc_file, attribute) if not exclude or (exclude and not any([attribute.is_a(e) for e in exclude])):
attribute = copy_deep(ifc_file, attribute, exclude=exclude)
elif isinstance(attribute, tuple) and attribute and isinstance(attribute[0], ifcopenshell.entity_instance): elif isinstance(attribute, tuple) and attribute and isinstance(attribute[0], ifcopenshell.entity_instance):
attribute = list(attribute) if not exclude or (exclude and not any([attribute[0].is_a(e) for e in exclude])):
for j, item in enumerate(attribute): attribute = list(attribute)
attribute[j] = copy_deep(ifc_file, item) for j, item in enumerate(attribute):
new[i] = attribute attribute[j] = copy_deep(ifc_file, item, exclude=exclude)
if new.attribute_name(i) == "GlobalId":
new[i] = ifcopenshell.guid.new()
else:
new[i] = attribute
return new return new
@@ -668,6 +668,7 @@ class TestCopyDeepIFC4(test.bootstrap.IFC4):
element = self.file.createIfcWall(GlobalId="id", Name="name", OwnerHistory=owner) element = self.file.createIfcWall(GlobalId="id", Name="name", OwnerHistory=owner)
element2 = subject.copy_deep(self.file, element) element2 = subject.copy_deep(self.file, element)
assert element.OwnerHistory != element2.OwnerHistory assert element.OwnerHistory != element2.OwnerHistory
assert element.GlobalId != element2.GlobalId
assert element.OwnerHistory.State == element2.OwnerHistory.State assert element.OwnerHistory.State == element2.OwnerHistory.State
def test_copying_an_element_recursively_even_if_references_are_aggregated(self): def test_copying_an_element_recursively_even_if_references_are_aggregated(self):
@@ -677,3 +678,17 @@ class TestCopyDeepIFC4(test.bootstrap.IFC4):
rel2 = subject.copy_deep(self.file, rel) rel2 = subject.copy_deep(self.file, rel)
assert rel.RelatedObjects != rel2.RelatedObjects assert rel.RelatedObjects != rel2.RelatedObjects
assert rel.RelatedObjects[0].Name == rel2.RelatedObjects[0].Name assert rel.RelatedObjects[0].Name == rel2.RelatedObjects[0].Name
def test_copying_an_element_recursively_with_an_exclude_filter(self):
owner = self.file.createIfcOwnerHistory()
owner.State = "READWRITE"
element = self.file.createIfcWall(GlobalId="id", Name="name", OwnerHistory=owner)
element2 = subject.copy_deep(self.file, element, exclude=["IfcOwnerHistory"])
assert element.OwnerHistory == element2.OwnerHistory
def test_copying_an_element_recursively_with_aggregates_with_an_exclude_filter(self):
element = self.file.createIfcWall(Name="name")
rel = self.file.createIfcRelAggregates()
rel.RelatedObjects = [element]
rel2 = subject.copy_deep(self.file, rel, exclude=["IfcWall"])
assert rel.RelatedObjects == rel2.RelatedObjects