mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Library UI - tree view
Example of tree view with libraries hierarchy - https://imgur.com/a/zPuaoPv Example of library assignment in non-tree view - https://imgur.com/a/9kZFblj
This commit is contained in:
@@ -66,6 +66,7 @@ classes = (
|
||||
operator.UnlinkIfc,
|
||||
operator.UnloadLink,
|
||||
workspace.ExploreHotkey,
|
||||
prop.LibraryBreadcrumb,
|
||||
prop.LibraryElement,
|
||||
prop.FilterCategory,
|
||||
prop.Link,
|
||||
|
||||
@@ -134,10 +134,11 @@ class ProjectLibraryData:
|
||||
|
||||
@classmethod
|
||||
def project_libraries_enum(cls) -> list[tuple[str, str, str, str, int]]:
|
||||
results = [
|
||||
("*", "All Libraries", "Show all elements", "", 0),
|
||||
("-", "No Library", "Show elements without library assigned", "", 1),
|
||||
]
|
||||
results = []
|
||||
project_libraries = cls.data["project_libraries"].values()
|
||||
if not project_libraries:
|
||||
results.append(("-", "No Library", "", "", 0))
|
||||
|
||||
props = tool.Project.get_project_props()
|
||||
libs = []
|
||||
for i, data in enumerate(cls.data["project_libraries"].values(), len(results)):
|
||||
|
||||
@@ -60,9 +60,10 @@ from bpy.app.handlers import persistent
|
||||
from ifcopenshell.geom import ShapeElementType
|
||||
from bonsai.bim.module.project.data import LinksData, ProjectLibraryData
|
||||
from bonsai.bim.module.project.decorator import ProjectDecorator, ClippingPlaneDecorator, MeasureDecorator
|
||||
from bonsai.bim.module.project.prop import BreadcrumbType
|
||||
from bonsai.bim.module.model.decorator import PolylineDecorator
|
||||
from bonsai.bim.module.model.polyline import PolylineOperator
|
||||
from typing import Union, TYPE_CHECKING
|
||||
from typing import Union, TYPE_CHECKING, Literal, get_args
|
||||
|
||||
|
||||
class NewProject(bpy.types.Operator):
|
||||
@@ -184,6 +185,7 @@ class SelectLibraryFile(bpy.types.Operator, IFCFileSelector):
|
||||
context.area.tag_redraw()
|
||||
if self.append_all:
|
||||
bpy.ops.bim.append_entire_library()
|
||||
ProjectLibraryData.load()
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -218,16 +220,27 @@ class RefreshLibrary(bpy.types.Operator):
|
||||
self.props.library_elements.clear()
|
||||
self.props.library_breadcrumb.clear()
|
||||
|
||||
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 (elements := library_file.by_type(importable_type)) and (elements := list(condition(elements))):
|
||||
elements = self.props.add_library_asset_group(importable_type, len(elements))
|
||||
if not self.props.show_library_tree:
|
||||
for appendable_type in sorted(tool.Project.get_appendable_asset_types()):
|
||||
elements = library_file.by_type(appendable_type)
|
||||
self.props.add_library_asset_class(appendable_type, len(elements))
|
||||
return {"FINISHED"}
|
||||
|
||||
# Library tree.
|
||||
# Add entry for unassigned elements.
|
||||
elements = set()
|
||||
for importable_type in sorted(tool.Project.get_appendable_asset_types()):
|
||||
elements.update(library_file.by_type(importable_type))
|
||||
rels = tool.Project.get_project_library_rels(library_file)
|
||||
elements = {e for e in elements if not tool.Project.is_element_assigned_to_project_library(e, rels)}
|
||||
self.props.add_library_project_library("Unassigned", len(elements), 0)
|
||||
|
||||
ifc_project = library_file.by_type("IfcProject")[0]
|
||||
hierarchy = tool.Project.get_project_hierarchy(library_file)
|
||||
tool.Project.load_project_libraries_to_ui(ifc_project, hierarchy)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -235,10 +248,14 @@ 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(description="IFC class to select")
|
||||
element_name: bpy.props.StringProperty()
|
||||
breadcrumb_type: bpy.props.EnumProperty(items=[(i, i, "") for i in get_args(BreadcrumbType)])
|
||||
library_id: bpy.props.IntProperty()
|
||||
|
||||
if TYPE_CHECKING:
|
||||
element_name: str
|
||||
breadcrumb_type: BreadcrumbType
|
||||
library_id: int
|
||||
|
||||
def execute(self, context):
|
||||
self.props = tool.Project.get_project_props()
|
||||
@@ -246,33 +263,81 @@ class ChangeLibraryElement(bpy.types.Operator):
|
||||
library_file = IfcStore.library_file
|
||||
assert library_file
|
||||
self.library_file = library_file
|
||||
self.props.active_library_element = self.element_name
|
||||
|
||||
crumb = self.props.library_breadcrumb.add()
|
||||
crumb.name = self.element_name
|
||||
crumb.breadcrumb_type = self.breadcrumb_type
|
||||
if self.breadcrumb_type == "LIBRARY":
|
||||
crumb.library_id = self.library_id
|
||||
|
||||
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_elements: dict[str, list[ifcopenshell.entity_instance]] = defaultdict(list)
|
||||
for element in elements:
|
||||
ifc_classes_elements[element.is_a()].append(element)
|
||||
active_project_library = None
|
||||
library_elements = None
|
||||
project_library_rels = None
|
||||
# Reverse to get last library in hierarchy.
|
||||
for entry in reversed(self.props.library_breadcrumb):
|
||||
if entry.breadcrumb_type == "LIBRARY":
|
||||
if entry.library_id == 0:
|
||||
# For unassigned elements.
|
||||
active_project_library = "NO_LIBRARY"
|
||||
project_library_rels = tool.Project.get_project_library_rels(library_file)
|
||||
else:
|
||||
active_project_library = library_file.by_id(entry.library_id)
|
||||
library_elements = tool.Project.get_project_library_elements(active_project_library)
|
||||
break
|
||||
|
||||
def filter_elements(elements: list[ifcopenshell.entity_instance]) -> list[ifcopenshell.entity_instance]:
|
||||
if active_project_library is None:
|
||||
return elements
|
||||
elif active_project_library == "NO_LIBRARY":
|
||||
assert project_library_rels is not None
|
||||
return [
|
||||
element
|
||||
for element in elements
|
||||
if not tool.Project.is_element_assigned_to_project_library(element, project_library_rels)
|
||||
]
|
||||
else:
|
||||
assert library_elements is not None
|
||||
return [e for e in elements if e in library_elements]
|
||||
|
||||
self.props.library_elements.clear()
|
||||
|
||||
if len(ifc_classes_elements) == 1 and list(ifc_classes_elements)[0] == self.element_name:
|
||||
for name, ifc_definition_id in sorted(
|
||||
[(self.get_name(e), e.id()) for e in ifc_classes_elements[self.element_name]]
|
||||
):
|
||||
self.add_library_asset(name, ifc_definition_id)
|
||||
else:
|
||||
for ifc_class in sorted(ifc_classes_elements):
|
||||
if ifc_class == self.element_name:
|
||||
continue
|
||||
self.props.add_library_asset_group(ifc_class, len(ifc_classes_elements[ifc_class]))
|
||||
elements_ = ifc_classes_elements[self.element_name]
|
||||
for name, ifc_definition_id, ifc_class in sorted([(self.get_name(e), e.id(), e.is_a()) for e in elements_]):
|
||||
self.add_library_asset(name, ifc_definition_id)
|
||||
if self.breadcrumb_type == "LIBRARY":
|
||||
hierarchy = tool.Project.get_project_hierarchy(library_file)
|
||||
assert active_project_library is not None
|
||||
if active_project_library == "NO_LIBRARY" or not hierarchy[active_project_library]:
|
||||
for appendable_type in sorted(tool.Project.get_appendable_asset_types()):
|
||||
elements = library_file.by_type(appendable_type)
|
||||
if elements := filter_elements(elements):
|
||||
self.props.add_library_asset_class(appendable_type, len(elements))
|
||||
else:
|
||||
tool.Project.load_project_libraries_to_ui(active_project_library, hierarchy)
|
||||
else: # breadcrumb_type CLASS.
|
||||
elements = self.library_file.by_type(self.element_name)
|
||||
elements = list(filter_elements(elements))
|
||||
ifc_classes_elements: dict[str, list[ifcopenshell.entity_instance]] = defaultdict(list)
|
||||
for element in elements:
|
||||
ifc_classes_elements[element.is_a()].append(element)
|
||||
|
||||
if len(ifc_classes_elements) == 1 and list(ifc_classes_elements)[0] == self.element_name:
|
||||
for name, ifc_definition_id in sorted(
|
||||
[(self.get_name(e), e.id()) for e in ifc_classes_elements[self.element_name]]
|
||||
):
|
||||
self.add_library_asset(name, ifc_definition_id)
|
||||
else:
|
||||
for ifc_class in sorted(ifc_classes_elements):
|
||||
if ifc_class == self.element_name:
|
||||
continue
|
||||
self.props.add_library_asset_class(ifc_class, len(ifc_classes_elements[ifc_class]))
|
||||
elements_ = ifc_classes_elements[self.element_name]
|
||||
for name, ifc_definition_id, ifc_class in sorted(
|
||||
[(self.get_name(e), e.id(), e.is_a()) for e in elements_]
|
||||
):
|
||||
self.add_library_asset(name, ifc_definition_id)
|
||||
|
||||
# Could occur if all elements were assigned to a different library.
|
||||
if len(self.props.library_elements) == 0:
|
||||
bpy.ops.bim.rewind_library()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_name(self, element: ifcopenshell.entity_instance) -> str:
|
||||
@@ -300,10 +365,7 @@ class ChangeLibraryElement(bpy.types.Operator):
|
||||
elif has_context := element.HasContext:
|
||||
relating_context: ifcopenshell.entity_instance
|
||||
relating_context = has_context[0].RelatingContext
|
||||
if selected_library in ("-", "*"):
|
||||
new.is_declared = relating_context.is_a("IfcProjectLibrary")
|
||||
else:
|
||||
new.is_declared = relating_context == self.library_file.by_id(int(selected_library))
|
||||
new.is_declared = relating_context == self.library_file.by_id(int(selected_library))
|
||||
|
||||
# is_appended.
|
||||
try:
|
||||
@@ -329,10 +391,17 @@ class RewindLibrary(bpy.types.Operator):
|
||||
if total_breadcrumbs < 2:
|
||||
bpy.ops.bim.refresh_library()
|
||||
return {"FINISHED"}
|
||||
element_name = self.props.library_breadcrumb[total_breadcrumbs - 2].name
|
||||
current_element = self.props.library_breadcrumb[total_breadcrumbs - 2]
|
||||
element_name = current_element.name
|
||||
breadcrumb_type = current_element.breadcrumb_type
|
||||
library_id = current_element.library_id
|
||||
self.props.library_breadcrumb.remove(total_breadcrumbs - 1)
|
||||
self.props.library_breadcrumb.remove(total_breadcrumbs - 2)
|
||||
bpy.ops.bim.change_library_element(element_name=element_name)
|
||||
bpy.ops.bim.change_library_element(
|
||||
element_name=element_name,
|
||||
breadcrumb_type=breadcrumb_type,
|
||||
library_id=library_id,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -360,10 +429,7 @@ class AssignLibraryDeclaration(bpy.types.Operator):
|
||||
library_file = IfcStore.library_file
|
||||
assert library_file
|
||||
|
||||
if props.selected_project_library in ("*", "-"):
|
||||
project_library = library_file.by_type("IfcProjectLibrary")[0]
|
||||
else:
|
||||
project_library = library_file.by_id(int(props.selected_project_library))
|
||||
project_library = library_file.by_id(int(props.selected_project_library))
|
||||
|
||||
ifcopenshell.api.project.assign_declaration(
|
||||
library_file,
|
||||
@@ -648,6 +714,7 @@ class EditProjectLibrary(bpy.types.Operator):
|
||||
ifcopenshell.api.nest.assign_object(library_file, [project_library], new_parent_library)
|
||||
|
||||
props.is_editing_project_library = False
|
||||
bpy.ops.bim.refresh_library()
|
||||
return {"FINISHED"}
|
||||
|
||||
def rollback(self, data):
|
||||
|
||||
@@ -35,7 +35,7 @@ from bpy.props import (
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Literal, Union
|
||||
from typing import TYPE_CHECKING, Literal, Union, get_args
|
||||
|
||||
|
||||
def get_export_schema(self: "BIMProjectProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
@@ -67,15 +67,15 @@ def update_library_file(self: "BIMProjectProperties", context: bpy.types.Context
|
||||
bpy.ops.bim.select_library_file(filepath=filepath.__str__())
|
||||
ProjectLibraryData.load()
|
||||
props = tool.Project.get_project_props()
|
||||
props.selected_project_library = "*"
|
||||
library_file = IfcStore.library_file
|
||||
assert library_file
|
||||
project_library = next(iter(library_file.by_type("IfcProjectLibrary")), None)
|
||||
props.selected_project_library = str(project_library.id()) if project_library else "-"
|
||||
|
||||
|
||||
def update_selected_project_library(self: "BIMProjectProperties", context: bpy.types.Context) -> None:
|
||||
if self.filter_by_library:
|
||||
bpy.ops.bim.refresh_library()
|
||||
else:
|
||||
# Ensure `.is_declared` up to date.
|
||||
tool.Project.update_current_library_page()
|
||||
# Ensure `.is_declared` up to date.
|
||||
tool.Project.update_current_library_page()
|
||||
|
||||
|
||||
def get_project_libaries(self: "BIMProjectProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
@@ -84,10 +84,7 @@ def get_project_libaries(self: "BIMProjectProperties", context: bpy.types.Contex
|
||||
return ProjectLibraryData.data["project_libraries_enum"]
|
||||
|
||||
|
||||
def filter_by_library_update(self: "BIMProjectProperties", context: bpy.types.Context) -> None:
|
||||
if self.filter_by_library and self.selected_project_library == "*":
|
||||
# Filter is toggled from OFF to ON, so it was showing all elements previously either way.
|
||||
return
|
||||
def show_library_tree_update(self: "BIMProjectProperties", context: bpy.types.Context) -> None:
|
||||
bpy.ops.bim.refresh_library()
|
||||
|
||||
|
||||
@@ -150,8 +147,12 @@ def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context)
|
||||
new.total_elements = len(ifcopenshell.util.element.get_types(ifc_type))
|
||||
|
||||
|
||||
LibraryElementType = Literal["ASSET", "CLASS", "LIBRARY"]
|
||||
|
||||
|
||||
class LibraryElement(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
element_type: EnumProperty(items=[(i, i, "") for i in get_args(LibraryElementType)], name="Element Type")
|
||||
# Asset group.
|
||||
asset_count: IntProperty(name="Asset Count")
|
||||
# Asset.
|
||||
@@ -166,6 +167,7 @@ class LibraryElement(PropertyGroup):
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: str
|
||||
element_type: LibraryElementType
|
||||
asset_count: int
|
||||
ifc_definition_id: int
|
||||
is_declared: bool
|
||||
@@ -217,6 +219,18 @@ class EditedObj(PropertyGroup):
|
||||
obj: Union[bpy.types.Object, None]
|
||||
|
||||
|
||||
BreadcrumbType = Literal["LIBRARY", "CLASS"]
|
||||
|
||||
|
||||
class LibraryBreadcrumb(PropertyGroup):
|
||||
breadcrumb_type: EnumProperty(items=[(i, i, "") for i in get_args(BreadcrumbType)])
|
||||
library_id: IntProperty(description="IFC Definition ID for libraries.")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
breadcrumb_type: BreadcrumbType
|
||||
library_id: int
|
||||
|
||||
|
||||
class BIMProjectProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
is_loading: BoolProperty(name="Is Loading", default=False)
|
||||
@@ -226,8 +240,7 @@ class BIMProjectProperties(PropertyGroup):
|
||||
organisation_name: StringProperty(name="Organisation")
|
||||
organisation_email: StringProperty(name="Organisation Email")
|
||||
authorisation: StringProperty(name="Authoriser")
|
||||
active_library_element: StringProperty(name="Enable Authoring Mode", default="")
|
||||
library_breadcrumb: CollectionProperty(name="Library Breadcrumb", type=StrProperty)
|
||||
library_breadcrumb: CollectionProperty(name="Library Breadcrumb", type=LibraryBreadcrumb)
|
||||
library_elements: CollectionProperty(name="Library Elements", type=LibraryElement)
|
||||
active_library_element_index: IntProperty(name="Active Library Element Index")
|
||||
filter_mode: bpy.props.EnumProperty(
|
||||
@@ -321,15 +334,15 @@ class BIMProjectProperties(PropertyGroup):
|
||||
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",
|
||||
name="Selected Project Library",
|
||||
description="Selected project library to edit or to assign elements to",
|
||||
update=update_selected_project_library,
|
||||
)
|
||||
filter_by_library: BoolProperty(
|
||||
name="Filter by Library",
|
||||
description="Filter library elements based on selected library. If unselected can be used to assign selected library to library elements.",
|
||||
show_library_tree: BoolProperty(
|
||||
name="Show Library Tree",
|
||||
description="Show project libraries hierarchy or just show the assets classes.",
|
||||
default=True,
|
||||
update=filter_by_library_update,
|
||||
update=show_library_tree_update,
|
||||
)
|
||||
is_editing_project_library: BoolProperty(
|
||||
name="Is Editing Project Library",
|
||||
@@ -357,10 +370,19 @@ class BIMProjectProperties(PropertyGroup):
|
||||
def clipping_planes_objs(self) -> list[bpy.types.Object]:
|
||||
return list({cp.obj for cp in self.clipping_planes if cp.obj})
|
||||
|
||||
def add_library_asset_group(self, name: str, asset_count: int) -> LibraryElement:
|
||||
def add_library_project_library(self, name: str, asset_count: int, ifc_definition_id: int) -> LibraryElement:
|
||||
new = self.library_elements.add()
|
||||
new.name = name
|
||||
new.asset_count = asset_count
|
||||
new.element_type = "LIBRARY"
|
||||
new.ifc_definition_id = ifc_definition_id
|
||||
return new
|
||||
|
||||
def add_library_asset_class(self, name: str, asset_count: int) -> LibraryElement:
|
||||
new = self.library_elements.add()
|
||||
new.name = name
|
||||
new.asset_count = asset_count
|
||||
new.element_type = "CLASS"
|
||||
return new
|
||||
|
||||
def get_library_element_index(self, lib_element: LibraryElement) -> int:
|
||||
@@ -375,8 +397,7 @@ class BIMProjectProperties(PropertyGroup):
|
||||
organisation_name: str
|
||||
organisation_email: str
|
||||
authorisation: str
|
||||
active_library_element: str
|
||||
library_breadcrumb: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
library_breadcrumb: bpy.types.bpy_prop_collection_idprop[LibraryBreadcrumb]
|
||||
library_elements: bpy.types.bpy_prop_collection_idprop[LibraryElement]
|
||||
active_library_element_index: int
|
||||
filter_mode: Literal["NONE", "DECOMPOSITION", "IFC_CLASS", "IFC_TYPE", "WHITELIST", "BLACKLIST"]
|
||||
@@ -410,8 +431,8 @@ class BIMProjectProperties(PropertyGroup):
|
||||
template_file: str
|
||||
|
||||
library_file: str
|
||||
selected_project_library: Union[Literal["*", "-"], str]
|
||||
filter_by_library: bool
|
||||
selected_project_library: Union[Literal["-"], str]
|
||||
show_library_tree: bool
|
||||
is_editing_project_library: bool
|
||||
editing_project_library_id: int
|
||||
project_library_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
@@ -424,6 +445,11 @@ class BIMProjectProperties(PropertyGroup):
|
||||
clipping_planes_active: int
|
||||
edited_objs: bpy.types.bpy_prop_collection_idprop[EditedObj]
|
||||
|
||||
def get_active_library_breadcrumb(self) -> Union[LibraryBreadcrumb, None]:
|
||||
if self.library_breadcrumb:
|
||||
return self.library_breadcrumb[-1]
|
||||
return None
|
||||
|
||||
|
||||
class MeasureToolSettings(PropertyGroup):
|
||||
measurement_type_items = [
|
||||
|
||||
@@ -392,7 +392,7 @@ class BIM_PT_project_library(Panel):
|
||||
|
||||
library_file = IfcStore.library_file
|
||||
assert library_file
|
||||
library_is_selected = props.selected_project_library not in ("*", "-")
|
||||
library_is_selected = props.selected_project_library != "-"
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(self.props, "selected_project_library", text="")
|
||||
@@ -403,7 +403,7 @@ class BIM_PT_project_library(Panel):
|
||||
if library_is_selected and not props.is_editing_project_library:
|
||||
row.prop(props, "is_editing_project_library", text="", icon="GREASEPENCIL")
|
||||
|
||||
row.prop(self.props, "filter_by_library", text="", icon="FILTER")
|
||||
row.prop(self.props, "show_library_tree", text="", icon="OUTLINER")
|
||||
|
||||
if props.is_editing_project_library:
|
||||
row = layout.row(align=True)
|
||||
@@ -420,8 +420,9 @@ class BIM_PT_project_library(Panel):
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=self.props.active_library_element or "Top Level Assets")
|
||||
if self.props.active_library_element:
|
||||
active_library_element = self.props.get_active_library_breadcrumb()
|
||||
row.label(text=(active_library_element.name if active_library_element else "Top Level Assets"))
|
||||
if active_library_element:
|
||||
row.operator("bim.rewind_library", icon="FRAME_PREV", text="")
|
||||
row.operator("bim.refresh_library", icon="FILE_REFRESH", text="")
|
||||
self.layout.template_list(
|
||||
@@ -511,9 +512,11 @@ class BIM_UL_library(UIList):
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
if not item.ifc_definition_id:
|
||||
if item.element_type != "ASSET" and item.asset_count > 0:
|
||||
op = row.operator("bim.change_library_element", text="", icon="DISCLOSURE_TRI_RIGHT", emboss=False)
|
||||
op.element_name = item.name
|
||||
op.breadcrumb_type = item.element_type
|
||||
op.library_id = item.ifc_definition_id
|
||||
row.label(text=item.name)
|
||||
if item.ifc_definition_id and item.is_declarable:
|
||||
if item.is_declared:
|
||||
@@ -522,7 +525,7 @@ class BIM_UL_library(UIList):
|
||||
else:
|
||||
op = row.operator("bim.assign_library_declaration", text="", icon="KEYFRAME", emboss=False)
|
||||
op.definition = item.ifc_definition_id
|
||||
if item.ifc_definition_id:
|
||||
if item.element_type == "ASSET":
|
||||
if item.is_appended:
|
||||
row.label(text="", icon="CHECKMARK")
|
||||
else:
|
||||
|
||||
@@ -31,6 +31,7 @@ import bonsai.core.unit
|
||||
import bonsai.core.owner
|
||||
import bonsai.bim.schema
|
||||
import bonsai.tool as tool
|
||||
from collections import defaultdict
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from ifcopenshell.api.project.append_asset import APPENDABLE_ASSET_TYPES
|
||||
from pathlib import Path
|
||||
@@ -39,6 +40,8 @@ from typing import Optional, Union, TYPE_CHECKING, Generator, Callable
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.project.prop import BIMProjectProperties
|
||||
|
||||
HiearchyDict = dict[ifcopenshell.entity_instance, "HiearchyDict"]
|
||||
|
||||
|
||||
class Project(bonsai.core.tool.Project):
|
||||
@classmethod
|
||||
@@ -296,54 +299,43 @@ class Project(bonsai.core.tool.Project):
|
||||
ifcopenshell.api.document.remove_reference(ifc_file, reference)
|
||||
|
||||
@classmethod
|
||||
def get_filter_for_active_library(
|
||||
def get_project_library_elements(
|
||||
cls, project_library: ifcopenshell.entity_instance
|
||||
) -> set[ifcopenshell.entity_instance]:
|
||||
return set(element for rel in project_library.Declares for element in rel.RelatedDefinitions)
|
||||
|
||||
@classmethod
|
||||
def get_project_library_rels(cls, ifc_file: ifcopenshell.file) -> set[ifcopenshell.entity_instance]:
|
||||
return set(rel for lib in ifc_file.by_type("IfcProjectLibrary") for rel in lib.Declares)
|
||||
|
||||
@classmethod
|
||||
def is_element_assigned_to_project_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
|
||||
|
||||
selected_project_library = props.selected_project_library if props.filter_by_library else "*"
|
||||
|
||||
if selected_project_library == "*":
|
||||
|
||||
def condition(elements: list[ifcopenshell.entity_instance]):
|
||||
yield from elements
|
||||
|
||||
elif 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(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]):
|
||||
# Hacky way to create empty generator.
|
||||
return
|
||||
yield
|
||||
|
||||
return condition
|
||||
element: ifcopenshell.entity_instance,
|
||||
project_library_rels: set[ifcopenshell.entity_instance],
|
||||
) -> bool:
|
||||
if not (has_context := getattr(element, "HasContext", ())):
|
||||
return False
|
||||
return any(rel in project_library_rels for rel in has_context)
|
||||
|
||||
@classmethod
|
||||
def update_current_library_page(cls):
|
||||
props = cls.get_project_props()
|
||||
element_name = props.active_library_element
|
||||
active_library_breadcrumb = props.get_active_library_breadcrumb()
|
||||
change_back = False
|
||||
if active_library_breadcrumb:
|
||||
name = active_library_breadcrumb.name
|
||||
breadcrumb_type = active_library_breadcrumb.breadcrumb_type
|
||||
library_id = active_library_breadcrumb.library_id
|
||||
change_back = True
|
||||
|
||||
bpy.ops.bim.rewind_library()
|
||||
bpy.ops.bim.change_library_element(element_name=element_name)
|
||||
if change_back:
|
||||
bpy.ops.bim.change_library_element(
|
||||
element_name=name,
|
||||
breadcrumb_type=breadcrumb_type,
|
||||
library_id=library_id,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_parent_library(cls, project_library: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
@@ -352,3 +344,34 @@ class Project(bonsai.core.tool.Project):
|
||||
return nests[0].RelatingObject
|
||||
# IfcProject.
|
||||
return project_library.HasContext[0].RelatingContext
|
||||
|
||||
@classmethod
|
||||
def get_project_hierarchy(cls, ifc_file: ifcopenshell.file) -> HiearchyDict:
|
||||
"""Get project hierarchy in the following form:
|
||||
|
||||
{
|
||||
IfcProject: { IfcProjectLibrary A: { ... }, },
|
||||
IfcProjectLibrary A: { IfcProjectLibrary B: { ... } },
|
||||
IfcProjectLibrary B: { ... },
|
||||
}
|
||||
|
||||
Use IfcProject to get hierarchy root.
|
||||
|
||||
"""
|
||||
hierarchy: HiearchyDict = defaultdict(dict)
|
||||
for project_library in ifc_file.by_type("IfcProjectLibrary"):
|
||||
parent_library = cls.get_parent_library(project_library)
|
||||
hierarchy[parent_library][project_library] = hierarchy[project_library]
|
||||
return hierarchy
|
||||
|
||||
@classmethod
|
||||
def load_project_libraries_to_ui(
|
||||
cls, parent_library: ifcopenshell.entity_instance, hierarchy: HiearchyDict
|
||||
) -> None:
|
||||
libraries = hierarchy[parent_library]
|
||||
props = cls.get_project_props()
|
||||
for project_library in libraries:
|
||||
library_elements = tool.Project.get_project_library_elements(project_library)
|
||||
props.add_library_project_library(
|
||||
project_library.Name or "Unnamed", len(library_elements), project_library.id()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user