From 83d7cda26b8e0f5e125d1bee98bbceb8c2fd1c7f Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 29 Jan 2025 16:52:04 +0500 Subject: [PATCH] Select IfcProjectLibrary to display for the loaded library Example - https://imgur.com/a/14LOL0J --- src/bonsai/bonsai/bim/module/project/data.py | 12 ++++++ .../bonsai/bim/module/project/operator.py | 11 ++++- src/bonsai/bonsai/bim/module/project/prop.py | 24 ++++++++++- src/bonsai/bonsai/bim/module/project/ui.py | 1 + src/bonsai/bonsai/tool/project.py | 41 ++++++++++++++++++- 5 files changed, 85 insertions(+), 4 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/project/data.py b/src/bonsai/bonsai/bim/module/project/data.py index 43d9b13273..9a6c9855ab 100644 --- a/src/bonsai/bonsai/bim/module/project/data.py +++ b/src/bonsai/bonsai/bim/module/project/data.py @@ -116,6 +116,8 @@ class ProjectLibraryData: def load(cls): cls.data = {} cls.data["project_libraries"] = cls.project_libraries() + # After .project_libraries(). + cls.data["project_libraries_enum"] = cls.project_libraries_enum() cls.is_loaded = True @classmethod @@ -129,6 +131,16 @@ class ProjectLibraryData: results[l.id()] = {k: v for k, v in l.get_info().items() if k in KEEP_ATTRS} return results + @classmethod + def project_libraries_enum(cls) -> list[tuple[str, str, str]]: + results = [ + ("*", "All Libraries", "Show all elements"), + ("-", "No Library", "Show elements without library assigned"), + ] + for data in cls.data["project_libraries"].values(): + results.append((str(data["id"]), data["Name"] or "", data["Description"] or "")) + return results + class LinksData: linked_data = {} diff --git a/src/bonsai/bonsai/bim/module/project/operator.py b/src/bonsai/bonsai/bim/module/project/operator.py index dbaf471945..c399042a92 100644 --- a/src/bonsai/bonsai/bim/module/project/operator.py +++ b/src/bonsai/bonsai/bim/module/project/operator.py @@ -214,10 +214,15 @@ class RefreshLibrary(bpy.types.Operator): self.props.active_library_element = "" + library_file = IfcStore.library_file + assert library_file + + condition = tool.Project.get_filter_for_active_library() for importable_type in sorted(tool.Project.get_appendable_asset_types()): - if IfcStore.library_file.by_type(importable_type): + if (elements := library_file.by_type(importable_type)) and next(condition(elements), None): new = self.props.library_elements.add() new.name = importable_type + return {"FINISHED"} @@ -225,7 +230,7 @@ class ChangeLibraryElement(bpy.types.Operator): bl_idname = "bim.change_library_element" bl_label = "Change Library Element" bl_options = {"REGISTER", "UNDO"} - element_name: bpy.props.StringProperty() + element_name: bpy.props.StringProperty(description="IFC class to select") if TYPE_CHECKING: element_name: str @@ -242,7 +247,9 @@ class ChangeLibraryElement(bpy.types.Operator): crumb = self.props.library_breadcrumb.add() crumb.name = self.element_name + filter_elements = tool.Project.get_filter_for_active_library() elements = self.library_file.by_type(self.element_name) + elements = list(filter_elements(elements)) [ifc_classes.add(e.is_a()) for e in elements] self.props.library_elements.clear() diff --git a/src/bonsai/bonsai/bim/module/project/prop.py b/src/bonsai/bonsai/bim/module/project/prop.py index 6de806391b..bc65a42f5d 100644 --- a/src/bonsai/bonsai/bim/module/project/prop.py +++ b/src/bonsai/bonsai/bim/module/project/prop.py @@ -21,7 +21,7 @@ import bpy import ifcopenshell.util.element import ifcopenshell.util.placement import bonsai.tool as tool -from bonsai.bim.module.project.data import ProjectData +from bonsai.bim.module.project.data import ProjectData, ProjectLibraryData from bonsai.bim.ifc import IfcStore from bonsai.bim.prop import StrProperty, ObjProperty from bpy.types import PropertyGroup @@ -66,6 +66,16 @@ def update_library_file(self: "BIMProjectProperties", context: bpy.types.Context bpy.ops.bim.select_library_file(filepath=filepath.__str__()) +def update_selected_project_library(self: "BIMProjectProperties", context: bpy.types.Context) -> None: + bpy.ops.bim.refresh_library() + + +def get_project_libaries(self: "BIMProjectProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]: + if not ProjectLibraryData.is_loaded: + ProjectLibraryData.load() + return ProjectLibraryData.data["project_libraries_enum"] + + def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context) -> None: self.filter_categories.clear() if self.filter_mode == "NONE": @@ -260,7 +270,16 @@ class BIMProjectProperties(PropertyGroup): active_link_index: IntProperty(name="Active Link Index") export_schema: EnumProperty(items=get_export_schema, name="IFC Schema", update=update_export_schema) template_file: EnumProperty(items=get_template_file, name="Template File") + + # Project library UI. library_file: EnumProperty(items=get_library_file, name="Library File", update=update_library_file) + selected_project_library: EnumProperty( + items=get_project_libaries, + name="Project Library", + description="Project library to display elements from", + update=update_selected_project_library, + ) + use_relative_project_path: BoolProperty(name="Use Relative Project Path", default=False) queried_obj: bpy.props.PointerProperty(type=bpy.types.Object) queried_obj_root: bpy.props.PointerProperty(type=bpy.types.Object) @@ -317,7 +336,10 @@ class BIMProjectProperties(PropertyGroup): active_link_index: int export_schema: str template_file: str + library_file: str + selected_project_library: Union[Literal["*", "-"], str] + use_relative_project_path: bool queried_obj: Union[bpy.types.Object, None] queried_obj_root: Union[bpy.types.Object, None] diff --git a/src/bonsai/bonsai/bim/module/project/ui.py b/src/bonsai/bonsai/bim/module/project/ui.py index 509e63f4fc..ba9e1b057c 100644 --- a/src/bonsai/bonsai/bim/module/project/ui.py +++ b/src/bonsai/bonsai/bim/module/project/ui.py @@ -367,6 +367,7 @@ class BIM_PT_project_library(Panel): row = self.layout.row() row.label(text="No Assets Found", icon="ERROR") return + self.layout.prop(self.props, "selected_project_library", text="") row = self.layout.row(align=True) row.label(text=self.props.active_library_element or "Top Level Assets") if self.props.active_library_element: diff --git a/src/bonsai/bonsai/tool/project.py b/src/bonsai/bonsai/tool/project.py index ea137067a3..81149dc898 100644 --- a/src/bonsai/bonsai/tool/project.py +++ b/src/bonsai/bonsai/tool/project.py @@ -34,7 +34,7 @@ import bonsai.tool as tool from bonsai.bim.ifc import IfcStore from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES from pathlib import Path -from typing import Optional, Union, TYPE_CHECKING +from typing import Optional, Union, TYPE_CHECKING, Generator, Callable if TYPE_CHECKING: from bonsai.bim.module.project.prop import BIMProjectProperties @@ -294,3 +294,42 @@ class Project(bonsai.core.tool.Project): if references_to_remove: for reference in references_to_remove: ifcopenshell.api.document.remove_reference(ifc_file, reference) + + @classmethod + def get_filter_for_active_library( + cls, + ) -> Callable[[list[ifcopenshell.entity_instance]], Generator[ifcopenshell.entity_instance, None, None]]: + props = cls.get_project_props() + library_file = IfcStore.library_file + assert library_file + + if props.selected_project_library == "*": + + def condition(elements: list[ifcopenshell.entity_instance]): + yield from elements + + elif props.selected_project_library == "-": + + def condition(elements: list[ifcopenshell.entity_instance]): + for element in elements: + if not getattr(element, "HasContext", False): + yield element + + else: + project_library = library_file.by_id(int(props.selected_project_library)) + project_library_rels = set(project_library.Declares) + if project_library_rels: + + def condition(elements: list[ifcopenshell.entity_instance]): + for element in elements: + for rel in getattr(element, "HasContext", ()): + if rel in project_library_rels: + yield element + break + + else: + + def condition(elements: list[ifcopenshell.entity_instance]): + pass + + return condition