Add manual drawing reference annotation operator

Adds operator to place manual elevation/section drawing reference
tags that survive drawing regeneration. Includes core function,
tool methods, type-selection dialog, default horizontal rotation
for elevation tags, and SVG null guard for unassigned references.

Generated with the assistance of an AI coding tool.
This commit is contained in:
Ryan Schultz
2026-03-15 12:02:33 -05:00
parent f0482e7f0c
commit ecd90e45b7
3 changed files with 66 additions and 0 deletions
@@ -895,6 +895,8 @@ class SvgWriter:
reference_id = "-" reference_id = "-"
sheet_id = "-" sheet_id = "-"
drawing = tool.Drawing.get_annotation_element(element) drawing = tool.Drawing.get_annotation_element(element)
if not drawing:
return ("-", "-")
reference = tool.Drawing.get_drawing_reference(drawing) reference = tool.Drawing.get_drawing_reference(drawing)
if reference: if reference:
for sheet_reference in tool.Ifc.get().by_type("IfcDocumentReference"): for sheet_reference in tool.Ifc.get().by_type("IfcDocumentReference"):
+22
View File
@@ -550,6 +550,28 @@ def add_annotation(
return obj return obj
def add_manual_drawing_reference(
ifc: type[tool.Ifc],
collector: type[tool.Collector],
drawing_tool: type[tool.Drawing],
drawing: ifcopenshell.entity_instance,
annotation_type: str,
) -> bpy.types.Object:
if annotation_type == "ELEVATION":
element = drawing_tool.create_manual_elevation_reference(drawing)
else: # SECTION
target_view = drawing_tool.get_drawing_target_view(drawing)
context = drawing_tool.get_annotation_context(target_view)
if not context:
context = drawing_tool.create_annotation_context(target_view, annotation_type)
element = drawing_tool.create_manual_section_reference(drawing, context)
drawing_tool.set_manual_drawing_reference(element)
ifc.run("group.assign_group", group=drawing_tool.get_drawing_group(drawing), products=[element])
obj = ifc.get_object(element)
collector.assign(obj)
return obj
def build_schedule(drawing: type[tool.Drawing], schedule: ifcopenshell.entity_instance) -> None: def build_schedule(drawing: type[tool.Drawing], schedule: ifcopenshell.entity_instance) -> None:
drawing.create_svg_schedule(schedule) drawing.create_svg_schedule(schedule)
drawing.open_svg(drawing.get_path_with_ext(drawing.get_document_uri(schedule), "svg")) drawing.open_svg(drawing.get_path_with_ext(drawing.get_document_uri(schedule), "svg"))
+42
View File
@@ -1979,6 +1979,48 @@ class Drawing(bonsai.core.tool.Drawing):
element.Name = elevation.Name or "Unnamed" element.Name = elevation.Name or "Unnamed"
return element return element
@classmethod
def create_manual_elevation_reference(cls, drawing: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
cursor_location = bpy.context.scene.cursor.location.copy()
obj = bpy.data.objects.new("Unnamed", None)
obj.empty_display_size = 0.1
obj.matrix_world = Matrix.Translation(cursor_location) @ Matrix.Rotation(math.radians(90), 4, "X")
element = cls.run_root_assign_class(
obj=obj, ifc_class="IfcAnnotation", predefined_type="ELEVATION", should_add_representation=False
)
element.Name = "Unnamed"
return element
@classmethod
def create_manual_section_reference(
cls, drawing: ifcopenshell.entity_instance, context: ifcopenshell.entity_instance
) -> ifcopenshell.entity_instance:
cursor_location = bpy.context.scene.cursor.location.copy()
mesh = bpy.data.meshes.new("Mesh")
obj = bpy.data.objects.new("Unnamed", mesh)
obj.matrix_world = Matrix.Translation(cursor_location)
element = cls.run_root_assign_class(
obj=obj, ifc_class="IfcAnnotation", predefined_type="SECTION", should_add_representation=False
)
element.Name = "Unnamed"
builder = ShapeBuilder(tool.Ifc.get())
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
p1 = cursor_location + Vector((-0.5, 0, 0))
p2 = cursor_location + Vector((0.5, 0, 0))
points = [p1 / unit_scale, p2 / unit_scale]
representation = builder.get_representation(context, [builder.polyline(points)])
ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation)
bonsai.core.geometry.switch_representation(tool.Ifc, tool.Geometry, obj=obj, representation=representation)
return element
@classmethod
def set_manual_drawing_reference(cls, element: ifcopenshell.entity_instance) -> None:
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={"IsManualDrawingReference": True})
@classmethod @classmethod
def regenerate_elevation_reference_annotation( def regenerate_elevation_reference_annotation(
cls, cls,