diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 2f39a71349..8162b5effd 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -338,22 +338,36 @@ class SwitchRepresentation(bpy.types.Operator, tool.Ifc.Operator): return False def _execute(self, context): - context = tool.Ifc.get().by_id(self.ifc_definition_id).ContextOfItems + provided_representation = tool.Ifc.get().by_id(self.ifc_definition_id) + ifc_context = provided_representation.ContextOfItems for obj in tool.Blender.get_selected_objects(): - if ( - (element := tool.Ifc.get_entity(obj)) - and obj.mode == "OBJECT" - and (representation := ifcopenshell.util.representation.get_representation(element, context)) - ): - core.switch_representation( - tool.Ifc, - tool.Geometry, - obj=obj, - representation=representation, - should_reload=self.should_reload, - is_global=self.should_switch_all_meshes, - should_sync_changes_first=True, - ) + if not (element := tool.Ifc.get_entity(obj)) or obj.mode != "OBJECT": + continue + + # Find representation to switch to. + if (active_representation := tool.Geometry.get_active_representation(obj)) is None: + # No active representation => probably has no representations. + continue + elif obj == context.active_object: + # Prioritize provided representation. + representation = provided_representation + elif active_representation.ContextOfItems == ifc_context: + # Prioritize already active representation if context matches. + representation = active_representation + else: + representation = ifcopenshell.util.representation.get_representation(element, ifc_context) + if not representation: + continue + + core.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=representation, + should_reload=self.should_reload, + is_global=self.should_switch_all_meshes, + should_sync_changes_first=True, + ) class RemoveRepresentation(bpy.types.Operator, tool.Ifc.Operator): diff --git a/src/bonsai/bonsai/tool/geometry.py b/src/bonsai/bonsai/tool/geometry.py index e7e5e4eaff..6071c8a25e 100644 --- a/src/bonsai/bonsai/tool/geometry.py +++ b/src/bonsai/bonsai/tool/geometry.py @@ -678,6 +678,7 @@ class Geometry(bonsai.core.tool.Geometry): element = tool.Ifc.get_entity(obj) assert element + ifc_file = tool.Ifc.get() elements = set() element_types = set() representation = ifcopenshell.util.representation.resolve_representation(representation) @@ -716,16 +717,26 @@ class Geometry(bonsai.core.tool.Geometry): else: iterator = None # For example, when switching representation of a type with no occurrences meshes = {} + base_representation = representation if iterator and iterator.initialize(): while True: shape = iterator.get() element = tool.Ifc.get().by_id(shape.id) if obj := tool.Ifc.get_object(element): + # It's possible that there will be multiple shapes for the same context, + # Unfortunately, iterator still processes them all and + # we need to ensure we pick the one that was requested for reimport. + representation_id = tool.Loader.get_representation_id_from_shape(shape.geometry) + representation = ifc_file.by_id(representation_id) + resolved_representation = ifcopenshell.util.representation.resolve_representation(representation) + if resolved_representation != base_representation: + if not iterator.next(): + break + continue + mesh_name = tool.Loader.get_mesh_name_from_shape(shape.geometry) mesh = meshes.get(mesh_name) if mesh is None: - # Duplicate code - representation = tool.Ifc.get().by_id(int(shape.geometry.id.split("-")[0])) if element.is_a("IfcAnnotation") and element.ObjectType == "DRAWING": mesh = tool.Loader.create_camera(element, representation, shape) elif element.is_a("IfcAnnotation") and ifc_importer.is_curve_annotation(element): diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index 8db14cfdd6..4a52a97e24 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -70,15 +70,20 @@ class Loader(bonsai.core.tool.Loader): cls.settings = settings @classmethod - def get_mesh_name_from_shape(cls, geometry: ifcopenshell.geom.ShapeType) -> str: - representation_id = geometry.id + def get_representation_id_from_shape(cls, geometry: ifcopenshell.geom.ShapeType) -> int: + representation_id: str = geometry.id if "-" in representation_id: # Example: 2432-openings-2468, where # 2432 is mapped representation id # and 2468 is IFCRELVOIDSELEMENT - representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0])) + representation_id = re.sub(r"\D", "", representation_id.split("-")[0]) else: - representation_id = int(re.sub(r"\D", "", representation_id)) + representation_id = re.sub(r"\D", "", representation_id) + return int(representation_id) + + @classmethod + def get_mesh_name_from_shape(cls, geometry: ifcopenshell.geom.ShapeType) -> str: + representation_id = cls.get_representation_id_from_shape(geometry) representation = tool.Ifc.get().by_id(representation_id) context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0 return cls.get_mesh_name(context_id, representation_id)