From f5f9862b786bde09aa568fe1c70ba97c13a46abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Mon, 16 Oct 2023 15:31:56 -0300 Subject: [PATCH 1/8] Fix #2187. ifc_duplicate_operator now recreates the IfcElementAssembly structure into a new Assembly --- .../bim/module/geometry/operator.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index bad584b4da..e67ffc3f1e 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -806,6 +806,11 @@ class OverrideDuplicateMove(bpy.types.Operator): if new.is_a("IfcRelSpaceBoundary"): tool.Boundary.decorate_boundary(new_obj) + # Recreate assembly relationship + for old in old_to_new.keys(): + if old.is_a("IfcElementAssembly"): + OverrideDuplicateMove.recreate_assembly(old_to_new) + # Recreate decompositions tool.Root.recreate_decompositions(relationships, old_to_new) blenderbim.bim.handler.refresh_ui_data() @@ -846,6 +851,22 @@ class OverrideDuplicateMove(bpy.types.Operator): return arrays_to_create, array_children + @staticmethod + def recreate_assembly(old_to_new): + for old_element, new_element in old_to_new.items(): + print(old_element, new_element) + old_parent = ifcopenshell.util.element.get_aggregate(old_element) + if old_parent: + new_parent = old_to_new[old_parent] + print(new_parent) + blenderbim.core.aggregate.assign_object( + tool.Ifc, + tool.Aggregate, + tool.Collector, + relating_obj=tool.Ifc.get_object(new_parent[0]), + related_obj=tool.Ifc.get_object(new_element[0]), + ) + class OverrideDuplicateMoveLinkedMacro(bpy.types.Macro): bl_idname = "bim.override_object_duplicate_move_linked_macro" From 0028fd3e9c9538f5e272ff715da9148a7359fba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Mon, 16 Oct 2023 15:39:38 -0300 Subject: [PATCH 2/8] ifc_duplicate_operator now remove the connections with old objects. Similar solutions presented in PR #3789 --- .../blenderbim/bim/module/geometry/operator.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index e67ffc3f1e..820456045a 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -811,6 +811,9 @@ class OverrideDuplicateMove(bpy.types.Operator): if old.is_a("IfcElementAssembly"): OverrideDuplicateMove.recreate_assembly(old_to_new) + # Remove connections with old objects + OverrideDuplicateMove.remove_old_connections(old_to_new) + # Recreate decompositions tool.Root.recreate_decompositions(relationships, old_to_new) blenderbim.bim.handler.refresh_ui_data() @@ -867,6 +870,17 @@ class OverrideDuplicateMove(bpy.types.Operator): related_obj=tool.Ifc.get_object(new_element[0]), ) + def remove_old_connections(old_to_new): + for new in old_to_new.values(): + for connection in new[0].ConnectedTo: + entity = connection.RelatedElement + if entity in old_to_new.keys(): + core.remove_connection(tool.Geometry, connection=connection) + for connection in new[0].ConnectedFrom: + entity = connection.RelatingElement + if entity in old_to_new.keys(): + core.remove_connection(tool.Geometry, connection=connection) + class OverrideDuplicateMoveLinkedMacro(bpy.types.Macro): bl_idname = "bim.override_object_duplicate_move_linked_macro" From f68baee5c3d000d4e24cc43c7a836a3e64e4a9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Mon, 16 Oct 2023 22:17:22 -0300 Subject: [PATCH 3/8] small deletions --- src/blenderbim/blenderbim/bim/module/geometry/operator.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 820456045a..3786206c47 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -857,11 +857,9 @@ class OverrideDuplicateMove(bpy.types.Operator): @staticmethod def recreate_assembly(old_to_new): for old_element, new_element in old_to_new.items(): - print(old_element, new_element) old_parent = ifcopenshell.util.element.get_aggregate(old_element) if old_parent: new_parent = old_to_new[old_parent] - print(new_parent) blenderbim.core.aggregate.assign_object( tool.Ifc, tool.Aggregate, From 37ab9c80e3bc34620e08199e9026344241bbd495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Tue, 17 Oct 2023 18:45:14 -0300 Subject: [PATCH 4/8] Duplication now recreates connections path #3770 --- .../bim/module/geometry/operator.py | 8 +++-- src/blenderbim/blenderbim/tool/root.py | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 3786206c47..1388111994 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -752,7 +752,8 @@ class OverrideDuplicateMove(bpy.types.Operator): self.new_active_obj = None # Track decompositions so they can be recreated after the operation - relationships = tool.Root.get_decomposition_relationships(objects_to_duplicate) + decomposition_relationships = tool.Root.get_decomposition_relationships(objects_to_duplicate) + connection_relationships = tool.Root.get_connection_relationships(objects_to_duplicate) old_to_new = {} for obj in objects_to_duplicate: @@ -811,11 +812,12 @@ class OverrideDuplicateMove(bpy.types.Operator): if old.is_a("IfcElementAssembly"): OverrideDuplicateMove.recreate_assembly(old_to_new) - # Remove connections with old objects + # Remove connections with old objects and recreates paths OverrideDuplicateMove.remove_old_connections(old_to_new) + tool.Root.recreate_connections(connection_relationships, old_to_new) # Recreate decompositions - tool.Root.recreate_decompositions(relationships, old_to_new) + tool.Root.recreate_decompositions(decomposition_relationships, old_to_new) blenderbim.bim.handler.refresh_ui_data() @staticmethod diff --git a/src/blenderbim/blenderbim/tool/root.py b/src/blenderbim/blenderbim/tool/root.py index ce198c9f00..0dc657353f 100644 --- a/src/blenderbim/blenderbim/tool/root.py +++ b/src/blenderbim/blenderbim/tool/root.py @@ -89,6 +89,26 @@ class Root(blenderbim.core.tool.Root): relationships[element] = {"type": "fill", "element": building} return relationships + @classmethod + def get_connection_relationships(cls, objs): + relationships = {} + for obj in objs: + element = tool.Ifc.get_entity(obj) + if not element: + continue + if hasattr(element, "ConnectedTo") and element.ConnectedTo: + paths = [connection for connection in element.ConnectedTo if connection.is_a("IfcRelConnectsPathElements")] + for path in paths: + relationships[element] = {"type": "path", + "related_connection_type": path.RelatedConnectionType, + "related_element": path.RelatedElement, + "related_priorities": path.RelatedPriorities, + "relating_connection_type": path.RelatingConnectionType, + "relating_element": path.RelatingElement, + "relating_priorities": path.RelatingPriorities, + } + return relationships + @classmethod def get_element_representation(cls, element, context): if context.is_a("IfcGeometricRepresentationSubContext"): @@ -197,6 +217,21 @@ class Root(blenderbim.core.tool.Root): should_sync_changes_first=False, ) + @classmethod + def recreate_connections(cls, relationship, old_to_new): + for element, data in relationship.items(): + new_relating_element = old_to_new.get(data["relating_element"])[0] + new_related_element = old_to_new.get(data["related_element"])[0] + ifcopenshell.api.run( + "geometry.connect_path", + tool.Ifc.get(), + relating_element=new_relating_element, + related_element=new_related_element, + relating_connection=data["relating_connection_type"], + related_connection=data["related_connection_type"], + ) + + @classmethod def run_geometry_add_representation( cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None From ef72cd32505b7ecae1efbe93d84669a6961cd4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Mon, 23 Oct 2023 15:46:14 -0300 Subject: [PATCH 5/8] recreates assembly for array children objects --- .../bim/module/geometry/operator.py | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 1388111994..67e3aa1512 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -858,17 +858,30 @@ class OverrideDuplicateMove(bpy.types.Operator): @staticmethod def recreate_assembly(old_to_new): - for old_element, new_element in old_to_new.items(): - old_parent = ifcopenshell.util.element.get_aggregate(old_element) - if old_parent: - new_parent = old_to_new[old_parent] + for old, new in old_to_new.items(): + old_aggregate = ifcopenshell.util.element.get_aggregate(old) + if old_aggregate: + new_aggregate = old_to_new[old_aggregate] blenderbim.core.aggregate.assign_object( tool.Ifc, tool.Aggregate, tool.Collector, - relating_obj=tool.Ifc.get_object(new_parent[0]), - related_obj=tool.Ifc.get_object(new_element[0]), + relating_obj=tool.Ifc.get_object(new_aggregate[0]), + related_obj=tool.Ifc.get_object(new[0]), ) + + # Make sure that the array children also get reassigned to the correct aggregate + pset = ifcopenshell.util.element.get_pset(new[0], "BBIM_Array") + if pset: + array_children = tool.Blender.Modifier.Array.get_all_children_objects(new[0]) + for obj in array_children: + blenderbim.core.aggregate.assign_object( + tool.Ifc, + tool.Aggregate, + tool.Collector, + relating_obj=tool.Ifc.get_object(new_aggregate[0]), + related_obj=tool.Ifc.get_object(tool.Ifc.get_entity(obj)), + ) def remove_old_connections(old_to_new): for new in old_to_new.values(): From af864e8556c5c6722c5486bbe37aa5e510e31029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Mon, 6 Nov 2023 12:15:31 -0300 Subject: [PATCH 6/8] added test scenario for duplicating an aggregate --- .../test/bim/feature/geometry.feature | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index 206769fc64..db809402d2 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -418,6 +418,27 @@ Scenario: Override duplicate move - copying a profiled extrusion Then the object "IfcWall/Cube.001" exists Then the object "IfcWall/Cube.001" has a "SweptSolid" representation of "Model/Body/MODEL_VIEW" +Scenario: Override duplicate move - copying an aggregate + Given an empty IFC project + And I add a cube + And the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_product" to "IfcElement" + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And the object "IfcWall/Cube" is selected + When I press "bim.add_aggregate" + Then the object "IfcWall/Cube" is in the collection "IfcElementAssembly/Assembly" + And the object "IfcElementAssembly/Assembly" is in the collection "IfcElementAssembly/Assembly" + And the collection "IfcElementAssembly/Assembly" is in the collection "IfcBuildingStorey/My Storey" + When the object "IfcWall/Cube" is selected + And additionally the object "IfcElementAssembly/Assembly" is selected + When I duplicate the selected objects + Then the object "IfcWall/Cube.001" exists + And the object "IfcWall/Cube.001" is in the collection "IfcElementAssembly/Assembly.001" + And the object "IfcElementAssembly/Assembly.001" exists + And the object "IfcElementAssembly/Assembly.001" is in the collection "IfcElementAssembly/Assembly.001" + And the collection "IfcElementAssembly/Assembly.001" is in the collection "IfcBuildingStorey/My Storey" + Scenario: Override duplicate move linked - without active IFC data Given an empty Blender session And I add a cube From 4bf0d9ccff41137b68ee01bd89a78fdb74e88816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Tue, 7 Nov 2023 18:19:42 -0300 Subject: [PATCH 7/8] added more tests --- .../test/bim/feature/geometry.feature | 56 +++++++++++++++++++ src/blenderbim/test/bim/test_feature.py | 25 +++++++++ 2 files changed, 81 insertions(+) diff --git a/src/blenderbim/test/bim/feature/geometry.feature b/src/blenderbim/test/bim/feature/geometry.feature index db809402d2..163f0e5c7d 100644 --- a/src/blenderbim/test/bim/feature/geometry.feature +++ b/src/blenderbim/test/bim/feature/geometry.feature @@ -439,6 +439,62 @@ Scenario: Override duplicate move - copying an aggregate And the object "IfcElementAssembly/Assembly.001" is in the collection "IfcElementAssembly/Assembly.001" And the collection "IfcElementAssembly/Assembly.001" is in the collection "IfcBuildingStorey/My Storey" +Scenario: Override duplicate move - copying objects with connection + Given an empty IFC project + And I load the demo construction library + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" + And the variable "element_type" is "[e for e in {ifc}.by_type('IfcWallType') if e.Name == 'WAL100'][0].id()" + And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}" + When I press "bim.add_constr_type_instance" + Then the object "IfcWall/Wall" is an "IfcWall" + And the object "IfcWall/Wall" dimensions are "1,0.1,3" + And the object "IfcWall/Wall" bottom left corner is at "0,0,0" + When I set "scene.BIMModelProperties.ifc_class" to "IfcSlabType" + And the variable "element_type" is "[e for e in {ifc}.by_type('IfcSlabType') if e.Name == 'FLR150'][0].id()" + And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}" + When I press "bim.add_constr_type_instance" + Then the object "IfcSlab/Slab" is an "IfcSlab" + When the object "IfcSlab/Slab" is selected + And the object "IfcSlab/Slab" is moved to "0,0,4" + When I deselect all objects + And the object "IfcWall/Wall" is selected + And additionally the object "IfcSlab/Slab" is selected + When I press "bim.hotkey(hotkey='S_E')" + Then the object "IfcWall/Wall" dimensions are "1,0.1,4" + When I duplicate the selected objects + Then the object "IfcWall/Wall.001" exists + And the variable "wall_name" is "[o.name for o in bpy.context.selected_objects if o.name == 'IfcWall/Wall.001'][0]" + Then the object "IfcSlab/Slab.001" exists + And the variable "slab_name" is "[o.name for o in bpy.context.selected_objects if o.name == 'IfcSlab/Slab.001'][0]" + Then the object "{wall_name}" has a connection with "{slab_name}" + +Scenario: Override duplicate move - copying walls with mitre joint + Given an empty IFC project + And I load the demo construction library + And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType" + And the variable "element_type" is "[e for e in {ifc}.by_type('IfcWallType') if e.Name == 'WAL100'][0].id()" + And I set "scene.BIMModelProperties.relating_type_id" to "{element_type}" + And I press "bim.hotkey(hotkey='S_A')" + And the cursor is at "0.5,0,0" + And I press "bim.hotkey(hotkey='S_A')" + And the object "IfcWall/Wall" is selected + And additionally the object "IfcWall/Wall.001" is selected + When I press "bim.hotkey(hotkey='S_Y')" + Then the object "IfcWall/Wall.001" dimensions are "0.5,0.1,3" + And the object "IfcWall/Wall.001" bottom left corner is at "0.5,0,0" + And the object "IfcWall/Wall" dimensions are "1.1,0.1,3" + And the object "IfcWall/Wall" bottom left corner is at "0.5,0.1,0" + And the object "IfcWall/Wall" top right corner is at "0.6,-1,3" + When I deselect all objects + And the object "IfcWall/Wall" is selected + And additionally the object "IfcWall/Wall.001" is selected + When I duplicate the selected objects + Then the object "IfcWall/Wall.002" exists + And the variable "wall_name1" is "[o.name for o in bpy.context.selected_objects if o.name == 'IfcWall/Wall.002'][0]" + Then the object "IfcWall/Wall.003" exists + And the variable "wall_name2" is "[o.name for o in bpy.context.selected_objects if o.name == 'IfcWall/Wall.003'][0]" + Then the object "{wall_name1}" has a connection with "{wall_name2}" + Scenario: Override duplicate move linked - without active IFC data Given an empty Blender session And I add a cube diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index 9fd1c4317f..ae424f1444 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -837,3 +837,28 @@ def run_pdb(): import pdb pdb.set_trace() + +@then(parsers.parse('the object "{obj_name1}" has a connection with "{obj_name2}"')) +def the_obj1_has_a_connection_with_obj2(obj_name1, obj_name2): + element1 = replace_variables(obj_name1) + element1 = tool.Ifc.get_entity(the_object_name_exists(element1)) + element2 = replace_variables(obj_name2) + element2 = tool.Ifc.get_entity(the_object_name_exists(element2)) + connections = [] + if hasattr(element1, "ConnectedTo") and element1.ConnectedTo: + connections = [connection for connection in element1.ConnectedTo] + elif hasattr(element1, "ConnectedFrom") and element1.ConnectedFrom: + connections = [connection for connection in element1.ConnectedFrom] + else: + assert False, f'Object "{obj_name1}" has no connections' + + + relationships = {} + for conn in connections: + relationships[conn.RelatedElement] = conn.RelatingElement + + for key, value in relationships.items(): + print(key, value) + # assert False, f"1-{key} and {element1.id()} and {key.id()}" + assert (key.id() == element1.id() and value.id() == element2.id()) or (key.id() == element2.id() and value.id() == element1.id()), f"The object {obj_name1} is connected to {obj_name2}" + From 61bd98e7dbf8d5f05a9bffeebe26570f54e5f23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Perdig=C3=A3o?= Date: Tue, 7 Nov 2023 18:28:43 -0300 Subject: [PATCH 8/8] moved 'recreate_assembly' to tool/root.py --- .../bim/module/geometry/operator.py | 52 +++++++++---------- src/blenderbim/blenderbim/tool/root.py | 28 ++++++++++ 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index 67e3aa1512..92627c7ebc 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -810,7 +810,7 @@ class OverrideDuplicateMove(bpy.types.Operator): # Recreate assembly relationship for old in old_to_new.keys(): if old.is_a("IfcElementAssembly"): - OverrideDuplicateMove.recreate_assembly(old_to_new) + tool.Root.recreate_assembly(old_to_new) # Remove connections with old objects and recreates paths OverrideDuplicateMove.remove_old_connections(old_to_new) @@ -856,32 +856,32 @@ class OverrideDuplicateMove(bpy.types.Operator): return arrays_to_create, array_children - @staticmethod - def recreate_assembly(old_to_new): - for old, new in old_to_new.items(): - old_aggregate = ifcopenshell.util.element.get_aggregate(old) - if old_aggregate: - new_aggregate = old_to_new[old_aggregate] - blenderbim.core.aggregate.assign_object( - tool.Ifc, - tool.Aggregate, - tool.Collector, - relating_obj=tool.Ifc.get_object(new_aggregate[0]), - related_obj=tool.Ifc.get_object(new[0]), - ) + # @staticmethod + # def recreate_assembly(old_to_new): + # for old, new in old_to_new.items(): + # old_aggregate = ifcopenshell.util.element.get_aggregate(old) + # if old_aggregate: + # new_aggregate = old_to_new[old_aggregate] + # blenderbim.core.aggregate.assign_object( + # tool.Ifc, + # tool.Aggregate, + # tool.Collector, + # relating_obj=tool.Ifc.get_object(new_aggregate[0]), + # related_obj=tool.Ifc.get_object(new[0]), + # ) - # Make sure that the array children also get reassigned to the correct aggregate - pset = ifcopenshell.util.element.get_pset(new[0], "BBIM_Array") - if pset: - array_children = tool.Blender.Modifier.Array.get_all_children_objects(new[0]) - for obj in array_children: - blenderbim.core.aggregate.assign_object( - tool.Ifc, - tool.Aggregate, - tool.Collector, - relating_obj=tool.Ifc.get_object(new_aggregate[0]), - related_obj=tool.Ifc.get_object(tool.Ifc.get_entity(obj)), - ) + # # Make sure that the array children also get reassigned to the correct aggregate + # pset = ifcopenshell.util.element.get_pset(new[0], "BBIM_Array") + # if pset: + # array_children = tool.Blender.Modifier.Array.get_all_children_objects(new[0]) + # for obj in array_children: + # blenderbim.core.aggregate.assign_object( + # tool.Ifc, + # tool.Aggregate, + # tool.Collector, + # relating_obj=tool.Ifc.get_object(new_aggregate[0]), + # related_obj=tool.Ifc.get_object(tool.Ifc.get_entity(obj)), + # ) def remove_old_connections(old_to_new): for new in old_to_new.values(): diff --git a/src/blenderbim/blenderbim/tool/root.py b/src/blenderbim/blenderbim/tool/root.py index 0dc657353f..364c01a276 100644 --- a/src/blenderbim/blenderbim/tool/root.py +++ b/src/blenderbim/blenderbim/tool/root.py @@ -230,6 +230,34 @@ class Root(blenderbim.core.tool.Root): relating_connection=data["relating_connection_type"], related_connection=data["related_connection_type"], ) + + + @classmethod + def recreate_assembly(cls, old_to_new): + for old, new in old_to_new.items(): + old_aggregate = ifcopenshell.util.element.get_aggregate(old) + if old_aggregate: + new_aggregate = old_to_new[old_aggregate] + blenderbim.core.aggregate.assign_object( + tool.Ifc, + tool.Aggregate, + tool.Collector, + relating_obj=tool.Ifc.get_object(new_aggregate[0]), + related_obj=tool.Ifc.get_object(new[0]), + ) + + # Make sure that the array children also get reassigned to the correct aggregate + pset = ifcopenshell.util.element.get_pset(new[0], "BBIM_Array") + if pset: + array_children = tool.Blender.Modifier.Array.get_all_children_objects(new[0]) + for obj in array_children: + blenderbim.core.aggregate.assign_object( + tool.Ifc, + tool.Aggregate, + tool.Collector, + relating_obj=tool.Ifc.get_object(new_aggregate[0]), + related_obj=tool.Ifc.get_object(tool.Ifc.get_entity(obj)), + ) @classmethod