Fix critical bug where copied representations would be inefficiently or incorrectly replicated.

This commit is contained in:
Dion Moult
2023-03-19 22:03:45 +11:00
parent cfd857233c
commit 088354a96b
2 changed files with 37 additions and 3 deletions
@@ -808,7 +808,7 @@ def copy(ifc_file, element):
return new return new
def copy_deep(ifc_file, element, exclude=None, exclude_callback=None): def copy_deep(ifc_file, element, exclude=None, exclude_callback=None, copied_entities=None):
""" """
Recursively copy an element and all of its directly related subelements. Recursively copy an element and all of its directly related subelements.
@@ -825,10 +825,22 @@ def copy_deep(ifc_file, element, exclude=None, exclude_callback=None):
:param exclude_callback: A callback to determine whether or not to exclude :param exclude_callback: A callback to determine whether or not to exclude
an entity or not. Returns True to exclude and False to exclude. an entity or not. Returns True to exclude and False to exclude.
:type exclude_callback: function,optional :type exclude_callback: function,optional
:param copied_entities: A dictionary of IDs as keys and entities as values
to reuse when coming across the same entity twice. This can typically
be left as None.
:type copied_entities: dict[int:ifcopenshell.entity_instance.entity_instance]
:return: The newly copied element :return: The newly copied element
:rtype: ifcopenshell.entity_instance.entity_instance :rtype: ifcopenshell.entity_instance.entity_instance
""" """
if copied_entities is None:
copied_entities = {}
else:
copied_entity = copied_entities.get(element.id(), None)
if copied_entity:
return copied_entity
new = ifc_file.create_entity(element.is_a()) new = ifc_file.create_entity(element.is_a())
if element.id():
copied_entities[element.id()] = new
for i, attribute in enumerate(element): for i, attribute in enumerate(element):
if attribute is None: if attribute is None:
continue continue
@@ -838,7 +850,7 @@ def copy_deep(ifc_file, element, exclude=None, exclude_callback=None):
elif exclude_callback and exclude_callback(attribute): elif exclude_callback and exclude_callback(attribute):
pass pass
else: else:
attribute = copy_deep(ifc_file, attribute, exclude=exclude) attribute = copy_deep(ifc_file, attribute, exclude=exclude, copied_entities=copied_entities)
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):
if exclude and any([attribute[0].is_a(e) for e in exclude]): if exclude and any([attribute[0].is_a(e) for e in exclude]):
pass pass
@@ -847,7 +859,13 @@ def copy_deep(ifc_file, element, exclude=None, exclude_callback=None):
else: else:
attribute = list(attribute) attribute = list(attribute)
for j, item in enumerate(attribute): for j, item in enumerate(attribute):
attribute[j] = copy_deep(ifc_file, item, exclude=exclude, exclude_callback=exclude_callback) attribute[j] = copy_deep(
ifc_file,
item,
exclude=exclude,
exclude_callback=exclude_callback,
copied_entities=copied_entities,
)
if new.attribute_name(i) == "GlobalId": if new.attribute_name(i) == "GlobalId":
new[i] = ifcopenshell.guid.new() new[i] = ifcopenshell.guid.new()
else: else:
@@ -707,3 +707,19 @@ class TestCopyDeepIFC4(test.bootstrap.IFC4):
rel.RelatedObjects = [element] rel.RelatedObjects = [element]
rel2 = subject.copy_deep(self.file, rel, exclude_callback=lambda x: x.is_a("IfcWall")) rel2 = subject.copy_deep(self.file, rel, exclude_callback=lambda x: x.is_a("IfcWall"))
assert rel.RelatedObjects == rel2.RelatedObjects assert rel.RelatedObjects == rel2.RelatedObjects
def test_copying_and_reusing_element_references(self):
points = self.file.createIfcCartesianPointList2D()
subelement1 = self.file.createIfcIndexedPolyCurve(points)
subelement2 = self.file.createIfcIndexedPolyCurve(points)
element = self.file.createIfcGeometricCurveSet([subelement1, subelement2])
element2 = subject.copy_deep(self.file, element)
assert element2.Elements[0].Points.id() == element2.Elements[1].Points.id()
def test_copying_primitive_entities(self):
element = self.file.createIfcIndexedPolyCurve(
Segments=(self.file.createIfcLineIndex((1, 2)), self.file.createIfcLineIndex((3, 4)))
)
element2 = subject.copy_deep(self.file, element)
assert element2.Segments[0][0] == (1, 2)
assert element2.Segments[1][0] == (3, 4)