diff --git a/src/bonsai/bonsai/core/geometry.py b/src/bonsai/bonsai/core/geometry.py index 83ba921d30..c33ee76d44 100644 --- a/src/bonsai/bonsai/core/geometry.py +++ b/src/bonsai/bonsai/core/geometry.py @@ -150,7 +150,15 @@ def remove_representation( element_type = geometry.get_element_type(element) data = None data_removed_by_switch_representation = False - if element_type and (geometry.is_mapped_representation(representation) or geometry.is_type_product(element)): + # Take the type-level branch only when the removal really is type-scoped: the + # object is the type itself, or the mapped rep is anchored to the type's + # RepresentationMaps. A mapped rep pointing at a *floating* map not owned by + # the type (typical of Revit imports) must be removed at the occurrence level + # instead -- the type branch would no-op and leave it unremovable. + if element_type and ( + geometry.is_type_product(element) + or geometry.is_mapped_representation_of_type(representation, element_type) + ): representation = geometry.resolve_mapped_representation(representation) data = geometry.get_representation_data(representation) if data and geometry.has_data_users(data): diff --git a/src/bonsai/bonsai/core/tool.py b/src/bonsai/bonsai/core/tool.py index 8a77d36661..5f01b260ac 100644 --- a/src/bonsai/bonsai/core/tool.py +++ b/src/bonsai/bonsai/core/tool.py @@ -477,6 +477,7 @@ class Geometry: def is_box_representation(cls, representation): pass def is_data_supported_for_adding_representation(cls, data): pass def is_mapped_representation(cls, representation): pass + def is_mapped_representation_of_type(cls, representation, element_type): pass def is_type_product(cls, element): pass def link(cls, element, obj): pass def record_object_materials(cls, obj): pass diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index 5747b76579..6544b18743 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -1317,6 +1317,25 @@ class Geometry(bonsai.core.tool.Geometry): def is_mapped_representation(cls, representation: ifcopenshell.entity_instance) -> bool: return representation.RepresentationType == "MappedRepresentation" + @classmethod + def is_mapped_representation_of_type( + cls, + representation: ifcopenshell.entity_instance, + element_type: Union[ifcopenshell.entity_instance, None], + ) -> bool: + """``True`` when ``representation`` is a mapped representation whose + ``IfcRepresentationMap`` is one of ``element_type``'s ``RepresentationMaps`` + -- i.e. a genuine type-anchored shared representation, as opposed to a + floating map not owned by the type (which Revit exports produce).""" + if element_type is None or representation.RepresentationType != "MappedRepresentation": + return False + if not representation.Items: + return False + item = representation.Items[0] + if not item.is_a("IfcMappedItem"): + return False + return item.MappingSource in (element_type.RepresentationMaps or []) + @classmethod def is_meshlike(cls, representation: ifcopenshell.entity_instance) -> bool: if ifcopenshell.util.representation.resolve_representation(representation).RepresentationType in (