From 1c573aee32038426b2a3bded46165796d4269da8 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 9 Jan 2024 12:57:18 +1100 Subject: [PATCH] Fix #4175. Add new UI for managing IFC object and edit modes. --- .../bim/module/geometry/__init__.py | 2 + .../bim/module/geometry/operator.py | 88 ++++++++++--------- .../blenderbim/bim/module/geometry/prop.py | 19 ++++ .../blenderbim/bim/module/geometry/ui.py | 5 ++ 4 files changed, 72 insertions(+), 42 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/__init__.py b/src/blenderbim/blenderbim/bim/module/geometry/__init__.py index db955f013f..9faf7dbe59 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/__init__.py @@ -83,6 +83,7 @@ def register(): bpy.types.OUTLINER_MT_object.append(ui.outliner_menu) bpy.types.VIEW3D_MT_object_context_menu.append(ui.object_menu) bpy.types.VIEW3D_MT_edit_mesh.append(ui.edit_mesh_menu) + bpy.types.VIEW3D_HT_header.append(ui.mode_menu) wm = bpy.context.window_manager if wm.keyconfigs.addon: km = wm.keyconfigs.addon.keymaps.new(name="Object Mode", space_type="EMPTY") @@ -129,6 +130,7 @@ def unregister(): bpy.types.OUTLINER_MT_object.remove(ui.outliner_menu) bpy.types.VIEW3D_MT_object_context_menu.remove(ui.outliner_menu) bpy.types.VIEW3D_MT_edit_mesh.remove(ui.edit_mesh_menu) + bpy.types.VIEW3D_HT_header.remove(ui.mode_menu) del bpy.types.Scene.BIMGeometryProperties del bpy.types.Object.BIMGeometryProperties wm = bpy.context.window_manager diff --git a/src/blenderbim/blenderbim/bim/module/geometry/operator.py b/src/blenderbim/blenderbim/bim/module/geometry/operator.py index ca27e93d35..425be29e82 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/operator.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/operator.py @@ -822,7 +822,7 @@ class OverrideDuplicateMove(bpy.types.Operator): # Recreate aggregate relationship for old in old_to_new.keys(): - if old.is_a("IfcElementAssembly"): + if old.is_a("IfcElementAssembly"): tool.Root.recreate_aggregate(old_to_new) # Remove connections with old objects and recreates paths @@ -870,7 +870,6 @@ class OverrideDuplicateMove(bpy.types.Operator): return arrays_to_create, array_children - def remove_old_connections(old_to_new): for new in old_to_new.values(): for connection in new[0].ConnectedTo: @@ -934,7 +933,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): self.pset_name = "BBIM_Linked_Aggregate" old_to_new = {} - def select_objects_and_add_data(element): + def select_objects_and_add_data(element): add_linked_aggregate_group(element) obj = tool.Ifc.get_object(element) obj.select_set(True) @@ -947,7 +946,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): add_linked_aggregate_pset(part, index) index += 1 - + obj = tool.Ifc.get_object(part) obj.select_set(True) @@ -960,19 +959,15 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): ] if self.group_name in product_groups_name: return - + linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.group_name) - ifcopenshell.api.run( - "group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group - ) + ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group) def add_linked_aggregate_pset(part, index): pset = ifcopenshell.util.element.get_pset(part, self.pset_name) - + if not pset: - pset = ifcopenshell.api.run( - "pset.add_pset", tool.Ifc.get(), product=part, name=self.pset_name - ) + pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=part, name=self.pset_name) else: pset = tool.Ifc.get().by_id(pset["id"]) @@ -984,8 +979,7 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): ) return index - - + if len(context.selected_objects) != 1: return {"FINISHED"} @@ -1002,18 +996,15 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator): self.report({"INFO"}, "Object is not part of a IfcElementAssembly.") return {"FINISHED"} - select_objects_and_add_data(selected_element) old_to_new = OverrideDuplicateMove.execute_ifc_duplicate_operator(self, context, linked=True) - # Recreate aggregate relationship for old in old_to_new.keys(): - if old.is_a("IfcElementAssembly"): + if old.is_a("IfcElementAssembly"): tool.Root.recreate_aggregate(old_to_new) - blenderbim.bim.handler.refresh_ui_data() return old_to_new @@ -1038,7 +1029,7 @@ class RefreshLinkedAggregate(bpy.types.Operator): def _execute(self, context): self.new_active_obj = None - self.group_name = 'BBIM_Linked_Aggregate' + self.group_name = "BBIM_Linked_Aggregate" self.pset_name = "BBIM_Linked_Aggregate" refresh_start_time = time() old_to_new = {} @@ -1050,9 +1041,9 @@ class RefreshLinkedAggregate(bpy.types.Operator): for part in parts: if part.is_a("IfcElementAssembly"): delete_objects(part) - + tool.Geometry.delete_ifc_object(tool.Ifc.get_object(part)) - + tool.Geometry.delete_ifc_object(tool.Ifc.get_object(element)) def get_element_assembly(element): @@ -1093,7 +1084,7 @@ class RefreshLinkedAggregate(bpy.types.Operator): except: self.report({"INFO"}, "Object is not part of a Linked Aggregate.") return None, None - + return list(set(linked_aggregate_groups)), selected_parents @@ -1104,7 +1095,7 @@ class RefreshLinkedAggregate(bpy.types.Operator): if not active_element: self.report({"INFO"}, "Object has no Ifc metadata.") return {"FINISHED"} - + active_element = get_element_assembly(active_element) selected_objs = context.selected_objects linked_aggregate_groups, selected_parents = handle_selection(selected_objs) @@ -1113,7 +1104,10 @@ class RefreshLinkedAggregate(bpy.types.Operator): if len(linked_aggregate_groups) > 1: if len(selected_parents) != len(linked_aggregate_groups): - self.report({"INFO"}, "Select only one object from each Linked Aggregate or multiple objects from the same Linked Aggregate.") + self.report( + {"INFO"}, + "Select only one object from each Linked Aggregate or multiple objects from the same Linked Aggregate.", + ) return {"FINISHED"} for group in linked_aggregate_groups: @@ -1121,15 +1115,15 @@ class RefreshLinkedAggregate(bpy.types.Operator): if len(linked_aggregate_groups) > 1: base_instance = [e for e in elements if e in selected_parents][0] instances_to_refresh = elements - + elif (len(linked_aggregate_groups) == 1) and (len(selected_parents) > 1): base_instance = active_element instances_to_refresh = [element for element in elements if element in selected_parents] - + else: base_instance = active_element instances_to_refresh = elements - + for element in instances_to_refresh: if element.GlobalId == base_instance.GlobalId: continue @@ -1140,34 +1134,34 @@ class RefreshLinkedAggregate(bpy.types.Operator): selected_matrix = selected_obj.matrix_world object_duplicate = tool.Ifc.get_object(element) duplicate_matrix = object_duplicate.matrix_world.decompose() - + delete_objects(element) - + for obj in context.selected_objects: obj.select_set(False) - + tool.Ifc.get_object(base_instance).select_set(True) - + old_to_new = DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(self, context) for old, new in old_to_new.items(): new_obj = tool.Ifc.get_object(new[0]) new_base_matrix = Matrix.LocRotScale(*duplicate_matrix) - matrix_diff = Matrix.inverted(selected_matrix) @ new_obj.matrix_world + matrix_diff = Matrix.inverted(selected_matrix) @ new_obj.matrix_world new_obj_matrix = new_base_matrix @ matrix_diff new_obj.matrix_world = new_obj_matrix - + if element_aggregate and new[0].is_a("IfcElementAssembly"): new_aggregate = ifcopenshell.util.element.get_aggregate(new[0]) - + if not new_aggregate: blenderbim.core.aggregate.assign_object( - tool.Ifc, - tool.Aggregate, - tool.Collector, - relating_obj=tool.Ifc.get_object(element_aggregate), - related_obj=tool.Ifc.get_object(new[0]), - ) - + tool.Ifc, + tool.Aggregate, + tool.Collector, + relating_obj=tool.Ifc.get_object(element_aggregate), + related_obj=tool.Ifc.get_object(new[0]), + ) + blenderbim.bim.handler.refresh_ui_data() operator_time = time() - refresh_start_time @@ -1393,7 +1387,12 @@ class OverrideModeSetEdit(bpy.types.Operator): # The active object is non-mesh-like. Set a valid object (or None) as active context.view_layer.objects.active = context.selected_objects[0] if context.selected_objects else None if context.active_object: - return tool.Blender.toggle_edit_mode(context) + tool.Blender.toggle_edit_mode(context) + context.scene.BIMGeometryProperties.is_changing_mode = True + if context.scene.BIMGeometryProperties.mode != "EDIT": + context.scene.BIMGeometryProperties.mode = "EDIT" + context.scene.BIMGeometryProperties.is_changing_mode = False + return {"FINISHED"} # Restore the selection if nothing worked for obj in selected_objs: @@ -1464,6 +1463,11 @@ class OverrideModeSetObject(bpy.types.Operator): bpy.ops.object.mode_set(mode="EDIT", toggle=True) + context.scene.BIMGeometryProperties.is_changing_mode = True + if context.scene.BIMGeometryProperties.mode != "OBJECT": + context.scene.BIMGeometryProperties.mode = "OBJECT" + context.scene.BIMGeometryProperties.is_changing_mode = False + if not tool.Ifc.get(): return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/geometry/prop.py b/src/blenderbim/blenderbim/bim/module/geometry/prop.py index f874172d28..b3a676e0d3 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/prop.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/prop.py @@ -38,6 +38,15 @@ def get_contexts(self, context): return RepresentationsData.data["contexts"] +def update_mode(self, context): + if self.is_changing_mode: + return + if self.mode == "OBJECT": + bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT") + elif self.mode == "EDIT": + bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT") + + class RepresentationItem(PropertyGroup): name: StringProperty(name="Name") surface_style: StringProperty(name="Surface Style") @@ -59,3 +68,13 @@ class BIMGeometryProperties(PropertyGroup): should_force_faceted_brep: BoolProperty(name="Force Faceted Breps", default=False) # Navisworks workaround should_force_triangulation: BoolProperty(name="Force Triangulation", default=False) + is_changing_mode: BoolProperty(name="Is Changing Mode", default=False) + mode: EnumProperty( + default="OBJECT", + items=[ + ("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0), + ("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 1), + ], + name="IFC Interaction Mode", + update=update_mode, + ) diff --git a/src/blenderbim/blenderbim/bim/module/geometry/ui.py b/src/blenderbim/blenderbim/bim/module/geometry/ui.py index 46ca82e9ec..a725a89118 100644 --- a/src/blenderbim/blenderbim/bim/module/geometry/ui.py +++ b/src/blenderbim/blenderbim/bim/module/geometry/ui.py @@ -31,6 +31,11 @@ from blenderbim.bim.module.geometry.data import ( ) +def mode_menu(self, context): + row = self.layout.row(align=True) + row.prop(context.scene.BIMGeometryProperties, "mode", text="", icon_value=blenderbim.bim.icons["IFC"].icon_id) + + def object_menu(self, context): self.layout.separator() self.layout.operator("bim.override_object_duplicate_move", icon="PLUGIN")