diff --git a/src/bonsai/bonsai/bim/helper.py b/src/bonsai/bonsai/bim/helper.py index 481d4ea080..873d368fad 100644 --- a/src/bonsai/bonsai/bim/helper.py +++ b/src/bonsai/bonsai/bim/helper.py @@ -535,7 +535,12 @@ def draw_filter( for j, ifc_filter in enumerate(filter_group.filters): if ifc_filter.type == "entity": row = box.row(align=True) - if tool.Blender.get_addon_preferences().chain_filter_with_set_operations and j > 0: + preferences = tool.Blender.get_addon_preferences() + if preferences.chain_filter_with_set_operations: + show_mode_toggle = j > 0 + else: + show_mode_toggle = preferences.default_filter_with_set_operations_for_globalid_and_class and j > 0 # PR 7315 mode + if show_mode_toggle: mode_icons = {"ADD": "ADD", "SUBTRACT": "REMOVE", "FILTER": "FILTER"} op = row.operator( "bim.toggle_filter_inclusion", @@ -749,7 +754,12 @@ def draw_filter( row.prop(ifc_filter, "value", text="") elif ifc_filter.type == "instance": row = box.row(align=True) - if tool.Blender.get_addon_preferences().chain_filter_with_set_operations and j > 0: + preferences = tool.Blender.get_addon_preferences() + if preferences.chain_filter_with_set_operations: + show_mode_toggle = j > 0 + else: + show_mode_toggle = preferences.default_filter_with_set_operations_for_globalid_and_class and j > 0 # PR 7315 mode + if show_mode_toggle: mode_icons = {"ADD": "ADD", "SUBTRACT": "REMOVE", "FILTER": "FILTER"} op = row.operator( "bim.toggle_filter_inclusion", diff --git a/src/bonsai/bonsai/bim/module/search/operator.py b/src/bonsai/bonsai/bim/module/search/operator.py index 9402aebcff..f9e7885ebe 100644 --- a/src/bonsai/bonsai/bim/module/search/operator.py +++ b/src/bonsai/bonsai/bim/module/search/operator.py @@ -760,8 +760,8 @@ class Search(Operator): else: assert_never(self.property_group) - - if tool.Blender.get_addon_preferences().chain_filter_with_set_operations: + preferences = tool.Blender.get_addon_preferences() + if preferences.chain_filter_with_set_operations or preferences.default_filter_with_set_operations_for_globalid_and_class: results = tool.Search.execute_filter_groups(props.filter_groups) else: results = ifcopenshell.util.selector.filter_elements( diff --git a/src/bonsai/bonsai/bim/ui.py b/src/bonsai/bonsai/bim/ui.py index 0cac35da80..5c7ff01226 100644 --- a/src/bonsai/bonsai/bim/ui.py +++ b/src/bonsai/bonsai/bim/ui.py @@ -691,10 +691,15 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): ) chain_filter_with_set_operations: BoolProperty( - name="Enable chained filters with set operations", + name="NEW filter mode: Enable chained filters with set operations", description="Enable chaining search filters with set operations: ADD (union: combine sets), SUBTRACT (difference: remove from set), FILTER (intersection: only elements in both sets), with autocomplete suggestions for filter values", default=False, ) + default_filter_with_set_operations_for_globalid_and_class: BoolProperty( + name="DEFAULT filter mode: Enable set operations for GlobalId/Class", + description="Enable ADD/SUBTRACT/FILTER toggle buttons on entity (Class) and instance (GlobalId) filters for the DEFAULT filter mode", + default=False, + ) if TYPE_CHECKING: svg2pdf_command: str @@ -734,6 +739,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): container_hide_show_isolate: bool mass_time_units_in_wizard: bool chain_filter_with_set_operations: bool + default_filter_with_set_operations_for_globalid_and_class: bool def draw(self, context: bpy.types.Context) -> None: layout = self.layout @@ -916,9 +922,14 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): def draw_extras_settings(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None: layout.prop(self, "container_hide_show_isolate") layout.prop(self, "mass_time_units_in_wizard") - row = layout.row(align=True) + layout.label(text="Filtering modes:") + box = layout.box() + row = box.row(align=True) row.prop(self, "chain_filter_with_set_operations") row.operator("bim.open_uri", text="", icon="HELP").uri = "https://community.osarch.org/discussion/3270" + row = box.row(align=True) + row.prop(self, "default_filter_with_set_operations_for_globalid_and_class") + row.operator("bim.open_uri", text="", icon="HELP").uri = "https://community.osarch.org/discussion/comment/27030" # Scene panel groups diff --git a/src/bonsai/bonsai/tool/search.py b/src/bonsai/bonsai/tool/search.py index ece6062f49..20c4b75387 100644 --- a/src/bonsai/bonsai/tool/search.py +++ b/src/bonsai/bonsai/tool/search.py @@ -184,23 +184,64 @@ class Search(bonsai.core.tool.Search): Within a single group chain, all filters chain sequentially with ADD/SUBTRACT/FILTER modes. Groups are combined with union (same as original " + " behavior). """ + preferences = tool.Blender.get_addon_preferences() + #print(f"\n{'='*80}") + #print(f"DEBUG: execute_filter_groups - Starting execution") + #print(f"DEBUG: Preferences - chain_filter_with_set_operations: {preferences.chain_filter_with_set_operations}") + #print(f"DEBUG: Preferences - default_filter_with_set_operations_for_globalid_and_class: {preferences.default_filter_with_set_operations_for_globalid_and_class}") + #print(f"DEBUG: Total filter groups: {len(filter_groups)}") + all_group_results = [] - for filter_group in filter_groups: + for group_idx, filter_group in enumerate(filter_groups): + #print(f"\n--- Group {group_idx} ---") group_results = set() for filter_index, ifc_filter in enumerate(filter_group.filters): + #print(f"\n Filter {filter_index}:") + #print(f" Type: {ifc_filter.type}") + #print(f" Value: {ifc_filter.value}") + #print(f" filter_mode property: {ifc_filter.filter_mode}") + if not ifc_filter.value: + #print(f" SKIPPED: No value") continue query = cls._export_single_filter(ifc_filter) if not query: + #print(f" SKIPPED: No query generated") continue - mode = "ADD" if filter_index == 0 else ifc_filter.filter_mode + #print(f" Generated query: {query}") + #print(f" Current group_results size before this filter: {len(group_results)}") + + # Determine the mode for this filter + if filter_index == 0: + mode = "ADD" # First filter is always ADD + #print(f" Mode decision: ADD (first filter)") + else: + # For entity and instance filters, check if set operations are enabled + if ifc_filter.type in ["entity", "instance"]: + # Use filter_mode only if chain mode OR default mode preference is enabled + if preferences.chain_filter_with_set_operations or preferences.default_filter_with_set_operations_for_globalid_and_class: + mode = ifc_filter.filter_mode + #print(f" Mode decision: {mode} (entity/instance with preference enabled)") + else: + # Default behavior: sequential filtering (FILTER mode) + mode = "FILTER" if group_results else "ADD" + #print(f" Mode decision: {mode} (entity/instance default behavior)") + else: + # For other filter types, use chain mode if enabled, otherwise FILTER + if preferences.chain_filter_with_set_operations: + mode = ifc_filter.filter_mode + #print(f" Mode decision: {mode} (other type with chain mode)") + else: + mode = "FILTER" if group_results else "ADD" + #print(f" Mode decision: {mode} (other type default behavior)") if mode == "ADD": results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query) + #print(f" ADD: Found {len(results)} elements, adding to group_results") group_results.update(results) elif mode == "SUBTRACT": @@ -209,9 +250,11 @@ class Search(bonsai.core.tool.Search): elements_to_remove = ifcopenshell.util.selector.filter_elements( tool.Ifc.get(), query_without_prefix, elements=group_results ) + #print(f" SUBTRACT: Removing {len(elements_to_remove)} elements from group_results") group_results -= elements_to_remove else: results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query) + #print(f" SUBTRACT: group_results empty, adding {len(results)} elements (fallback to ADD)") group_results.update(results) elif mode == "FILTER": @@ -219,11 +262,16 @@ class Search(bonsai.core.tool.Search): results = ifcopenshell.util.selector.filter_elements( tool.Ifc.get(), query, elements=group_results ) + #print(f" FILTER: Filtering group_results, result: {len(results)} elements") group_results = results else: results = ifcopenshell.util.selector.filter_elements(tool.Ifc.get(), query) + #print(f" FILTER: group_results empty, found {len(results)} elements (fallback to ADD)") group_results.update(results) + + #print(f" Group results size after this filter: {len(group_results)}") + #print(f"\n Group {group_idx} final size: {len(group_results)}") if group_results: all_group_results.append(group_results) @@ -231,6 +279,10 @@ class Search(bonsai.core.tool.Search): for group_results in all_group_results: final_results.update(group_results) + #print(f"\n{'='*80}") + #print(f"DEBUG: Final combined results: {len(final_results)} elements") + #print(f"{'='*80}\n") + return final_results @classmethod