mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Fix #5230. Reimplement spatial decomposition hierarchy.
This commit is contained in:
@@ -64,6 +64,10 @@ def update_should_include_children(self, context):
|
||||
tool.Spatial.load_contained_elements()
|
||||
|
||||
|
||||
def update_element_mode(self, context):
|
||||
tool.Spatial.load_contained_elements()
|
||||
|
||||
|
||||
def update_container_obj(self, context):
|
||||
if self.container_obj is None or not (obj := context.active_object):
|
||||
return
|
||||
@@ -112,6 +116,8 @@ class Element(PropertyGroup):
|
||||
name="IFC Definition ID",
|
||||
description="ID of the element type / occurrence. 0 if 'type' is 'CLASS' or if it's 'TYPE' but it represents untyped elements",
|
||||
)
|
||||
level: IntProperty(name="Level", default=0)
|
||||
has_children: BoolProperty(name="Has Children", default=False)
|
||||
total: IntProperty(name="Total")
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=False)
|
||||
type: EnumProperty(
|
||||
@@ -134,6 +140,14 @@ class BIMSpatialDecompositionProperties(PropertyGroup):
|
||||
expanded_elements: StringProperty(name="Expanded Elements", default="{}")
|
||||
active_element_index: IntProperty(name="Active Element Index")
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
element_mode: EnumProperty(
|
||||
name="Element Mode",
|
||||
items=(
|
||||
("TYPE", "Class > Type > Occurrence", "Show a breakdown by IfcClass, relating type, and occurrence"),
|
||||
("DECOMPOSITION", "Element Decomposition", "Show a breakdown by element decomposition"),
|
||||
),
|
||||
update=update_element_mode,
|
||||
)
|
||||
subelement_class: bpy.props.EnumProperty(items=get_subelement_class, name="Subelement Class")
|
||||
default_container: IntProperty(name="Default Container", default=0)
|
||||
should_include_children: BoolProperty(
|
||||
|
||||
@@ -160,8 +160,11 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
if not self.props.total_elements:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{self.props.active_container.ifc_class} > No Elements", icon="FILE_3D")
|
||||
row.prop(self.props, "should_include_children", text="", icon="OUTLINER")
|
||||
row.operator("bim.assign_container", icon="FOLDER_REDIRECT", text="").container = ifc_definition_id
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "element_mode", text="", icon="FILEBROWSER")
|
||||
row.prop(self.props, "should_include_children", text="", icon="OUTLINER")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
@@ -169,7 +172,6 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
text=f"{self.props.active_container.ifc_class} > {self.props.total_elements} Elements",
|
||||
icon="FILE_3D",
|
||||
)
|
||||
row.prop(self.props, "should_include_children", text="", icon="OUTLINER")
|
||||
row.operator("bim.assign_container", icon="FOLDER_REDIRECT", text="").container = ifc_definition_id
|
||||
op = row.operator("bim.select_decomposed_element", icon="OBJECT_DATA", text="")
|
||||
if active_element := self.props.active_element:
|
||||
@@ -179,6 +181,10 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
op = row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.container = ifc_definition_id
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "element_mode", text="", icon="FILEBROWSER")
|
||||
row.prop(self.props, "should_include_children", text="", icon="OUTLINER")
|
||||
|
||||
self.layout.template_list(
|
||||
"BIM_UL_elements",
|
||||
"",
|
||||
@@ -273,23 +279,26 @@ class BIM_UL_elements(UIList):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
item_type = item.type
|
||||
if item_type == "CLASS":
|
||||
for _ in range(item.level):
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.has_children:
|
||||
self.draw_toggle(row, item.is_expanded, index)
|
||||
if item_type == "CLASS":
|
||||
row.label(text=item.name)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.label(text=str(item.total))
|
||||
elif item_type == "TYPE":
|
||||
row.label(text="", icon="BLANK1")
|
||||
self.draw_toggle(row, item.is_expanded, index)
|
||||
row.label(text=item.name)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.label(text=str(item.total))
|
||||
else: # OCCURRENCE
|
||||
for _ in range(2):
|
||||
row.label(text="", icon="BLANK1")
|
||||
elif item_type == "OCCURRENCE":
|
||||
row.label(text=item.name)
|
||||
if item.total:
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.label(text=str(item.total))
|
||||
|
||||
def draw_filter(self, context, layout):
|
||||
row = layout.row()
|
||||
|
||||
@@ -226,6 +226,14 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
return
|
||||
|
||||
container = tool.Ifc.get().by_id(container.ifc_definition_id)
|
||||
if props.element_mode == "TYPE":
|
||||
cls.load_contained_elements_by_type(container)
|
||||
elif props.element_mode == "DECOMPOSITION":
|
||||
cls.load_contained_elements_by_decomposition(container)
|
||||
|
||||
@classmethod
|
||||
def load_contained_elements_by_type(cls, container: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
results = cls.get_grouped_elements_in_container(container)
|
||||
|
||||
expanded_elements = json.loads(props.expanded_elements)
|
||||
@@ -239,6 +247,7 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
new = props.elements.add()
|
||||
new.name = ifc_class
|
||||
new.type = "CLASS"
|
||||
new.has_children = True
|
||||
class_is_expanded = ifc_class in expanded_classes
|
||||
class_is_expanded_r = ifc_class in expanded_classes_r
|
||||
new.is_expanded = class_is_expanded or class_is_expanded_r
|
||||
@@ -251,6 +260,8 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
if new.is_expanded:
|
||||
new2 = props.elements.add()
|
||||
new2.type = "TYPE"
|
||||
new2.has_children = True
|
||||
new2.level = 1
|
||||
new2.name = type_data["type_name"]
|
||||
new2.ifc_class = ifc_class
|
||||
new2.total = total2
|
||||
@@ -265,6 +276,7 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
for element in type_data["elements"]:
|
||||
occurrence = props.elements.add()
|
||||
occurrence.name = element.Name or "Unnamed"
|
||||
occurrence.level = 2
|
||||
occurrence.ifc_definition_id = element.id()
|
||||
occurrence.type = "OCCURRENCE"
|
||||
|
||||
@@ -274,6 +286,35 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
|
||||
props.total_elements = total_elements
|
||||
|
||||
@classmethod
|
||||
def load_contained_elements_by_decomposition(cls, container: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
expanded_elements = json.loads(props.expanded_elements)
|
||||
expanded_ifc_ids = expanded_elements.get("IFC_ID", [])
|
||||
|
||||
def add_elements(elements, level=0):
|
||||
for element in sorted(elements, key=lambda x: f"{x.is_a()}/{x.Name or 'Unnamed'}"):
|
||||
ifc_definition_id = element.id()
|
||||
name = f"{element.is_a()}/{element.Name or 'Unnamed'}"
|
||||
new = props.elements.add()
|
||||
new.name = name
|
||||
new.level = level
|
||||
new.ifc_definition_id = ifc_definition_id
|
||||
new.type = "OCCURRENCE"
|
||||
children = [
|
||||
e
|
||||
for e in ifcopenshell.util.element.get_decomposition(element, is_recursive=False)
|
||||
if not e.is_a("IfcFeatureElement")
|
||||
]
|
||||
if children:
|
||||
new.has_children = True
|
||||
new.total = len(children)
|
||||
if ifc_definition_id in expanded_ifc_ids:
|
||||
add_elements(children, level=level + 1)
|
||||
|
||||
elements = ifcopenshell.util.element.get_decomposition(container, is_recursive=False)
|
||||
add_elements(elements)
|
||||
|
||||
@classmethod
|
||||
def filter_elements(
|
||||
cls,
|
||||
@@ -373,7 +414,14 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
@classmethod
|
||||
def toggle_container_element(cls, element_index: int, is_recursive: bool) -> None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
if props.element_mode == "TYPE":
|
||||
cls.toggle_container_element_by_type(element_index, is_recursive)
|
||||
elif props.element_mode == "DECOMPOSITION":
|
||||
cls.toggle_container_element_by_decomposition(element_index, is_recursive)
|
||||
|
||||
@classmethod
|
||||
def toggle_container_element_by_type(cls, element_index: int, is_recursive: bool) -> None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
expanded_elements: dict[str, list[Union[str, int]]] = json.loads(props.expanded_elements)
|
||||
|
||||
element = props.elements[element_index]
|
||||
@@ -420,6 +468,39 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
|
||||
props.expanded_elements = json.dumps(expanded_elements)
|
||||
|
||||
@classmethod
|
||||
def toggle_container_element_by_decomposition(cls, element_index: int, is_recursive: bool) -> None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
element = props.elements[element_index]
|
||||
expanded_elements: dict[str, list[Union[str, int]]] = json.loads(props.expanded_elements)
|
||||
expanded_elements_list: list[Union[str, int]] = expanded_elements.setdefault("IFC_ID", [])
|
||||
ifc_definition_id = element.ifc_definition_id
|
||||
if ifc_definition_id in expanded_elements_list:
|
||||
expanded_elements_list.remove(ifc_definition_id)
|
||||
should_expand = False
|
||||
else:
|
||||
expanded_elements_list.append(ifc_definition_id)
|
||||
should_expand = True
|
||||
|
||||
if is_recursive:
|
||||
queue = [tool.Ifc.get().by_id(ifc_definition_id)]
|
||||
while queue:
|
||||
element = queue.pop()
|
||||
children = [
|
||||
e
|
||||
for e in ifcopenshell.util.element.get_decomposition(element, is_recursive=False)
|
||||
if not e.is_a("IfcFeatureElement")
|
||||
]
|
||||
ifc_definition_id = element.id()
|
||||
if children:
|
||||
if should_expand is False and ifc_definition_id in expanded_elements_list:
|
||||
expanded_elements_list.remove(ifc_definition_id)
|
||||
elif should_expand is True and ifc_definition_id not in expanded_elements_list:
|
||||
expanded_elements_list.append(ifc_definition_id)
|
||||
queue.extend(children)
|
||||
|
||||
props.expanded_elements = json.dumps(expanded_elements)
|
||||
|
||||
# HERE STARTS SPATIAL TOOL
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user