From 24b3cfc0677fff18de3864174277888d3e81b1a3 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 17 Jan 2025 20:53:29 +1100 Subject: [PATCH] Fix #5897. Fix #5488. Fix #5498. Bug where you couldn't edit newly added annotation. The annotation adding code was a bit of a mess, spread around create_annotation, create_annotation_occurrence, and bpy.ops.bim.add_annotation. I've now consolidated it all into core.add_annotation and updated it to work with the new item editing mode (basically new objects need to reload their representation to populate item_ids). Things are still messy, but a bit less now. --- .../bonsai/bim/module/drawing/operator.py | 22 ++++--- .../bonsai/bim/module/drawing/workspace.py | 59 ++++++++++--------- src/bonsai/bonsai/core/drawing.py | 15 ++++- src/bonsai/bonsai/tool/drawing.py | 34 +++++++++-- 4 files changed, 87 insertions(+), 43 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 91e895f488..4526555f7d 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -1423,27 +1423,33 @@ class AddAnnotation(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_annotation" bl_label = "Add Annotation" bl_options = {"REGISTER", "UNDO"} - object_type: bpy.props.StringProperty() - data_type: bpy.props.StringProperty() description: bpy.props.StringProperty() @classmethod def poll(cls, context): - return IfcStore.get_file() and context.scene.camera + return tool.Ifc.get() and context.scene.camera @classmethod def description(cls, context, operator): return operator.description or "" def _execute(self, context): - drawing = tool.Ifc.get_entity(context.scene.camera) - if not drawing: + props = bpy.context.scene.BIMAnnotationProperties + if not (drawing := tool.Ifc.get_entity(context.scene.camera)): self.report({"WARNING"}, "Not a BIM camera") return - r = core.add_annotation(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing, object_type=self.object_type) - if isinstance(r, str): - self.report({"WARNING"}, r) + obj = core.add_annotation( + tool.Ifc, + tool.Collector, + tool.Drawing, + drawing=drawing, + object_type=props.object_type, + relating_type=tool.Ifc.get().by_id(int(props.relating_type_id)) if props.relating_type_id != "0" else None, + enable_editing=True, + ) + if props.object_type == "IMAGE": + bpy.ops.bim.add_reference_image("INVOKE_DEFAULT", use_existing_object_by_name=obj.name) class AddSheet(bpy.types.Operator, tool.Ifc.Operator): diff --git a/src/bonsai/bonsai/bim/module/drawing/workspace.py b/src/bonsai/bonsai/bim/module/drawing/workspace.py index b5469ddd90..a6def05ad5 100644 --- a/src/bonsai/bonsai/bim/module/drawing/workspace.py +++ b/src/bonsai/bonsai/bim/module/drawing/workspace.py @@ -20,7 +20,9 @@ import os import bpy import bonsai.core.type +import bonsai.core.drawing as core import bonsai.tool as tool +import ifcopenshell.util.representation from bonsai.bim.module.drawing.data import DecoratorData, AnnotationData from bonsai.bim.helper import prop_with_search from bpy.types import WorkSpaceTool @@ -161,12 +163,12 @@ def create_annotation_occurrence(context): obj = tool.Drawing.create_annotation_object(drawing, object_type) obj.name = relating_type.Name ifc_context = tool.Drawing.get_annotation_context(tool.Drawing.get_drawing_target_view(drawing), object_type) - relating_type_repr = tool.Drawing.get_annotation_representation(relating_type) + relating_type_rep = tool.Drawing.get_annotation_representation(relating_type) element = tool.Drawing.run_root_assign_class( obj=obj, ifc_class="IfcAnnotation", predefined_type=object_type, - should_add_representation=not bool(relating_type_repr), + should_add_representation=not relating_type_rep, context=ifc_context, ifc_representation_class=tool.Drawing.get_ifc_representation_class(object_type), ) @@ -175,27 +177,23 @@ def create_annotation_occurrence(context): tool.Ifc.run("group.assign_group", group=tool.Drawing.get_drawing_group(drawing), products=[element]) tool.Collector.assign(obj) - tool.Blender.select_and_activate_single_object(context, obj) - if relating_type_repr is None and props.object_type == "IMAGE": + if relating_type_rep is None and props.object_type == "IMAGE": bpy.ops.bim.add_reference_image("INVOKE_DEFAULT", use_existing_object_by_name=obj.name) - -def create_annotation(): - props = bpy.context.scene.BIMAnnotationProperties - if props.relating_type_id != "0": - create_annotation_occurrence(bpy.context) - else: - object_type = props.object_type - if not bpy.ops.bim.add_annotation.poll(): - return - bpy.ops.bim.add_annotation( - object_type=object_type, data_type=tool.Drawing.ANNOTATION_TYPES_DATA[object_type][-1] + if representation := ifcopenshell.util.representation.get_representation(element, ifc_context): + bonsai.core.geometry.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, ) - if props.object_type == "IMAGE": - bpy.ops.bim.add_reference_image( - "INVOKE_DEFAULT", use_existing_object_by_name=bpy.context.active_object.name - ) + + if obj.data and not relating_type_rep: + tool.Drawing.enable_editing(obj) class AnnotationToolUI: @@ -294,21 +292,28 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator): def hotkey_S_T(self): props = bpy.context.scene.BIMAnnotationProperties - annotation_type = props.object_type + object_type = props.object_type - if annotation_type not in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP: - self.report({"ERROR"}, f"Annotation type {annotation_type} is not supported for tagging.") + if object_type not in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP: + self.report({"ERROR"}, f"Annotation type {object_type} is not supported for tagging.") return related_objects = bpy.context.selected_objects for related_object in related_objects: - create_annotation() - obj = bpy.context.active_object - bpy.ops.object.mode_set(mode="OBJECT") - tool.Drawing.setup_annotation_object(obj, annotation_type, related_object) + obj = core.add_annotation( + tool.Ifc, + tool.Collector, + tool.Drawing, + drawing=tool.Ifc.get_entity(bpy.context.scene.camera), + object_type=object_type, + relating_type=tool.Ifc.get().by_id(int(props.relating_type_id)) if props.relating_type_id != "0" else None, + enable_editing=False, + ) + tool.Drawing.setup_annotation_object(obj, object_type, related_object) def hotkey_S_A(self): - create_annotation() + if bpy.ops.bim.add_annotation.poll(): + bpy.ops.bim.add_annotation() def hotkey_S_E(self): if not bpy.context.active_object: diff --git a/src/bonsai/bonsai/core/drawing.py b/src/bonsai/bonsai/core/drawing.py index 9dd4490e98..e5ae3cd493 100644 --- a/src/bonsai/bonsai/core/drawing.py +++ b/src/bonsai/bonsai/core/drawing.py @@ -406,7 +406,9 @@ def add_annotation( drawing_tool: tool.Drawing, drawing: ifcopenshell.entity_instance, object_type: str, -) -> None: + relating_type: ifcopenshell.entity_instance, + enable_editing: bool = False, +) -> bpy.types.Object: target_view = drawing_tool.get_drawing_target_view(drawing) context = drawing_tool.get_annotation_context(target_view, object_type) if not context: @@ -416,17 +418,24 @@ def add_annotation( obj = drawing_tool.create_annotation_object(drawing, object_type) element = ifc.get_entity(obj) if not element: + relating_type_rep = drawing_tool.get_annotation_representation(relating_type) if relating_type else None element = drawing_tool.run_root_assign_class( obj=obj, ifc_class="IfcAnnotation", predefined_type=object_type, - should_add_representation=True, + should_add_representation=not relating_type_rep, context=context, ifc_representation_class=drawing_tool.get_ifc_representation_class(object_type), ) + if relating_type: + drawing_tool.run_type_assign_type(element=element, relating_type=relating_type) ifc.run("group.assign_group", group=drawing_tool.get_drawing_group(drawing), products=[element]) + if representation := drawing_tool.get_representation(element, context): + drawing_tool.reload_representation(obj=obj, representation=representation) collector.assign(obj, should_clean_users_collection=True) - drawing_tool.enable_editing(obj) + if not relating_type_rep and object_type != "IMAGE" and enable_editing: + drawing_tool.enable_editing(obj) + return obj def build_schedule(drawing: tool.Drawing, schedule: ifcopenshell.entity_instance) -> None: diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 6ebad7696f..d15a8cca49 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -348,11 +348,15 @@ class Drawing(bonsai.core.tool.Drawing): @classmethod def enable_editing(cls, obj: bpy.types.Object) -> None: - bpy.ops.object.select_all(action="DESELECT") - bpy.context.view_layer.objects.active = obj - obj.select_set(True) - if obj.data: - bpy.ops.object.mode_set(mode="EDIT") + from bonsai.bim.module.geometry.data import ViewportData + + tool.Blender.select_and_activate_single_object(bpy.context, obj) + if not obj.data: + return + ViewportData.load() # Reload valid modes + bpy.ops.bim.override_mode_set_edit() # Enter item mode + ViewportData.load() # Reload valid modes + bpy.ops.bim.override_mode_set_edit() # Enter edit mode @classmethod def enable_editing_drawings(cls) -> None: @@ -988,6 +992,26 @@ class Drawing(bonsai.core.tool.Drawing): ifc_representation_class=ifc_representation_class, ) + @classmethod + def run_type_assign_type(cls, element: ifcopenshell.entity_instance, relating_type: ifcopenshell.entity_instance): + return bonsai.core.type.assign_type(tool.Ifc, tool.Type, element=element, type=relating_type) + + @classmethod + def reload_representation(cls, obj: bpy.types.Object, representation: ifcopenshell.entity_instance): + return bonsai.core.geometry.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + ) + + @classmethod + def get_representation(cls, element, context): + return ifcopenshell.util.representation.get_representation(element, context) + @classmethod def set_drawing_collection_name( cls, drawing: ifcopenshell.entity_instance, collection: bpy.types.Collection