Rewrite mode switching from object to item to edit modes

This commit is contained in:
Dion Moult
2024-09-22 22:17:00 +10:00
parent df494a3def
commit 14fe3e6150
5 changed files with 151 additions and 155 deletions
+43 -10
View File
@@ -46,18 +46,51 @@ class ViewportData:
@classmethod
def mode(cls):
obj_mode = ("OBJECT", "IFC Object Mode", "View and move the placements of objects", "OBJECT_DATAMODE", 0)
item_mode = ("ITEM", "IFC Item Mode", "View individual representation items", "MESH_DATA", 1)
edit_mode = ("EDIT", "IFC Edit Mode", "Edit representation items", "EDITMODE_HLT", 2)
obj = bpy.context.active_object
modes = [
("OBJECT", "IFC Object Mode", "", "OBJECT_DATAMODE", 0),
("ITEM", "IFC Item Mode", "", "MESH_DATA", 1),
]
if (
not obj
or not tool.Blender.is_editable(obj)
or ((element := tool.Ifc.get_entity(obj)) and tool.Geometry.is_locked(element))
):
print("checking", obj)
element = tool.Ifc.get_entity(obj)
modes = [obj_mode]
if bpy.context.scene.BIMGeometryProperties.representation_obj:
modes.append(item_mode)
if not obj:
return modes
modes.append(("EDIT", "IFC Edit Mode", "", "EDITMODE_HLT", 2))
if obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
pass
elif element:
if (usage := tool.Model.get_usage_type(element)) and usage in ("LAYER1", "LAYER2"):
pass
elif tool.Geometry.is_locked(element):
pass
elif usage == "PROFILE":
modes.append(edit_mode)
elif usage == "LAYER3":
modes.append(edit_mode)
elif obj.data and tool.Geometry.is_profile_based(obj.data):
modes.append(edit_mode)
elif element.is_a("IfcRelSpaceBoundary"):
modes.append(edit_mode)
elif element.is_a("IfcGridAxis"):
modes.append(edit_mode)
elif tool.Blender.Modifier.is_editing_parameters(obj):
# This should go BEFORE the modifiers
pass
elif tool.Blender.Modifier.is_roof(element):
modes.append(edit_mode)
elif tool.Blender.Modifier.is_railing(element):
modes.append(edit_mode)
elif item_mode not in modes:
modes.append(item_mode)
elif tool.Geometry.is_representation_item(obj):
modes.append(edit_mode)
return modes
+77 -135
View File
@@ -1576,135 +1576,91 @@ class OverridePasteBuffer(bpy.types.Operator):
class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
bl_description = "Switch from Object mode to Edit mode"
bl_description = "Switch from Object to Item to Edit mode"
bl_idname = "bim.override_mode_set_edit"
bl_label = "IFC Mode Set Edit"
bl_options = {"REGISTER", "UNDO"}
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
if not tool.Ifc.get():
return tool.Blender.toggle_edit_mode(context)
return self.execute(context)
def _execute(self, context):
selected_objs = tool.Blender.get_selected_objects()
active_obj = context.active_object
selected_objs = context.selected_objects # Purposely exclude active object
if context.active_object:
context.active_object.select_set(True)
if len(selected_objs) == 1 and context.active_object:
self.handle_single_object(context, context.active_object)
elif len(selected_objs) == 0:
tool.Geometry.disable_item_mode()
elif len(selected_objs) > 1:
self.handle_multiple_selected_objects(context)
element = tool.Ifc.get_entity(context.active_object)
if element and element.is_a("IfcRelSpaceBoundary"):
return bpy.ops.bim.enable_editing_boundary_geometry()
for obj in selected_objs:
if obj == context.scene.BIMGeometryProperties.representation_obj:
self.report({"ERROR"}, f"Element '{obj.name}' is in item mode and cannot be edited directly.")
obj.select_set(False)
continue
if tool.Geometry.is_representation_item(obj):
return self.enable_editing_representation_item(context, obj)
if not tool.Blender.is_editable(obj):
self.report({"INFO"}, f"Object cannot be edited: {obj.name}")
obj.select_set(False)
continue
element = tool.Ifc.get_entity(obj)
if not element:
continue
if tool.Geometry.is_locked(element):
self.report({"ERROR"}, f"Element '{obj.name}' is locked and cannot be edited.")
obj.select_set(False)
continue
representation = tool.Geometry.get_active_representation(obj)
if not representation:
continue
if tool.Blender.Modifier.is_modifier_with_non_editable_path(element):
obj.select_set(False)
continue
usage_type = tool.Model.get_usage_type(element)
is_profile = True
representation_class = tool.Geometry.get_ifc_representation_class(element, representation)
if usage_type == "PROFILE":
operator = lambda: bpy.ops.bim.hotkey(hotkey="A_E")
elif representation_class and "IfcCircleProfileDef" in representation_class:
self.report({"INFO"}, "Can't edit Circle Profile Extrusion")
obj.select_set(False)
continue
elif (
tool.Geometry.is_profile_based(obj.data)
or usage_type == "LAYER3"
or tool.Geometry.is_swept_profile(representation)
):
operator = lambda: bpy.ops.bim.hotkey(hotkey="S_E")
def handle_single_object(self, context, obj):
element = tool.Ifc.get_entity(obj)
if obj == context.scene.BIMGeometryProperties.representation_obj:
self.report({"ERROR"}, f"Element '{obj.name}' is in item mode and cannot be edited directly")
elif obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
self.report({"ERROR"}, "Clipping planes cannot be edited")
elif element:
if (usage := tool.Model.get_usage_type(element)) and usage in ("LAYER1", "LAYER2"):
self.report({"INFO"}, f"Parametric {usage} elements cannot be edited directly")
elif tool.Geometry.is_locked(element):
self.report({"ERROR"}, f"Element '{obj.name}' is locked and cannot be edited")
elif usage == "PROFILE":
bpy.ops.bim.hotkey(hotkey="A_E")
elif usage == "LAYER3":
bpy.ops.bim.hotkey(hotkey="S_E")
elif obj.data and tool.Geometry.is_profile_based(obj.data):
bpy.ops.bim.hotkey(hotkey="S_E")
elif element.is_a("IfcRelSpaceBoundary"):
bpy.ops.bim.enable_editing_boundary_geometry()
elif element.is_a("IfcGridAxis"):
self.enable_edit_mode(context)
elif tool.Blender.Modifier.is_editing_parameters(obj):
# This should go BEFORE the modifiers
self.report({"INFO"}, "Can't edit path while the modifier parameters are being modified")
obj.select_set(False)
continue
self.report({"INFO"}, "Can't edit while modifier parameters are being modified")
elif tool.Blender.Modifier.is_roof(element):
operator = lambda: bpy.ops.bim.enable_editing_roof_path()
bpy.ops.bim.enable_editing_roof_path()
elif tool.Blender.Modifier.is_railing(element):
operator = lambda: bpy.ops.bim.enable_editing_railing_path()
bpy.ops.bim.enable_editing_railing_path()
else:
is_profile = False
if is_profile:
if len(context.selected_objects) == 1 and context.active_object == context.selected_objects[0]:
bpy.ops.bim.import_representation_items()
elif tool.Geometry.is_representation_item(obj):
self.enable_editing_representation_item(context, obj)
def handle_multiple_selected_objects(self, context):
obj = context.active_object
if obj and (tool.Ifc.get_entity(obj) or tool.Geometry.is_representation_item(obj)):
tool.Blender.select_and_activate_single_object(context, obj)
self.handle_single_object(context, obj)
else:
blender_objs = []
ifc_objs = []
for selected_obj in tool.Blender.get_selected_objects():
if tool.Ifc.get_entity(selected_obj) or tool.Geometry.is_representation_item(selected_obj):
ifc_objs.append(selected_obj)
else:
blender_objs.append(selected_obj)
if blender_objs:
for obj in ifc_objs:
obj.select_set(False)
context.view_layer.objects.active = blender_objs[0]
blender_objs[0].select_set(True)
return tool.Blender.toggle_edit_mode(context)
elif ifc_objs:
obj = ifc_objs[0]
tool.Blender.select_and_activate_single_object(context, obj)
operator()
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"}
self.report({"INFO"}, "Only a single profile-based representation can be edited at a time.")
obj.select_set(False)
continue
if tool.Geometry.is_meshlike(representation):
tool.Ifc.edit(obj)
if getattr(element, "HasOpenings", None):
# Mesh elements with openings must disable openings
# so that you can edit the original topology.
core.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=representation,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
apply_openings=False,
)
if isinstance(obj.data, bpy.types.Mesh):
tool.Geometry.apply_item_ids_as_vertex_groups(obj)
tool.Geometry.dissolve_triangulated_edges(obj)
obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
else:
obj.select_set(False)
continue
if len(selected_objs) > 1 and (
not context.selected_objects or len(context.selected_objects) != len(selected_objs)
):
# We are trying to edit at least one non-mesh-like object : Display a hint to the user
self.report({"INFO"}, "Only mesh-compatible representations may be edited concurrently in edit mode.")
if context.active_object not in context.selected_objects:
# 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:
if tool.Blender.toggle_edit_mode(context) == {"CANCELLED"}:
return {"CANCELLED"}
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:
obj.select_set(True)
context.view_layer.objects.active = active_obj
return {"FINISHED"}
self.handle_single_object(obj)
def enable_editing_representation_item(self, context, obj):
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if tool.Geometry.is_meshlike_item(item):
tool.Geometry.dissolve_triangulated_edges(obj)
tool.Blender.select_and_activate_single_object(context, obj)
obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
self.enable_edit_mode(context)
@@ -1727,16 +1683,9 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
context.scene.BIMGeometryProperties.mode = "EDIT"
context.scene.BIMGeometryProperties.is_changing_mode = False
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
if not tool.Ifc.get():
return tool.Blender.toggle_edit_mode(context)
return self.execute(context)
class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
bl_description = "Switch from Edit to Item or Object mode"
bl_idname = "bim.override_mode_set_object"
bl_label = "IFC Mode Set Object"
bl_options = {"REGISTER", "UNDO"}
@@ -1746,15 +1695,14 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
if not tool.Ifc.get():
return tool.Blender.toggle_edit_mode(context)
if not context.active_object:
return {"FINISHED"}
self.is_valid = True
if context.mode != "OBJECT":
bpy.ops.object.mode_set(mode="OBJECT")
if not tool.Ifc.get():
return {"FINISHED"}
tool.Blender.toggle_edit_mode(context)
context.scene.BIMGeometryProperties.is_changing_mode = True
if context.scene.BIMGeometryProperties.representation_obj:
@@ -1864,10 +1812,8 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item)
obj.data.BIMMeshProperties.ifc_definition_id = new_item.id()
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
return
else:
tool.Geometry.import_item(obj)
return
elif item.is_a("IfcSweptAreaSolid"):
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
ProfileDecorator.uninstall()
@@ -2344,10 +2290,9 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = context.active_object
tool.Geometry.apply_item_ids_as_vertex_groups(obj)
tool.Geometry.dissolve_triangulated_edges(obj)
props = context.scene.BIMGeometryProperties
props.representation_obj = obj
obj.hide_set(True)
tool.Geometry.lock_object(obj)
context.scene.BIMGeometryProperties.is_changing_mode = True
@@ -2355,16 +2300,11 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
context.scene.BIMGeometryProperties.mode = "ITEM"
context.scene.BIMGeometryProperties.is_changing_mode = False
item_ids = set()
for vgroup in obj.vertex_groups:
if vgroup.name.startswith("ios_item_id_"):
item_ids.add(int(vgroup.name.split("_")[-1]))
tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get())
tool.Loader.settings.context_settings = tool.Loader.create_settings()
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
for item_id in item_ids:
for item_id in set(obj.data["ios_item_ids"]):
item = tool.Ifc.get().by_id(item_id)
item_mesh = bpy.data.meshes.new(f"Item/{item.is_a()}/{item_id}")
item_mesh.BIMMeshProperties.ifc_definition_id = item_id
@@ -2436,6 +2376,7 @@ class AddMeshlikeItem(bpy.types.Operator, tool.Ifc.Operator):
bm.free()
mesh.update()
obj.matrix_world = props.representation_obj.matrix_world
obj.show_in_front = True
new = props.item_objs.add()
new.obj = obj
@@ -2481,6 +2422,7 @@ class AddSweptAreaSolidItem(bpy.types.Operator, tool.Ifc.Operator):
matrix.translation = context.scene.cursor.location
local_matrix = props.representation_obj.matrix_world.inverted() @ matrix
obj.show_in_front = True
obj.matrix_world = matrix
tool.Geometry.record_object_position(obj)
+16 -10
View File
@@ -42,16 +42,22 @@ def get_contexts(self, context):
def update_mode(self, context):
if self.is_changing_mode:
return
if self.mode == "OBJECT":
if self.representation_obj:
tool.Geometry.unlock_object(self.representation_obj)
tool.Geometry.sync_item_positions()
self.representation_obj = None
bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT")
elif self.mode == "ITEM":
bpy.ops.bim.import_representation_items()
elif self.mode == "EDIT":
bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT")
if context.mode.startswith("EDIT"):
if self.mode == "OBJECT":
bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT")
tool.Geometry.disable_item_mode()
elif self.mode == "ITEM":
bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT")
elif self.mode == "EDIT":
pass
else:
if self.mode == "OBJECT":
tool.Geometry.disable_item_mode()
elif self.mode == "ITEM":
if not self.representation_obj:
bpy.ops.bim.import_representation_items()
elif self.mode == "EDIT":
bpy.ops.bim.override_mode_set_edit("INVOKE_DEFAULT")
def update_representation_obj(self, context):
@@ -111,6 +111,8 @@ def update_grid_is_locked(self, context):
tool.Geometry.lock_object(obj)
else:
tool.Geometry.unlock_object(obj)
# Need to update ViewportData.mode.
bonsai.bim.handler.refresh_ui_data()
def update_spatial_is_locked(self, context):
+13
View File
@@ -1472,3 +1472,16 @@ class Geometry(bonsai.core.tool.Geometry):
obj.matrix_world = props.representation_obj.matrix_world @ position
for vert in obj.data.vertices:
vert.co = position_i @ vert.co
@classmethod
def disable_item_mode(cls):
props = bpy.context.scene.BIMGeometryProperties
if props.representation_obj:
props.representation_obj.hide_set(False)
cls.unlock_object(props.representation_obj)
cls.sync_item_positions()
props.is_changing_mode = True
if props.mode != "OBJECT":
props.mode = "OBJECT"
props.is_changing_mode = False
props.representation_obj = None