From ca652f4534152a33df5ed48ef073e0c72072cd1a Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Sun, 15 Mar 2026 12:34:52 -0500 Subject: [PATCH] Add AssignManualDrawingReference operator Adds operator to link a manual drawing reference tag to a target drawing via IfcRelAssignsToProduct, with a pre-populated dialog and immediate Properties panel refresh on confirm. Generated with the assistance of an AI coding tool. --- .../bonsai/bim/module/drawing/operator.py | 49 +++++++++++++++++++ src/bonsai/bonsai/core/drawing.py | 12 +++++ 2 files changed, 61 insertions(+) diff --git a/src/bonsai/bonsai/bim/module/drawing/operator.py b/src/bonsai/bonsai/bim/module/drawing/operator.py index 728b9ed7a3..f9df38b268 100644 --- a/src/bonsai/bonsai/bim/module/drawing/operator.py +++ b/src/bonsai/bonsai/bim/module/drawing/operator.py @@ -1908,6 +1908,55 @@ class AddAnnotation(bpy.types.Operator, tool.Ifc.Operator): # inherits from it. This placeholder keeps the module namespace tidy. +def _get_drawing_enum_items(self, context): + items = [("0", "None", "Clear the drawing assignment")] + if ifc := tool.Ifc.get(): + for d in ifc.by_type("IfcAnnotation"): + if d.ObjectType == "DRAWING": + items.append((str(d.id()), d.Name or f"Drawing {d.id()}", "")) + return items + + +class AssignManualDrawingReference(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.assign_manual_drawing_reference" + bl_label = "Assign Drawing" + bl_description = "Assign a target drawing to this manual drawing reference tag" + bl_options = {"REGISTER", "UNDO"} + drawing_id: bpy.props.EnumProperty(name="Drawing", items=_get_drawing_enum_items) + + @classmethod + def poll(cls, context): + if not tool.Ifc.get() or not context.active_object: + return False + element = tool.Ifc.get_entity(context.active_object) + if not element or not element.is_a("IfcAnnotation"): + return False + if element.ObjectType not in ("ELEVATION", "SECTION"): + cls.poll_message_set("Active object is not an elevation or section annotation.") + return False + if not ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "IsManualDrawingReference"): + cls.poll_message_set("Active object is not a manual drawing reference.") + return False + return True + + def invoke(self, context, event): + element = tool.Ifc.get_entity(context.active_object) + current = tool.Drawing.get_annotation_element(element) + self.drawing_id = str(current.id()) if current else "0" + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + self.layout.prop(self, "drawing_id") + + def _execute(self, context): + element = tool.Ifc.get_entity(context.active_object) + drawing = tool.Ifc.get().by_id(int(self.drawing_id)) if self.drawing_id != "0" else None + core.assign_manual_drawing_reference(tool.Ifc, tool.Drawing, element=element, drawing=drawing) + for area in context.screen.areas: + if area.type == "PROPERTIES": + area.tag_redraw() + + class AddSheet(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_sheet" bl_label = "Add Sheet" diff --git a/src/bonsai/bonsai/core/drawing.py b/src/bonsai/bonsai/core/drawing.py index 0078d7e7b6..952261a2c4 100644 --- a/src/bonsai/bonsai/core/drawing.py +++ b/src/bonsai/bonsai/core/drawing.py @@ -550,6 +550,18 @@ def add_annotation( return obj +def assign_manual_drawing_reference( + ifc: type[tool.Ifc], + drawing_tool: type[tool.Drawing], + element: ifcopenshell.entity_instance, + drawing: Optional[ifcopenshell.entity_instance], +) -> None: + for existing in drawing_tool.get_assigned_product_workaround(element): + ifc.run("drawing.unassign_product", relating_product=existing, related_object=element) + if drawing: + ifc.run("drawing.assign_product", relating_product=drawing, related_object=element) + + def add_manual_drawing_reference( ifc: type[tool.Ifc], collector: type[tool.Collector],