From f59942c453e6b5939b46a3b0ef7001200d0adab8 Mon Sep 17 00:00:00 2001 From: Ryan Schultz Date: Tue, 7 Apr 2026 10:10:42 -0500 Subject: [PATCH] Helps with #7853: Select objects by RepresentationType from panel Clicking the RepresentationType label in the Representations panel selects all visible objects whose active representation matches that type. Ctrl+Click broadens the selection to any object that has the type in any of its representations, whether currently active or not. Generated with the assistance of an AI coding tool. --- .../bonsai/bim/module/geometry/__init__.py | 1 + .../bonsai/bim/module/geometry/operator.py | 49 +++++++++++++++++++ src/bonsai/bonsai/bim/module/geometry/ui.py | 7 ++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/bonsai/bonsai/bim/module/geometry/__init__.py b/src/bonsai/bonsai/bim/module/geometry/__init__.py index 111b67b5b05..4b8a8abb29e 100644 --- a/src/bonsai/bonsai/bim/module/geometry/__init__.py +++ b/src/bonsai/bonsai/bim/module/geometry/__init__.py @@ -69,6 +69,7 @@ operator.RemoveRepresentation, operator.RemoveRepresentationItem, operator.RemoveRepresentationItemFromShapeAspect, + operator.SelectByRepresentationType, operator.SelectConnection, operator.SelectRepresentationItem, operator.SwitchRepresentation, diff --git a/src/bonsai/bonsai/bim/module/geometry/operator.py b/src/bonsai/bonsai/bim/module/geometry/operator.py index 7382b093f4b..96a7bb7ccf9 100644 --- a/src/bonsai/bonsai/bim/module/geometry/operator.py +++ b/src/bonsai/bonsai/bim/module/geometry/operator.py @@ -417,6 +417,55 @@ def _execute(self, context): core.select_connection(tool.Geometry, connection=tool.Ifc.get().by_id(self.connection)) +class SelectByRepresentationType(bpy.types.Operator): + bl_idname = "bim.select_by_representation_type" + bl_label = "Select By Representation Type" + bl_description = ( + "Select objects whose active representation matches this type. " + "Ctrl+Click to also include objects that have this type in any representation (active or not)" + ) + bl_options = {"REGISTER", "UNDO"} + representation_type: bpy.props.StringProperty() + select_inactive: bpy.props.BoolProperty(default=False, options={"SKIP_SAVE"}) + + def invoke(self, context, event): + self.select_inactive = event.ctrl + return self.execute(context) + + def execute(self, context): + ifc = tool.Ifc.get() + if not ifc: + return {"CANCELLED"} + # Strip the "*" suffix used for mapped/resolved representations. + target_type = self.representation_type.rstrip("*") + matched = 0 + for obj in context.visible_objects: + element = tool.Ifc.get_entity(obj) + if not element: + obj.select_set(False) + continue + if self.select_inactive: + # Ctrl: match any representation on the element, active or not. + has_type = any( + (ifcopenshell.util.representation.resolve_representation(rep).RepresentationType or "") == target_type + for rep in ifcopenshell.util.representation.get_representations_iter(element) + ) + else: + # Default: match only the currently active (displayed) representation. + active_rep = tool.Geometry.get_active_representation(obj) + if active_rep is None: + obj.select_set(False) + continue + resolved = ifcopenshell.util.representation.resolve_representation(active_rep) + has_type = (resolved.RepresentationType or "") == target_type + obj.select_set(has_type) + if has_type: + matched += 1 + mode = "any representation" if self.select_inactive else "active representation" + self.report({"INFO"}, f"Selected {matched} object(s) with RepresentationType '{target_type}' ({mode})") + return {"FINISHED"} + + class RemoveConnection(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_connection" bl_label = "Remove Connection" diff --git a/src/bonsai/bonsai/bim/module/geometry/ui.py b/src/bonsai/bonsai/bim/module/geometry/ui.py index e7912c7a779..bf391c61693 100644 --- a/src/bonsai/bonsai/bim/module/geometry/ui.py +++ b/src/bonsai/bonsai/bim/module/geometry/ui.py @@ -152,7 +152,12 @@ def draw(self, context): row.label(text=representation["ContextType"]) row.label(text=representation["ContextIdentifier"]) row.label(text=representation["TargetView"]) - row.label(text=representation["RepresentationType"]) + op = row.operator( + "bim.select_by_representation_type", + text=representation["RepresentationType"], + emboss=False, + ) + op.representation_type = representation["RepresentationType"] op = row.operator( "bim.switch_representation", icon="FILE_REFRESH" if representation["is_active"] else "OUTLINER_DATA_MESH",