Allow bulk annotation product assignment

Closes #7787: Previously bim.assign_selected_as_product required exactly
2 objects. With multiple annotations referencing the same
product, users had to repeat the operation once per
annotation. Now any number of IfcAnnotations can be selected
alongside a single product object and all are assigned in
one operation and one undo step.

Generated with the assistance of an AI coding tool.
This commit is contained in:
Ryan Schultz
2026-03-14 10:09:53 -05:00
committed by Dion Moult
parent c3a87c8f9c
commit 868bb5c39e
@@ -3364,57 +3364,55 @@ class OrderTextLiteralDown(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
# Ifc Operator is unnecessary, because suboperator is handling IFC changes. class AssignSelectedObjectAsProduct(bpy.types.Operator, tool.Ifc.Operator):
class AssignSelectedObjectAsProduct(bpy.types.Operator):
bl_idname = "bim.assign_selected_as_product" bl_idname = "bim.assign_selected_as_product"
bl_label = "Assign Selected Object As Product" bl_label = "Assign Selected Object As Product"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if len(context.selected_objects) != 2: if len(context.selected_objects) < 2:
cls.poll_message_set("2 objects need to be selected") cls.poll_message_set("At least 2 objects need to be selected")
return False return False
return True return True
def execute(self, context): def _execute(self, context):
assert bpy.context.view_layer
objs = context.selected_objects[:] objs = context.selected_objects[:]
obj1, obj2 = objs ifc_objs = [(o, tool.Ifc.get_entity(o)) for o in objs if tool.Ifc.get_entity(o)]
element1 = tool.Ifc.get_entity(obj1)
element2 = tool.Ifc.get_entity(obj2)
assert element1 and element2
# Check if at least one object is an IfcAnnotation annotations = [(o, e) for o, e in ifc_objs if e.is_a("IfcAnnotation")]
is_annotation1 = element1.is_a("IfcAnnotation") non_annotations = [(o, e) for o, e in ifc_objs if not e.is_a("IfcAnnotation")]
is_annotation2 = element2.is_a("IfcAnnotation")
if not (is_annotation1 or is_annotation2): if not annotations:
self.report({"ERROR"}, "At least one of the selected objects must be IfcAnnotation.") self.report({"ERROR"}, "At least one selected object must be an IfcAnnotation.")
return {"CANCELLED"} return {"CANCELLED"}
# If both are annotations, use the currently active object as relating product if len(non_annotations) == 1:
if is_annotation1 and is_annotation2: # One product, one or more annotations — assign all annotations to the product.
product = non_annotations[0][1]
elif len(non_annotations) == 0 and len(annotations) == 2:
# Both objects are annotations — use the non-active one as the relating product.
active_obj = context.active_object active_obj = context.active_object
if active_obj == obj1: if annotations[0][0] == active_obj:
other_selected_object = obj1 annotation_obj, annotation = annotations[0]
bpy.context.view_layer.objects.active = obj2 product = annotations[1][1]
else: else:
other_selected_object = obj2 annotation_obj, annotation = annotations[1]
bpy.context.view_layer.objects.active = obj1 product = annotations[0][1]
# If only one is an annotation, make it the active object core.edit_assigned_product(tool.Ifc, tool.Drawing, obj=annotation_obj, product=product)
elif is_annotation1: tool.Blender.update_viewport()
other_selected_object = obj2 return
bpy.context.view_layer.objects.active = obj1
else: else:
other_selected_object = obj1 self.report(
bpy.context.view_layer.objects.active = obj2 {"ERROR"},
"Select exactly one product object and one or more IfcAnnotation objects.",
)
return {"CANCELLED"}
assert (active_obj := context.active_object) for annotation_obj, _ in annotations:
props = tool.Drawing.get_object_assigned_product_props(active_obj) core.edit_assigned_product(tool.Ifc, tool.Drawing, obj=annotation_obj, product=product)
props.relating_product = other_selected_object
bpy.ops.bim.edit_assigned_product() tool.Blender.update_viewport()
return {"FINISHED"}
class EditAssignedProduct(bpy.types.Operator, tool.Ifc.Operator): class EditAssignedProduct(bpy.types.Operator, tool.Ifc.Operator):