mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Implement filtered selection in new spatial decomposition panel
This commit is contained in:
@@ -237,15 +237,36 @@ class SelectDecomposedElements(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_decomposed_elements"
|
||||
bl_label = "Select Children"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
should_filter: bpy.props.BoolProperty(name="Should Filter", default=True, options={"SKIP_SAVE"})
|
||||
container: bpy.props.IntProperty()
|
||||
ifc_class: bpy.props.StringProperty()
|
||||
relating_type: bpy.props.IntProperty()
|
||||
|
||||
@classmethod
|
||||
def description(cls, context, operator):
|
||||
return f"Select all elements contained in this {operator.ifc_class}"
|
||||
return "Select all contained elements filtered by this type" + "\nALT+CLICK to select all contained elements"
|
||||
|
||||
def invoke(self, context, event):
|
||||
if event.type == "LEFTMOUSE" and event.alt:
|
||||
self.should_filter = False
|
||||
return self.execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
core.select_decomposed_elements(tool.Spatial, container=tool.Ifc.get().by_id(self.container))
|
||||
if self.should_filter:
|
||||
ifc_class = self.ifc_class
|
||||
relating_type = tool.Ifc.get().by_id(self.relating_type) if self.relating_type else None
|
||||
is_untyped = self.relating_type == 0
|
||||
else:
|
||||
ifc_class = ""
|
||||
relating_type = None
|
||||
is_untyped = False
|
||||
core.select_decomposed_elements(
|
||||
tool.Spatial,
|
||||
container=tool.Ifc.get().by_id(self.container),
|
||||
ifc_class=ifc_class,
|
||||
relating_type=relating_type,
|
||||
is_untyped=is_untyped,
|
||||
)
|
||||
|
||||
|
||||
class SetDefaultContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@@ -123,6 +123,7 @@ class Element(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
is_class: BoolProperty(name="Is Class", default=False)
|
||||
is_type: BoolProperty(name="Is Type", default=False)
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
total: IntProperty(name="Total")
|
||||
|
||||
|
||||
@@ -144,3 +145,8 @@ class BIMSpatialDecompositionProperties(PropertyGroup):
|
||||
def active_container(self):
|
||||
if self.containers and self.active_container_index < len(self.containers):
|
||||
return self.containers[self.active_container_index]
|
||||
|
||||
@property
|
||||
def active_element(self):
|
||||
if self.elements and self.active_element_index < len(self.elements):
|
||||
return self.elements[self.active_element_index]
|
||||
|
||||
@@ -161,15 +161,20 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
|
||||
if not self.props.total_elements:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="No Contained Elements", icon="FILE_3D")
|
||||
row.label(text=f"{self.props.active_container.ifc_class} > No Contained Elements", icon="FILE_3D")
|
||||
row.prop(self.props, "should_include_children", text="", icon="OUTLINER")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{self.props.total_elements} Contained Elements", icon="FILE_3D")
|
||||
row.label(text=f"{self.props.active_container.ifc_class} > {self.props.total_elements} Contained Elements", icon="FILE_3D")
|
||||
row.prop(self.props, "should_include_children", text="", icon="OUTLINER")
|
||||
op = row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.ifc_class = self.props.active_container.ifc_class
|
||||
if self.props.active_element.is_class:
|
||||
op.ifc_class = self.props.active_element.name
|
||||
op.relating_type = 0
|
||||
elif self.props.active_element.is_type:
|
||||
op.ifc_class = ""
|
||||
op.relating_type = self.props.active_element.ifc_definition_id
|
||||
op.container = ifc_definition_id
|
||||
|
||||
self.layout.template_list(
|
||||
@@ -187,7 +192,20 @@ class BIM_UL_containers_manager(UIList):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
self.draw_hierarchy(row, item)
|
||||
row.prop(item, "name", emboss=False, text="")
|
||||
icon = {
|
||||
"IfcProject": "FILE",
|
||||
"IfcSite": "WORLD",
|
||||
"IfcBuilding": "HOME",
|
||||
"IfcBuildingStorey": "LINENUMBERS_OFF",
|
||||
"IfcSpace": "ANTIALIASED",
|
||||
"IfcFacilityPart": "MOD_FLUID",
|
||||
"IfcBridgePart": "MOD_FLUID",
|
||||
"IfcFacilityPartCommon": "MOD_FLUID",
|
||||
"IfcMarinePart": "MOD_FLUID",
|
||||
"IfcRailwayPart": "MOD_FLUID",
|
||||
"IfcRoadPart": "MOD_FLUID",
|
||||
}.get(item.ifc_class, "META_PLANE")
|
||||
row.prop(item, "name", emboss=False, text="", icon=icon)
|
||||
if item.long_name:
|
||||
row.prop(item, "long_name", emboss=False, text="")
|
||||
col = row.column()
|
||||
@@ -208,24 +226,6 @@ class BIM_UL_containers_manager(UIList):
|
||||
)
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.ifc_class == "IfcProject":
|
||||
row.label(text="", icon="FILE")
|
||||
else:
|
||||
icon = {
|
||||
"IfcSite": "WORLD",
|
||||
"IfcBuilding": "HOME",
|
||||
"IfcBuildingStorey": "LINENUMBERS_OFF",
|
||||
"IfcSpace": "ANTIALIASED",
|
||||
"IfcFacilityPart": "MOD_FLUID",
|
||||
"IfcBridgePart": "MOD_FLUID",
|
||||
"IfcFacilityPartCommon": "MOD_FLUID",
|
||||
"IfcMarinePart": "MOD_FLUID",
|
||||
"IfcRailwayPart": "MOD_FLUID",
|
||||
"IfcRoadPart": "MOD_FLUID",
|
||||
}.get(item.ifc_class, "META_PLANE")
|
||||
op = row.operator("bim.select_decomposed_elements", icon=icon, text="", emboss=False)
|
||||
op.ifc_class = item.ifc_class
|
||||
op.container = item.ifc_definition_id
|
||||
|
||||
|
||||
class BIM_UL_elements(UIList):
|
||||
@@ -240,7 +240,6 @@ class BIM_UL_elements(UIList):
|
||||
col.label(text=str(item.total))
|
||||
elif item.is_type:
|
||||
row.label(text="", icon="BLANK1")
|
||||
row.label(text="", icon="DISCLOSURE_TRI_DOWN")
|
||||
row.label(text=item.name)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
|
||||
@@ -149,8 +149,21 @@ def delete_container(
|
||||
spatial.import_spatial_decomposition()
|
||||
|
||||
|
||||
def select_decomposed_elements(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
|
||||
spatial.select_products(spatial.get_decomposed_elements(container))
|
||||
def select_decomposed_elements(
|
||||
spatial: tool.Spatial,
|
||||
container: ifcopenshell.entity_instance,
|
||||
ifc_class: str | None = None,
|
||||
relating_type: ifcopenshell.entity_instance | None = None,
|
||||
is_untyped: bool = False,
|
||||
) -> None:
|
||||
elements = spatial.get_decomposed_elements(container)
|
||||
if ifc_class:
|
||||
elements = spatial.filter_elements_by_class(elements, ifc_class)
|
||||
elif relating_type:
|
||||
elements = spatial.filter_elements_by_relating_type(elements, relating_type)
|
||||
elif is_untyped:
|
||||
elements = spatial.filter_elements_by_untyped(elements)
|
||||
spatial.select_products(elements)
|
||||
|
||||
|
||||
def generate_space(ifc: tool.Ifc, model: tool.Model, root: tool.Root, spatial: tool.Spatial, type: tool.Type) -> None:
|
||||
|
||||
@@ -39,7 +39,7 @@ import json
|
||||
from math import pi
|
||||
from mathutils import Vector, Matrix
|
||||
from shapely import Polygon
|
||||
from typing import Generator, Optional, Union, Literal, Any
|
||||
from typing import Generator, Optional, Union, Literal, Any, List
|
||||
|
||||
|
||||
class Spatial(blenderbim.core.tool.Spatial):
|
||||
@@ -244,9 +244,10 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
continue
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
ifc_class = element.is_a()
|
||||
ifc_definition_id = element_type.id() if element_type else 0
|
||||
type_name = element_type.Name or "Unnamed" if element_type else "Untyped"
|
||||
results.setdefault(ifc_class, {}).setdefault(type_name, 0)
|
||||
results[ifc_class][type_name] += 1
|
||||
results.setdefault(ifc_class, {}).setdefault(ifc_definition_id, {"total": 0, "type_name": type_name})
|
||||
results[ifc_class][ifc_definition_id]["total"] += 1
|
||||
|
||||
total_elements = 0
|
||||
for ifc_class in sorted(results.keys()):
|
||||
@@ -254,17 +255,34 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
new.name = ifc_class
|
||||
new.is_class = True
|
||||
total = 0
|
||||
for type_name in sorted(results[ifc_class].keys()):
|
||||
for ifc_definition_id in sorted(
|
||||
results[ifc_class].keys(), key=lambda x: results[ifc_class][x]["type_name"]
|
||||
):
|
||||
new2 = props.elements.add()
|
||||
new2.name = type_name
|
||||
new2.is_type = True
|
||||
new2.total = results[ifc_class][type_name]
|
||||
new2.name = results[ifc_class][ifc_definition_id]["type_name"]
|
||||
new2.total = results[ifc_class][ifc_definition_id]["total"]
|
||||
new2.ifc_definition_id = ifc_definition_id
|
||||
total += new2.total
|
||||
new.total = total
|
||||
total_elements += total
|
||||
|
||||
props.total_elements = total_elements
|
||||
|
||||
@classmethod
|
||||
def filter_elements_by_class(cls, elements: List[ifcopenshell.entity_instance], ifc_class: str):
|
||||
return [e for e in elements if e.is_a(ifc_class)]
|
||||
|
||||
@classmethod
|
||||
def filter_elements_by_relating_type(
|
||||
cls, elements: List[ifcopenshell.entity_instance], relating_type: ifcopenshell.entity_instance
|
||||
):
|
||||
return [e for e in elements if ifcopenshell.util.element.get_type(e) == relating_type]
|
||||
|
||||
@classmethod
|
||||
def filter_elements_by_untyped(cls, elements: List[ifcopenshell.entity_instance]):
|
||||
return [e for e in elements if not ifcopenshell.util.element.get_type(e)]
|
||||
|
||||
@classmethod
|
||||
def import_spatial_decomposition(cls) -> None:
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
|
||||
Reference in New Issue
Block a user