Bonsai: add "Share Annotations" option to Duplicate Drawing

When duplicating a drawing with "Should Duplicate Annotations" enabled, a
new "Share Annotations" toggle lets the manual annotations be shared with
the new drawing (multi-group membership - editing one updates both) instead
of being copied into independent entities. Auto-generated tags
(grid/section/storey) are always copied, since they are regenerated per
drawing and cannot be shared.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ryan Schultz
2026-08-20 10:34:49 -05:00
parent d911f18449
commit 8285e62365
2 changed files with 38 additions and 12 deletions
@@ -202,6 +202,14 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
drawing: bpy.props.IntProperty() drawing: bpy.props.IntProperty()
should_duplicate_annotations: bpy.props.BoolProperty(name="Should Duplicate Annotations", default=False) should_duplicate_annotations: bpy.props.BoolProperty(name="Should Duplicate Annotations", default=False)
share_annotations: bpy.props.BoolProperty(
name="Share Annotations",
description=(
"Assign the same annotations to the new drawing (shared - editing one updates both) "
"instead of creating independent copies. Auto-generated tags are always copied"
),
default=False,
)
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
@@ -216,8 +224,11 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
def draw(self, context): def draw(self, context):
assert self.layout assert self.layout
row = self.layout layout = self.layout
row.prop(self, "should_duplicate_annotations") layout.prop(self, "should_duplicate_annotations")
row = layout.row()
row.enabled = self.should_duplicate_annotations
row.prop(self, "share_annotations")
def _execute(self, context): def _execute(self, context):
props = tool.Drawing.get_document_props() props = tool.Drawing.get_document_props()
@@ -228,6 +239,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
tool.Geometry, tool.Geometry,
drawing=tool.Ifc.get().by_id(self.drawing), drawing=tool.Ifc.get().by_id(self.drawing),
should_duplicate_annotations=self.should_duplicate_annotations, should_duplicate_annotations=self.should_duplicate_annotations,
share_annotations=self.share_annotations,
) )
+15 -1
View File
@@ -355,6 +355,7 @@ def duplicate_drawing(
geometry: type[tool.Geometry], geometry: type[tool.Geometry],
drawing: ifcopenshell.entity_instance, drawing: ifcopenshell.entity_instance,
should_duplicate_annotations: bool = False, should_duplicate_annotations: bool = False,
share_annotations: bool = False,
) -> ifcopenshell.entity_instance: ) -> ifcopenshell.entity_instance:
drawing_name = drawing_tool.ensure_unique_drawing_name(drawing_tool.get_name(drawing)) drawing_name = drawing_tool.ensure_unique_drawing_name(drawing_tool.get_name(drawing))
new_drawing = ifc.run("root.copy_class", product=drawing) new_drawing = ifc.run("root.copy_class", product=drawing)
@@ -367,8 +368,21 @@ def duplicate_drawing(
ifc.run("group.edit_group", group=new_group, attributes={"Name": drawing_name, "ObjectType": "DRAWING"}) ifc.run("group.edit_group", group=new_group, attributes={"Name": drawing_name, "ObjectType": "DRAWING"})
ifc.run("group.assign_group", group=new_group, products=[new_drawing]) ifc.run("group.assign_group", group=new_group, products=[new_drawing])
if should_duplicate_annotations: if should_duplicate_annotations:
source_annotations = [a for a in drawing_tool.get_group_elements(group) if a != drawing]
if share_annotations:
# Share manual annotations with the new drawing (multi-group membership):
# the same entity now belongs to both drawings' groups, so edits propagate.
# Auto-generated tags (grid/section/storey) can't be shared - they are
# regenerated per drawing - so they are still duplicated.
to_share = [a for a in source_annotations if not drawing_tool.is_auto_annotation(a)]
to_duplicate = [a for a in source_annotations if drawing_tool.is_auto_annotation(a)]
if to_share:
ifc.run("group.assign_group", group=new_group, products=to_share)
else:
to_duplicate = source_annotations
if to_duplicate:
new_annotations: list[ifcopenshell.entity_instance] = [] new_annotations: list[ifcopenshell.entity_instance] = []
annotation_objs = [ifc.get_object(a) for a in drawing_tool.get_group_elements(group) if a != drawing] annotation_objs = [ifc.get_object(a) for a in to_duplicate]
old_to_new, _ = geometry.duplicate_ifc_objects(annotation_objs) old_to_new, _ = geometry.duplicate_ifc_objects(annotation_objs)
for new_elements in old_to_new.values(): for new_elements in old_to_new.values():
# Remove the Blender object, since we haven't actually activated the duplicated drawing # Remove the Blender object, since we haven't actually activated the duplicated drawing