Merge branch 'improvements-on-override-duplicate' into refactor-duplicate-aggregate

This commit is contained in:
Bruno Perdigão
2023-11-20 16:24:16 -03:00
4 changed files with 215 additions and 2 deletions
@@ -760,7 +760,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:
@@ -815,8 +816,17 @@ 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"):
tool.Root.recreate_assembly(old_to_new)
# 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()
return old_to_new
@@ -856,6 +866,44 @@ 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]),
# )
# # 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():
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"
+63
View File
@@ -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,49 @@ 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 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
def run_geometry_add_representation(
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
@@ -418,6 +418,83 @@ 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 - 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
+25
View File
@@ -862,3 +862,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}"