mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 10:57:49 +00:00
Fix ci-bonsai-daily: assign_class drops active_object; assign_material usage
Two root causes behind ~16 failing BDD scenarios in the material / assign-type area: - AssignClass._execute (bim/module/root/operator.py) relinks objects into new collections (e.g. types into the hidden "IfcTypeProduct" collection) and then restores the selection via tool.Blender.validate_object_selection. Without a context.view_layer.update() in between, a just-relinked (but valid) active object is seen as "not in the current view layer" and dropped, so context.active_object becomes None for the rest of the caller and every scenario that reads active_object after assigning a type fails. Add the view_layer.update() before the selection-restore (an idiom already used ~15 times elsewhere). - core.assign_material forwarded the selected plain IfcMaterial into ifcopenshell.api.material.assign_material even when auto-upgrading the assignment to a "...Usage" type, which only accepts a material *set* or None, tripping an intentional API assertion. Pass material=None for the usage branch and let the existing add_material_to_set follow-up populate the derived set. Verified in headless Blender: the material / assign_type / assigned-material BDD cluster goes from 23 failed / 14 passed to 7 failed / 30 passed. The 7 remaining failures are an unrelated stale-STEP-id fixture cluster (Item/IfcFace/NN) handled separately. This change was made with the assistance of an AI tool. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -380,6 +380,14 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# TODO: reload representation might lead to the object being replaced by object of the other type.
|
||||
# We probably should track it somehow and keep the original selection.
|
||||
|
||||
# Objects may have just been relinked into new collections (e.g. types
|
||||
# into the "IfcTypeProduct" collection) above. Make sure the view
|
||||
# layer's object bases are up to date before validating the selection
|
||||
# below, otherwise a just-relinked (but perfectly valid) active object
|
||||
# can be seen as "not in the current view layer" and be dropped,
|
||||
# leaving `context.active_object` as None for the rest of the caller.
|
||||
context.view_layer.update()
|
||||
|
||||
# Validate selection and reapply it.
|
||||
current_selection = tool.Blender.validate_object_selection(*current_selection)
|
||||
tool.Blender.set_objects_selection(*current_selection)
|
||||
|
||||
@@ -130,10 +130,23 @@ def assign_material(
|
||||
|
||||
if can_be_usage and not material_tool.is_type_product(element):
|
||||
element_material_type = material_type + "Usage"
|
||||
# A "...Usage" type always wraps an existing material *set*
|
||||
# (IfcMaterialLayerSet/IfcMaterialProfileSet), never a plain
|
||||
# IfcMaterial. `material` here is whatever single IfcMaterial is
|
||||
# selected in the Object Materials UI, so it can't be passed
|
||||
# through as-is or ifcopenshell.api.material.assign_material will
|
||||
# assert (e.g. "IfcMaterial cannot be assiged as a
|
||||
# IfcMaterialLayerSetUsage."). Passing None lets the API derive
|
||||
# (or create) the proper set from the element's type; the
|
||||
# selected material is added into that set below instead.
|
||||
material_for_assignment = None
|
||||
else:
|
||||
element_material_type = material_type
|
||||
material_for_assignment = material
|
||||
|
||||
ifc.run("material.assign_material", products=[element], type=element_material_type, material=material)
|
||||
ifc.run(
|
||||
"material.assign_material", products=[element], type=element_material_type, material=material_for_assignment
|
||||
)
|
||||
assigned_material = material_tool.get_material(element)
|
||||
assert assigned_material # Type checker.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user