From ca866b366955e05839740b3459f667c96f1cbb71 Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Wed, 8 Apr 2026 13:59:14 -0500 Subject: [PATCH] Add external SVG reference support to manual drawing reference tags Extends manual drawing reference annotations (elevation and section) to also support external SVG references imported via bim.add_reference. - Add "Is a Reference" checkbox to the annotation tool sidebar, shown when Elevation or Section is the active type; checking it and pressing Add opens a dialog to optionally link the tag to a Bonsai drawing or an external SVG reference - The MANUAL_DRAWING_REFERENCE dropdown type is retained for backwards compatibility; selecting it shows a style picker (Elevation/Section) and the same linking dialog - External-reference annotations are flagged with IsDocumentReference in EPset_Annotation and linked to their IfcDocumentInformation via IfcRelAssociatesDocument; drawing-reference annotations continue to use IfcRelAssignsToProduct as before - SVG export resolves the correct reference/sheet IDs for both link types via get_reference_and_sheet_id_from_annotation - Add IsDocumentReference to the EPset_Annotation pset template --- src/bonsai/bonsai/bim/module/drawing/ui.py | 3 +- src/bonsai/bonsai/core/drawing.py | 8 ++++ src/bonsai/bonsai/tool/drawing.py | 43 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/bonsai/bonsai/bim/module/drawing/ui.py b/src/bonsai/bonsai/bim/module/drawing/ui.py index 8d769fecb9..3f580cabb0 100644 --- a/src/bonsai/bonsai/bim/module/drawing/ui.py +++ b/src/bonsai/bonsai/bim/module/drawing/ui.py @@ -559,8 +559,9 @@ class BIM_PT_product_assignments(Panel): element = tool.Ifc.get_entity(obj) if element and tool.Drawing.is_manual_drawing_reference(element): row = self.layout.row(align=True) + fallback = "No Reference Assigned" if element.ObjectType == "REFERENCE" else "No Drawing Assigned" row.label( - text=ProductAssignmentsData.data["relating_product"] or "No Drawing Assigned", icon="IMAGE_DATA" + text=ProductAssignmentsData.data["relating_product"] or fallback, icon="IMAGE_DATA" ) row.operator("bim.assign_manual_drawing_reference", icon="GREASEPENCIL", text="") return diff --git a/src/bonsai/bonsai/core/drawing.py b/src/bonsai/bonsai/core/drawing.py index cc11ff2833..b6e18cea43 100644 --- a/src/bonsai/bonsai/core/drawing.py +++ b/src/bonsai/bonsai/core/drawing.py @@ -562,6 +562,14 @@ def assign_manual_drawing_reference( ifc.run("drawing.assign_product", relating_product=drawing, related_object=element) +def assign_manual_reference_document( + drawing_tool: type[tool.Drawing], + element: ifcopenshell.entity_instance, + document: Optional[ifcopenshell.entity_instance], +) -> None: + drawing_tool.set_annotation_reference_doc(element, document) + + def build_schedule(drawing: type[tool.Drawing], schedule: ifcopenshell.entity_instance) -> None: drawing.create_svg_schedule(schedule) drawing.open_svg(drawing.get_path_with_ext(drawing.get_document_uri(schedule), "svg")) diff --git a/src/bonsai/bonsai/tool/drawing.py b/src/bonsai/bonsai/tool/drawing.py index 95e4603c38..a024eed769 100644 --- a/src/bonsai/bonsai/tool/drawing.py +++ b/src/bonsai/bonsai/tool/drawing.py @@ -2021,6 +2021,49 @@ class Drawing(bonsai.core.tool.Drawing): pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name="EPset_Annotation") ifcopenshell.api.pset.edit_pset(ifc_file, pset=pset, properties={"IsManualDrawingReference": True}) + @classmethod + def is_document_reference(cls, element: ifcopenshell.entity_instance) -> bool: + """Return True if this annotation links to an external document (not a Bonsai drawing camera).""" + return bool(ifcopenshell.util.element.get_pset(element, "EPset_Annotation", "IsDocumentReference")) + + @classmethod + def set_document_reference_flag(cls, element: ifcopenshell.entity_instance) -> None: + """Mark this annotation as pointing to an external document reference.""" + ifc_file = tool.Ifc.get() + pset = tool.Pset.get_element_pset(element, "EPset_Annotation") + if not pset: + pset = ifcopenshell.api.pset.add_pset(ifc_file, product=element, name="EPset_Annotation") + ifcopenshell.api.pset.edit_pset(ifc_file, pset=pset, properties={"IsDocumentReference": True}) + + @classmethod + def get_annotation_reference_doc( + cls, element: ifcopenshell.entity_instance + ) -> Union[ifcopenshell.entity_instance, None]: + """Return the IfcDocumentInformation linked to a document-reference annotation.""" + for rel in element.HasAssociations: + if rel.is_a("IfcRelAssociatesDocument"): + doc = rel.RelatingDocument + if doc.is_a("IfcDocumentInformation"): + return doc + return None + + @classmethod + def set_annotation_reference_doc( + cls, + element: ifcopenshell.entity_instance, + document: Union[ifcopenshell.entity_instance, None], + ) -> None: + """Associate (or clear) an IfcDocumentInformation on a document-reference annotation.""" + ifc_file = tool.Ifc.get() + # Remove existing document associations on this annotation. + for rel in list(element.HasAssociations): + if rel.is_a("IfcRelAssociatesDocument"): + ifcopenshell.api.document.unassign_document( + ifc_file, products=[element], document=rel.RelatingDocument + ) + if document: + ifcopenshell.api.document.assign_document(ifc_file, products=[element], document=document) + @classmethod def regenerate_elevation_reference_annotation( cls,