From 3446643816ffb2d8bd59e9f702bebbc2a2507d8d Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 5 Dec 2024 18:27:22 +0500 Subject: [PATCH] bim.switch_representation to handle representations with the same context 1) If object had multiple representations for the same context, then switch simply wouldn't work as it would always pick up the first representation it could find. Now it prioritizes the one user is selecting in UI. 2) If some object is selected and it also has multiple representations for the same context, then switch representation would switch it to any representation in that context it first finds. As user doesn't provide representation explicitly in that case, now it will stick to the already active representation on selected object if it matches the context, to avoid accident switches. Notcied by working with example from #5824 --- .../bonsai/bim/module/geometry/operator.py | 44 ++++++++++++------- src/bonsai/bonsai/tool/geometry.py | 15 ++++++- src/bonsai/bonsai/tool/loader.py | 13 ++++-- 3 files changed, 51 insertions(+), 21 deletions(-) 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)