diff --git a/src/blenderbim/blenderbim/bim/module/spatial/__init__.py b/src/blenderbim/blenderbim/bim/module/spatial/__init__.py index f48afcfa3a..3aab40cbfc 100644 --- a/src/blenderbim/blenderbim/bim/module/spatial/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/spatial/__init__.py @@ -22,23 +22,23 @@ from . import ui, prop, operator, workspace classes = ( operator.AssignContainer, operator.ChangeSpatialLevel, + operator.ContractContainer, operator.CopyToContainer, + operator.DeleteContainer, operator.DereferenceStructure, operator.DisableEditingContainer, + operator.EditContainerAttributes, operator.EnableEditingContainer, + operator.ExpandContainer, + operator.LoadContainerManager, operator.ReferenceStructure, operator.RemoveContainer, operator.SelectContainer, - operator.SelectSimilarContainer, - operator.SelectProduct, - operator.LoadContainerManager, - operator.EditContainerAttributes, - operator.AddBuildingStorey, - operator.ContractContainer, - operator.ExpandContainer, - operator.DeleteContainer, operator.SelectDecomposedElements, + operator.SelectProduct, + operator.SelectSimilarContainer, prop.SpatialElement, + prop.Element, prop.BIMSpatialProperties, prop.BIMObjectSpatialProperties, prop.BIMContainer, @@ -46,6 +46,7 @@ classes = ( ui.BIM_PT_spatial, ui.BIM_UL_containers, ui.BIM_UL_containers_manager, + ui.BIM_UL_elements, ui.BIM_PT_project_tree, workspace.Hotkey, ) diff --git a/src/blenderbim/blenderbim/bim/module/spatial/operator.py b/src/blenderbim/blenderbim/bim/module/spatial/operator.py index b9c3f00202..1441bbc18a 100644 --- a/src/blenderbim/blenderbim/bim/module/spatial/operator.py +++ b/src/blenderbim/blenderbim/bim/module/spatial/operator.py @@ -227,26 +227,6 @@ class DeleteContainer(bpy.types.Operator, tool.Ifc.Operator): def _execute(self, context): core.delete_container(tool.Ifc, tool.Spatial, tool.Geometry, container=tool.Ifc.get().by_id(self.container)) -class AddBuildingStorey(bpy.types.Operator, tool.Ifc.Operator): - bl_idname = "bim.add_building_storey" - bl_label = "Add Storey" - bl_options = {"REGISTER", "UNDO"} - part_class: bpy.props.StringProperty() - - def _execute(self, context): - active_container = tool.Spatial.get_active_container() - obj = tool.Ifc.get_object(active_container) - blenderbim.core.aggregate.add_part_to_object( - tool.Ifc, - tool.Aggregate, - tool.Collector, - tool.Blender, - obj=obj, - part_class=self.part_class, - part_name="Unnamed", - ) - core.load_container_manager(tool.Spatial) - class SelectDecomposedElements(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.select_decomposed_elements" diff --git a/src/blenderbim/blenderbim/bim/module/spatial/prop.py b/src/blenderbim/blenderbim/bim/module/spatial/prop.py index f6480ae31a..93eb0bfea2 100644 --- a/src/blenderbim/blenderbim/bim/module/spatial/prop.py +++ b/src/blenderbim/blenderbim/bim/module/spatial/prop.py @@ -49,6 +49,10 @@ def update_name(self, context): tool.Spatial.edit_container_name(tool.Ifc.get().by_id(ifc_definition_id), self.name) +def update_active_container_index(self, context): + tool.Spatial.load_contained_elements() + + def update_relating_container_from_object(self, context): if self.relating_container_object is None or context.active_object is None: return @@ -105,13 +109,23 @@ class BIMContainer(PropertyGroup): ifc_definition_id: IntProperty(name="IFC Definition ID") +class Element(PropertyGroup): + name: StringProperty(name="Name") + is_class: BoolProperty(name="Is Class", default=False) + is_type: BoolProperty(name="Is Type", default=False) + total: IntProperty(name="Total") + + class BIMProjectTreeProperties(PropertyGroup): containers: CollectionProperty(name="Containers", type=BIMContainer) contracted_containers: StringProperty(name="Contracted containers", default="[]") expanded_containers: StringProperty(name="Expanded containers", default="[]") - active_container_index: IntProperty(name="Active Container Index") + active_container_index: IntProperty(name="Active Container Index", update=update_active_container_index) + elements: CollectionProperty(name="Elements", type=Element) + active_element_index: IntProperty(name="Active Element Index") + total_elements: IntProperty(name="Total Elements") @property def active_container(self): - if self.active_container_index < len(self.containers): + if self.containers and self.active_container_index < len(self.containers): return self.containers[self.active_container_index] diff --git a/src/blenderbim/blenderbim/bim/module/spatial/ui.py b/src/blenderbim/blenderbim/bim/module/spatial/ui.py index 32fbc6cee9..1a32843a7b 100644 --- a/src/blenderbim/blenderbim/bim/module/spatial/ui.py +++ b/src/blenderbim/blenderbim/bim/module/spatial/ui.py @@ -102,23 +102,30 @@ class BIM_PT_project_tree(Panel): @classmethod def poll(cls, context): - return tool.Ifc.get() and tool.Ifc.schema().name() != "IFC2X3" + return tool.Ifc.get() def draw(self, context): if not ProjectTreeData.is_loaded: ProjectTreeData.load() self.props = context.scene.BIMProjectTreeProperties - row = self.layout.row() - row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="Load Spatial Structure") + if self.props.active_container: - ifc_definition_id = self.props.containers[self.props.active_container_index].ifc_definition_id row = self.layout.row(align=True) - row.alignment = "RIGHT" - row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="") - spatial_data = ProjectTreeData.data["containers"].get(ifc_definition_id, None) - if spatial_data and spatial_data["type"] in ["IfcBuildingStorey", "IfcBuilding"]: - row.operator("bim.add_building_storey", icon="ADD", text="Add storey").part_class = "IfcBuildingStorey" - row.operator("bim.delete_container", icon="X", text="").container = ifc_definition_id + row.label( + text=f"Active {self.props.active_container.ifc_class}: {self.props.active_container.name}", + icon="OUTLINER_COLLECTION", + ) + row.operator("bim.select_decomposed_elements", icon="HIDE_OFF", text="") + row.operator("bim.delete_container", icon="X", text="").container = ( + self.props.active_container.ifc_definition_id + ) + row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="") + + else: + row = self.layout.row(align=True) + row.label(text="Warning: No Active Container", icon="ERROR") + row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="") + self.layout.template_list( "BIM_UL_containers_manager", "", @@ -126,6 +133,32 @@ class BIM_PT_project_tree(Panel): "containers", self.props, "active_container_index", + rows=10, + ) + + if not self.props.active_container: + return + + # spatial_data = ProjectTreeData.data["containers"].get(ifc_definition_id, None) + # if spatial_data and spatial_data["type"] in ["IfcBuildingStorey", "IfcBuilding"]: + # row.operator("bim.add_building_storey", icon="ADD", text="Add storey").part_class = "IfcBuildingStorey" + + if not self.props.total_elements: + row = self.layout.row() + row.label(text="No Contained Elements", icon="FILE_3D") + return + + row = self.layout.row(align=True) + row.label(text=f"{self.props.total_elements} Contained Elements", icon="FILE_3D") + row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="") + + self.layout.template_list( + "BIM_UL_elements", + "", + self.props, + "elements", + self.props, + "active_element_index", ) @@ -134,11 +167,11 @@ class BIM_UL_containers_manager(UIList): if item: row = layout.row(align=True) self.draw_hierarchy(row, item) - split1 = row.split(factor=0.7) - split1.prop(item, "name", emboss=False, text="") - split2 = row.split(factor=1) - split2.alignment = "RIGHT" - split2.prop(item, "elevation", emboss=False, text="") + row.prop(item, "name", emboss=False, text="") + row.prop(item, "long_name", emboss=False, text="") + col = row.column() + col.alignment = "RIGHT" + col.prop(item, "elevation", emboss=False, text="") def draw_hierarchy(self, row, item): for i in range(0, item.level_index): @@ -164,3 +197,22 @@ class BIM_UL_containers_manager(UIList): row.label(text="", icon="ANTIALIASED") else: row.label(text="", icon="META_PLANE") + + +class BIM_UL_elements(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + if item: + row = layout.row(align=True) + if item.is_class: + row.label(text="", icon="DISCLOSURE_TRI_DOWN") + row.label(text=item.name) + col = row.column() + col.alignment = "RIGHT" + 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" + col.label(text=str(item.total)) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 31ddcb2eaa..f995b81e49 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -831,7 +831,7 @@ class Spatial: def can_reference(cls, structure, element): pass def contract_container(cls, container): pass def copy_xy(cls, src_obj, destination_obj): pass - def create_new_storey_li(cls, element, level_index): pass + def import_spatial_structure(cls, element, level_index): pass def deselect_objects(cls): pass def disable_editing(cls, obj): pass def duplicate_object_and_data(cls, obj): pass diff --git a/src/blenderbim/blenderbim/tool/spatial.py b/src/blenderbim/blenderbim/tool/spatial.py index 19ffce1230..23d1076d21 100644 --- a/src/blenderbim/blenderbim/tool/spatial.py +++ b/src/blenderbim/blenderbim/tool/spatial.py @@ -206,6 +206,40 @@ class Spatial(blenderbim.core.tool.Spatial): z = src_obj.location[2] src_obj.location = (destination_obj.location[0], destination_obj.location[1], z) + @classmethod + def load_contained_elements(cls): + props = bpy.context.scene.BIMProjectTreeProperties + props.elements.clear() + if not (container := props.active_container): + return + + container = tool.Ifc.get().by_id(container.ifc_definition_id) + + results = {} + for element in ifcopenshell.util.element.get_contained(container): + element_type = ifcopenshell.util.element.get_type(element) + ifc_class = element.is_a() + 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 + + total_elements = 0 + for ifc_class in sorted(results.keys()): + new = props.elements.add() + new.name = ifc_class + new.is_class = True + total = 0 + for type_name in sorted(results[ifc_class].keys()): + new2 = props.elements.add() + new2.name = type_name + new2.is_type = True + new2.total = results[ifc_class][type_name] + total += new2.total + new.total = total + total_elements += total + + props.total_elements = total_elements + @classmethod def load_container_manager(cls): props = bpy.context.scene.BIMProjectTreeProperties