From 340d4fb82a602dcdd1b36426cc905e42c9a2d78a Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Fri, 26 Jun 2026 17:22:27 -0500 Subject: [PATCH] Honor ApplicableOccurrence in is_relating_type_compatible Companion to the assign_type.py fix. The same class-pairing validation added in 10ee5aef4f also gates the Bonsai-side type assignment UI via tool.Type.is_relating_type_compatible, which the AssignType operator uses to filter selectable objects. For annotation types (abstract IfcTypeProduct), get_applicable_types(IfcAnnotation) is empty, so every annotation was skipped with "No selected object can be typed by IfcTypeProduct." Honor the type's ApplicableOccurrence attribute as a fallback, matching the core API fix. occurrence.is_a() handles subtypes and returns False for unknown tokens, so free-form text is not trusted blindly. Co-Authored-By: Claude Opus 4.8 --- src/bonsai/bonsai/tool/type.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bonsai/bonsai/tool/type.py b/src/bonsai/bonsai/tool/type.py index f2617ad060..6a212afb0f 100644 --- a/src/bonsai/bonsai/tool/type.py +++ b/src/bonsai/bonsai/tool/type.py @@ -107,7 +107,15 @@ class Type(bonsai.core.tool.Type): # RelatingType / RelatedObjects classes; the one-to-one class pairing # is a buildingSMART implementer agreement, not file-validation. schema = occurrence.file.schema - return relating_type.is_a() in ifcopenshell.util.type.get_applicable_types(occurrence.is_a(), schema=schema) + if relating_type.is_a() in ifcopenshell.util.type.get_applicable_types(occurrence.is_a(), schema=schema): + return True + # The implementer agreement map has no entry for the abstract + # IfcTypeProduct, which Bonsai uses for annotation types. The schema + # defines IfcTypeProduct.ApplicableOccurrence for exactly this purpose, + # so honor it. occurrence.is_a() handles subtypes and unknown tokens. + if applicable_occurrence := getattr(relating_type, "ApplicableOccurrence", None): + return occurrence.is_a(applicable_occurrence.split("/", 1)[0]) + return False @classmethod def has_material_usage(cls, element: ifcopenshell.entity_instance) -> bool: