From 199be3363f8a7fdd4a3b12fa63559430089eca51 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 14 Oct 2021 16:13:48 +1100 Subject: [PATCH] Fix bug where copying elements shared a placement. This fix speeds up copying parent elements in the decomposition significantly, like when copying a storey. --- .../ifcopenshell/api/root/copy_class.py | 12 +++++++++-- .../test/api/root/test_copy_class.py | 20 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py index 045285f45e..db85ff5efc 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/copy_class.py @@ -12,11 +12,14 @@ class Usecase: def execute(self): self.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.file.schema) result = ifcopenshell.util.element.copy(self.file, self.settings["product"]) + self.copy_direct_attributes(result) self.copy_indirect_attributes(self.settings["product"], result) - # Copying representations is too hard, so for now we just don't do it. - self.remove_representations(result) return result + def copy_direct_attributes(self, to_element): + self.remove_representations(to_element) + self.copy_object_placements(to_element) + def copy_indirect_attributes(self, from_element, to_element): for inverse in self.file.get_inverse(from_element): if inverse.is_a("IfcRelDefinesByProperties"): @@ -46,3 +49,8 @@ class Usecase: element.Representation = None elif element.is_a("IfcTypeProduct"): element.RepresentationMaps = None + + def copy_object_placements(self, element): + if not element.is_a("IfcProduct") or not element.ObjectPlacement: + return + element.ObjectPlacement = ifcopenshell.util.element.copy(self.file, element.ObjectPlacement) diff --git a/src/ifcopenshell-python/test/api/root/test_copy_class.py b/src/ifcopenshell-python/test/api/root/test_copy_class.py index e731ec783b..728faae499 100644 --- a/src/ifcopenshell-python/test/api/root/test_copy_class.py +++ b/src/ifcopenshell-python/test/api/root/test_copy_class.py @@ -1,3 +1,4 @@ +import numpy import test.bootstrap import ifcopenshell.api @@ -10,7 +11,20 @@ class TestCopyClass(test.bootstrap.IFC4): assert new.GlobalId != element.GlobalId assert new.is_a("IfcWall") - def test_copying_an_element_with_properties(self): + def test_copying_object_placements_so_children_of_the_original_element_dont_reference_the_new_element(self): + ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject") + ifcopenshell.api.run("unit.assign_unit", self.file) + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") + subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") + ifcopenshell.api.run("spatial.assign_container", self.file, product=subelement, relating_structure=element) + matrix = numpy.identity(4) + ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=element, matrix=matrix.copy()) + ifcopenshell.api.run("geometry.edit_object_placement", self.file, product=subelement, matrix=matrix.copy()) + new = ifcopenshell.api.run("root.copy_class", self.file, product=element) + assert subelement.ObjectPlacement.PlacementRelTo == element.ObjectPlacement + assert subelement.ObjectPlacement.PlacementRelTo != new.ObjectPlacement + + def test_copying_psets_so_changing_properties_of_the_new_element_does_not_affect_the_old(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foobar") ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"foo": "bar"}) @@ -32,7 +46,7 @@ class TestCopyClass(test.bootstrap.IFC4): assert element.ContainsElements assert not new.ContainsElements - def test_copying_contents_of_a_container(self): + def test_copying_contents_of_a_container_and_maintaining_the_containment_relationship(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") ifcopenshell.api.run("spatial.assign_container", self.file, product=subelement, relating_structure=element) @@ -55,7 +69,7 @@ class TestCopyClass(test.bootstrap.IFC4): assert element.IsDecomposedBy assert not new.IsDecomposedBy - def test_copying_an_aggregate_decomposition(self): + def test_copying_an_aggregate_decomposition_and_maintaining_the_aggregate_relationship(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly") subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBeam") ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element)