From fcab5d445f574bb56fff75c4302b19938b7bc3b5 Mon Sep 17 00:00:00 2001 From: Vukas Pajic <83825269+vulevukusej@users.noreply.github.com> Date: Thu, 21 Jul 2022 08:20:25 +0200 Subject: [PATCH] test features --- .../blenderbim/bim/module/search/operator.py | 205 ++++++++++++++++++ .../test/bim/feature/search.feature | 16 ++ src/blenderbim/test/bim/test_feature.py | 9 + .../ifcopenshell/api/group/assign_group.py | 2 +- 4 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 src/blenderbim/test/bim/feature/search.feature diff --git a/src/blenderbim/blenderbim/bim/module/search/operator.py b/src/blenderbim/blenderbim/bim/module/search/operator.py index 5004f5c7fa..0e39d4a424 100644 --- a/src/blenderbim/blenderbim/bim/module/search/operator.py +++ b/src/blenderbim/blenderbim/bim/module/search/operator.py @@ -433,3 +433,208 @@ class ActivateIfcBuildingStoreyFilter(bpy.types.Operator): row = self.layout.row(align=True) row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT" row.operator("bim.toggle_filter_selection", text="Deselect All").action = "DESELECT" + +class UnhideAllElements(Operator): + """Filter model elements based on selection""" + + bl_idname = "bim.reset_3d_view" + bl_label = "Reset 3D View" + bl_idname = "bim.unhide_all_elements" + bl_label = "Unhide All Elements" + + def execute(self, context): + for obj in bpy.data.scenes["Scene"].objects: + obj.hide_set(False) + return {"FINISHED"} + + +class FilterModelElements(Operator): + """Filter model elements based on selection""" + + bl_idname = "bim.filter_model_elements" + bl_label = "Filter Model Elements" + option: StringProperty("select|isolate|hide", default="select") + + def execute(self, context): + selector = context.scene.IfcSelectorProperties + selection = selector.selector_query_syntax if selector.manual_override else self.add_groups(selector) + selector.selector_query_syntax = selection + self.update_model_view(context, selection) + return {"FINISHED"} + + def add_groups(self, selector): + selection = "" + for group_index, group in enumerate(selector.groups): + if group_index != 0: + selection += " | " + selection += "(" if len(selector.groups) > 1 else "" + selection = self.add_queries(selection, group) + + selection += ")" if len(selector.groups) > 1 else "" + return selection + + def add_queries(self, selection, group): + for query_index, query in enumerate(group.queries): + if query_index != 0: + selection += " & " if query.and_or == "and" else " | " + + if query.selector == "IFC Class": + active_option = query.active_option.split(": ")[1] + selection += f".{active_option}" + selection = self.add_filters(selection, query) + + elif query.selector == "GlobalId": + selection += f"#{query.value}" + + elif query.selector == "IfcElementType": + index = int(query.active_sub_option.split(":")[0]) + selection += f"* #{query.sub_options[index].global_id}" + + elif query.selector == "IfcSpatialElement": + index = int(query.active_sub_option.split(":")[0]) + selection += f"@ #{query.sub_options[index].global_id}" + return selection + + def add_filters(self, selection, query): + for f_index, f in enumerate(query.filters): + + if f_index != 0: + selection += " & " if f.and_or == "and" else " | " + selection += f".{query.active_option}" + + selection += "[" + + if f.selector == "IfcPropertySet": + selection += f'{f.active_option.split(": ")[1]}.{f.active_sub_option.split(": ")[1]} {"!" if f.negation else ""}="{f.value}"' + elif f.selector == "Attribute": + selection += f'{f.attribute} {"!" if f.negation else ""}= "{f.value}"' + + selection += "]" + return selection + + def update_model_view(self, context, selection): + query = Selector.parse(IfcStore.file, selection) + sel_element_ids = [e.id() for e in query] + bpy.ops.object.select_all(action="DESELECT") + + for obj in bpy.data.scenes["Scene"].objects: + obj.hide_set(False) # reset 3d view + + if self.option == "select": + if obj.BIMObjectProperties.ifc_definition_id in sel_element_ids: + obj.select_set(True) + elif self.option == "isolate": + if obj.BIMObjectProperties.ifc_definition_id not in sel_element_ids: + obj.hide_set(True) + elif self.option == "hide": + if obj.BIMObjectProperties.ifc_definition_id in sel_element_ids: + obj.hide_set(True) + + +class IfcSelector(Operator): + """Select elements in model with IFC Selector""" + bl_idname = "bim.ifc_selector" + bl_label = "Select elements with IFC Selector" + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width=800) + + @classmethod + def poll(cls, context): + return IfcStore.get_file() + + def execute(self, context): + return {"FINISHED"} + + def draw(self, context): + from . import ui + ui.IfcSelectorUI.draw(context, self.layout) + + +class SaveSelectorQuery(Operator): + bl_idname = "bim.save_selector_query" + bl_label = "Save Selector Query" + save_name: StringProperty() + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context): + layout = self.layout + layout.prop(self, "save_name") + + def execute(self, context): + ifc_selector = context.scene.IfcSelectorProperties + new = ifc_selector.query_library.add() + new.name = self.save_name + new.query = ifc_selector.selector_query_syntax + return {"FINISHED"} + + +class OpenQueryLibrary(Operator): + """Open Query Library""" + bl_idname = "bim.open_query_library" + bl_label = "Open Query Library" + + def invoke(self, context, event): + return context.window_manager.invoke_popup(self, width=400) + + def draw(self, context): + layout = self.layout + ifc_selector = context.scene.IfcSelectorProperties + + for index, query in enumerate(ifc_selector.query_library): + row = layout.row(align=True) + row.prop(query, "query", text=query.name) + op = row.operator("bim.load_query", icon="SORT_ASC", text="") + op.index = index + + row.context_pointer_set(name="bim_prop_group", data=ifc_selector) + op = row.operator("bim.edit_blender_collection", icon="REMOVE", text="") + op.option = "remove" + op.collection = "query_library" + op.index = index + + def execute(self, context): + return {"FINISHED"} + + +class LoadQuery(Operator): + bl_idname = "bim.load_query" + bl_label = "Load Query" + index: IntProperty() + + def invoke(self, context, event): + close_operator_panel(event) + return self.execute(context) + + def execute(self, context): + ifc_selector = context.scene.IfcSelectorProperties + ifc_selector.selector_query_syntax = ifc_selector.query_library[self.index].query + return {"FINISHED"} + +class AddToIfcGroup(Operator): + bl_idname = "bim.add_to_ifc_group" + bl_label = "Add to IFC Group" + group_name: StringProperty(name="Group Name") + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self, width=400) + + def draw(self, context): + layout = self.layout + layout.prop(self, "group_name") + + def execute(self, context): + self.file = IfcStore.get_file() + ifc_selector = context.scene.IfcSelectorProperties + selector_query_syntax = ifc_selector.selector_query_syntax + + group = ifcopenshell.api.run("group.add_group", self.file, **{"Name": self.group_name}) + objects = Selector.parse(self.file, selector_query_syntax) + + ifcopenshell.api.run("group.assign_group", self.file, **{"product": objects, "group": group}) + + Data.load(IfcStore.get_file()) + + return {"FINISHED"} diff --git a/src/blenderbim/test/bim/feature/search.feature b/src/blenderbim/test/bim/feature/search.feature new file mode 100644 index 0000000000..76217d6f98 --- /dev/null +++ b/src/blenderbim/test/bim/feature/search.feature @@ -0,0 +1,16 @@ +@root +Feature: Search + +Scenario: Select all walls + Given an empty IFC project + And I add a cube + When the object "Cube" is selected + And I set "scene.BIMRootProperties.ifc_class" to "IfcWall" + And I press "bim.assign_class" + And I add a new group to IfcSelector + And I add a new query to IfcSelector + And I set "scene.IfcSelectorProperties.groups[0].queries[0].active_option" to "IFC Class" + And I set "scene.IfcSelectorProperties.groups[0].queries[0].active_option" to "124: IfcWall" + And I press 'bim.filter_model_elements' + Then bpy.context.selected_objects should contain "Cube" + diff --git a/src/blenderbim/test/bim/test_feature.py b/src/blenderbim/test/bim/test_feature.py index 4cd6dbcd5a..b56785404a 100644 --- a/src/blenderbim/test/bim/test_feature.py +++ b/src/blenderbim/test/bim/test_feature.py @@ -98,6 +98,15 @@ def i_add_a_sun(): def i_add_a_material(): bpy.context.active_object.active_material = bpy.data.materials.new("Material") +@given("I add a new group to IfcSelector") +@when("I add a new group to IfcSelector") +def i_add_a_new_collection_item(): + bpy.data.scenes["Scene"].IfcSelectorProperties.groups.add() + +@given("I add a new query to IfcSelector") +@when("I add a new query to IfcSelector") +def i_add_a_new_collection_item(): + bpy.data.scenes["Scene"].IfcSelectorProperties.groups[0].queries.add() @given(parsers.parse('the material "{name}" colour is set to "{colour}"')) @when(parsers.parse('the material "{name}" colour is set to "{colour}"')) diff --git a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py index b8cdea2d9f..ed252a1878 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py +++ b/src/ifcopenshell-python/ifcopenshell/api/group/assign_group.py @@ -37,7 +37,7 @@ class Usecase: **{ "GlobalId": ifcopenshell.guid.new(), "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatedObjects": [self.settings["product"]], + "RelatedObjects": self.settings["product"], "RelatingGroup": self.settings["group"], } )