From f3b541f00a184238f552471ece59fe7eb285ea34 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 20 Oct 2023 17:51:30 +1100 Subject: [PATCH] Fix #3095. Very basic implementation of a new zones panel. --- src/blenderbim/blenderbim/bim/__init__.py | 1 + .../blenderbim/bim/module/system/__init__.py | 16 +++- .../blenderbim/bim/module/system/data.py | 33 ++++++- .../blenderbim/bim/module/system/operator.py | 95 +++++++++++++++++++ .../blenderbim/bim/module/system/prop.py | 13 +++ .../blenderbim/bim/module/system/ui.py | 89 ++++++++++++++++- src/blenderbim/blenderbim/bim/ui.py | 14 +++ 7 files changed, 253 insertions(+), 8 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/__init__.py b/src/blenderbim/blenderbim/bim/__init__.py index 9cc36d3668..62f444208e 100644 --- a/src/blenderbim/blenderbim/bim/__init__.py +++ b/src/blenderbim/blenderbim/bim/__init__.py @@ -150,6 +150,7 @@ classes = [ # Services and systems ui.BIM_PT_tab_services, ui.BIM_PT_tab_services_object, + ui.BIM_PT_tab_zones, # Structural analysis ui.BIM_PT_tab_structural, # Construction scheduling diff --git a/src/blenderbim/blenderbim/bim/module/system/__init__.py b/src/blenderbim/blenderbim/bim/module/system/__init__.py index ea95b8965e..bb264dc9b8 100644 --- a/src/blenderbim/blenderbim/bim/module/system/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/system/__init__.py @@ -22,38 +22,52 @@ from . import ui, prop, operator, decorator classes = ( operator.AddPort, operator.AddSystem, + operator.AddZone, operator.AssignSystem, operator.ConnectPort, operator.DisableEditingSystem, + operator.DisableEditingZone, operator.DisableSystemEditingUI, operator.DisconnectPort, operator.EditSystem, + operator.EditZone, operator.EnableEditingSystem, + operator.EnableEditingZone, operator.HidePorts, operator.LoadSystems, + operator.LoadZones, + operator.MEPConnectElements, operator.RemovePort, operator.RemoveSystem, + operator.RemoveZone, operator.SelectSystemProducts, - operator.MEPConnectElements, operator.SetFlowDirection, operator.ShowPorts, operator.UnassignSystem, + operator.UnloadZones, prop.System, + prop.Zone, prop.BIMSystemProperties, + prop.BIMZoneProperties, ui.BIM_PT_systems, ui.BIM_PT_object_systems, + ui.BIM_PT_zones, + ui.BIM_PT_active_object_zones, ui.BIM_PT_ports, ui.BIM_PT_port, ui.BIM_UL_systems, ui.BIM_UL_object_systems, + ui.BIM_UL_zones, ) def register(): bpy.types.Scene.BIMSystemProperties = bpy.props.PointerProperty(type=prop.BIMSystemProperties) + bpy.types.Scene.BIMZoneProperties = bpy.props.PointerProperty(type=prop.BIMZoneProperties) bpy.app.handlers.load_post.append(decorator.toggle_decorations_on_load) def unregister(): del bpy.types.Scene.BIMSystemProperties + del bpy.types.Scene.BIMZoneProperties bpy.app.handlers.load_post.remove(decorator.toggle_decorations_on_load) diff --git a/src/blenderbim/blenderbim/bim/module/system/data.py b/src/blenderbim/blenderbim/bim/module/system/data.py index 48f7e88471..bc88b4c22f 100644 --- a/src/blenderbim/blenderbim/bim/module/system/data.py +++ b/src/blenderbim/blenderbim/bim/module/system/data.py @@ -25,6 +25,8 @@ import blenderbim.tool as tool def refresh(): SystemData.is_loaded = False + ZonesData.is_loaded = False + ActiveObjectZonesData.is_loaded = False ObjectSystemData.is_loaded = False PortData.is_loaded = False SystemDecorationData.is_loaded = False @@ -47,8 +49,6 @@ class SystemData: declaration = tool.Ifc.schema().declaration_by_name("IfcSystem") declarations = ifcopenshell.util.schema.get_subtypes(declaration) version = tool.Ifc.get_schema() - if version == "IFC2X3": - declarations += [tool.Ifc.schema().declaration_by_name("IfcZone")] # We're only interested in systems for services. Not sure why IFC groups these together. return [ @@ -193,3 +193,32 @@ class SystemDecorationData: ports_data.append(port_data) cls.elements_ports_positions[element] = ports_data return cls.elements_ports_positions[element] + + +class ZonesData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { "total_zones": cls.total_zones() } + cls.is_loaded = True + + @classmethod + def total_zones(cls): + return len(tool.Ifc.get().by_type("IfcZone")) + + +class ActiveObjectZonesData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { "zones": cls.zones() } + cls.is_loaded = True + + @classmethod + def zones(cls): + systems = ifcopenshell.util.system.get_element_systems(tool.Ifc.get_entity(bpy.context.active_object)) + return [s.Name or "Unnamed" for s in systems if s.is_a("IfcZone")] diff --git a/src/blenderbim/blenderbim/bim/module/system/operator.py b/src/blenderbim/blenderbim/bim/module/system/operator.py index 4d40d9c36d..8f1671458f 100644 --- a/src/blenderbim/blenderbim/bim/module/system/operator.py +++ b/src/blenderbim/blenderbim/bim/module/system/operator.py @@ -21,6 +21,7 @@ import ifcopenshell.api import blenderbim.tool as tool import blenderbim.core.system as core import blenderbim.bim.handler +import blenderbim.bim.helper from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.system.data import PortData from mathutils import Matrix @@ -326,3 +327,97 @@ class SetFlowDirection(bpy.types.Operator, Operator): self.report({"ERROR"}, "Selected elements are not connected to set the flow direction") return {"CANCELLED"} + + +class LoadZones(bpy.types.Operator, Operator): + bl_idname = "bim.load_zones" + bl_label = "Load Zones" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + props = bpy.context.scene.BIMZoneProperties + props.zones.clear() + for zone in tool.Ifc.get().by_type("IfcZone"): + new = props.zones.add() + new.ifc_definition_id = zone.id() + new.name = zone.Name or "Unnamed" + props.is_loaded = True + props.is_editing = 0 + + +class UnloadZones(bpy.types.Operator, Operator): + bl_idname = "bim.unload_zones" + bl_label = "Unload Zones" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + props = bpy.context.scene.BIMZoneProperties + props.is_loaded = False + + +class AddZone(bpy.types.Operator, Operator): + bl_idname = "bim.add_zone" + bl_label = "Add Zone" + bl_options = {"REGISTER", "UNDO"} + name: bpy.props.StringProperty() + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + row = self.layout + row.prop(self, "name", text="Name") + + def _execute(self, context): + element = ifcopenshell.api.run("system.add_system", tool.Ifc.get(), ifc_class="IfcZone") + if self.name: + element.Name = self.name + bpy.ops.bim.load_zones() + + +class EnableEditingZone(bpy.types.Operator, Operator): + bl_idname = "bim.enable_editing_zone" + bl_label = "Enable Editing Zone" + bl_options = {"REGISTER", "UNDO"} + zone: bpy.props.IntProperty() + + def _execute(self, context): + props = bpy.context.scene.BIMZoneProperties + props.attributes.clear() + blenderbim.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.zone), props.attributes) + props.is_editing = self.zone + + +class DisableEditingZone(bpy.types.Operator, Operator): + bl_idname = "bim.disable_editing_zone" + bl_label = "Disable Editing Zone" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + props = bpy.context.scene.BIMZoneProperties + props.is_editing = 0 + + +class EditZone(bpy.types.Operator, Operator): + bl_idname = "bim.edit_zone" + bl_label = "Edit Zone" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + props = bpy.context.scene.BIMZoneProperties + zone = tool.Ifc.get().by_id(props.is_editing) + attributes = blenderbim.bim.helper.export_attributes(props.attributes) + ifcopenshell.api.run("system.edit_system", tool.Ifc.get(), system=zone, attributes=attributes) + props.is_editing = 0 + bpy.ops.bim.load_zones() + + +class RemoveZone(bpy.types.Operator, Operator): + bl_idname = "bim.remove_zone" + bl_label = "Remove Zone" + bl_options = {"REGISTER", "UNDO"} + zone: bpy.props.IntProperty() + + def _execute(self, context): + ifcopenshell.api.run("system.remove_system", tool.Ifc.get(), system=tool.Ifc.get().by_id(self.zone)) + bpy.ops.bim.load_zones() diff --git a/src/blenderbim/blenderbim/bim/module/system/prop.py b/src/blenderbim/blenderbim/bim/module/system/prop.py index f1ffa3263c..3b79421116 100644 --- a/src/blenderbim/blenderbim/bim/module/system/prop.py +++ b/src/blenderbim/blenderbim/bim/module/system/prop.py @@ -45,6 +45,11 @@ class System(PropertyGroup): ifc_definition_id: IntProperty(name="IFC Definition ID") +class Zone(PropertyGroup): + name: StringProperty(name="Name") + ifc_definition_id: IntProperty(name="IFC Definition ID") + + def toggle_decorations(self, context): toggle = self.should_draw_decorations if toggle: @@ -64,3 +69,11 @@ class BIMSystemProperties(PropertyGroup): should_draw_decorations: BoolProperty( name="Should Draw Decorations", description="Toggle system decorations", update=toggle_decorations ) + + +class BIMZoneProperties(PropertyGroup): + attributes: CollectionProperty(name="Attributes", type=Attribute) + is_loaded: BoolProperty(name="Is Loaded", default=False) + is_editing: IntProperty(name="Is Editing", default=0) + zones: CollectionProperty(name="Zones", type=Zone) + active_zone_index: IntProperty(name="Active Zone Index") diff --git a/src/blenderbim/blenderbim/bim/module/system/ui.py b/src/blenderbim/blenderbim/bim/module/system/ui.py index c70438292f..8d66c7d2a5 100644 --- a/src/blenderbim/blenderbim/bim/module/system/ui.py +++ b/src/blenderbim/blenderbim/bim/module/system/ui.py @@ -16,11 +16,11 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . -from blenderbim.bim.helper import prop_with_search +import blenderbim.bim.helper import blenderbim.tool as tool +from blenderbim.bim.helper import prop_with_search from bpy.types import Panel, UIList -from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.module.system.data import SystemData, ObjectSystemData, PortData +from blenderbim.bim.module.system.data import SystemData, ZonesData, ActiveObjectZonesData, ObjectSystemData, PortData FLOW_DIRECTION_TO_ICON = { @@ -42,7 +42,7 @@ class BIM_PT_systems(Panel): @classmethod def poll(cls, context): - return IfcStore.get_file() + return tool.Ifc.get() def draw(self, context): if not SystemData.is_loaded: @@ -95,7 +95,7 @@ class BIM_PT_object_systems(Panel): def poll(cls, context): if not context.active_object: return False - return IfcStore.get_file() and context.active_object.BIMObjectProperties.ifc_definition_id + return tool.Ifc.get() and context.active_object.BIMObjectProperties.ifc_definition_id def draw(self, context): if not ObjectSystemData.is_loaded: @@ -294,6 +294,78 @@ class BIM_PT_port(Panel): ).direction = flow_direction +class BIM_PT_zones(Panel): + bl_label = "Zones" + bl_idname = "BIM_PT_zones" + bl_options = {"HIDE_HEADER"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + bl_parent_id = "BIM_PT_tab_zones" + + @classmethod + def poll(cls, context): + return tool.Ifc.get() + + def draw(self, context): + if not ZonesData.is_loaded: + ZonesData.load() + self.props = context.scene.BIMZoneProperties + + row = self.layout.row(align=True) + row.label(text="{} Zones Found".format(ZonesData.data["total_zones"]), icon="SEQ_STRIP_META") + if not self.props.is_loaded: + row.operator("bim.load_zones", text="", icon="GREASEPENCIL") + return + + row.operator("bim.add_zone", text="", icon="ADD") + row.operator("bim.unload_zones", text="", icon="CANCEL") + + if self.props.zones and self.props.active_zone_index < len(self.props.zones): + row = self.layout.row(align=True) + ifc_definition_id = self.props.zones[self.props.active_zone_index].ifc_definition_id + row.operator("bim.enable_editing_zone", text="Edit Zone", icon="GREASEPENCIL").zone = ifc_definition_id + row.operator("bim.select_system_products", text="", icon="RESTRICT_SELECT_OFF").system = ifc_definition_id + row.operator("bim.assign_system", text="", icon="KEYFRAME_HLT").system = ifc_definition_id + row.operator("bim.unassign_system", text="", icon="KEYFRAME").system = ifc_definition_id + row.operator("bim.remove_zone", text="", icon="X").zone = ifc_definition_id + + self.layout.template_list("BIM_UL_zones", "", self.props, "zones", self.props, "active_zone_index") + + if self.props.is_editing: + blenderbim.bim.helper.draw_attributes(self.props.attributes, self.layout) + row = self.layout.row(align=True) + row.operator("bim.edit_zone", icon="CHECKMARK") + row.operator("bim.disable_editing_zone", icon="CANCEL", text="") + + +class BIM_PT_active_object_zones(Panel): + bl_label = "Active Object Zones" + bl_idname = "BIM_PT_active_object_zones" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + bl_parent_id = "BIM_PT_tab_zones" + + @classmethod + def poll(cls, context): + return tool.Ifc.get() and context.active_object and tool.Ifc.get_entity(context.active_object) + + + def draw(self, context): + if not ActiveObjectZonesData.is_loaded: + ActiveObjectZonesData.load() + self.props = context.scene.BIMZoneProperties + + for zone in ActiveObjectZonesData.data["zones"]: + row = self.layout.row() + row.label(text=zone, icon="SEQ_STRIP_META") + + if not ActiveObjectZonesData.data["zones"]: + row = self.layout.row() + row.label(text="Active Object Has No Zones") + + class BIM_UL_systems(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): system_icons = { @@ -341,3 +413,10 @@ class BIM_UL_object_systems(UIList): row = layout.row(align=True) row.label(text=item.name, icon=system_icons[item.ifc_class]) row.operator("bim.assign_system", text="", icon="ADD").system = item.ifc_definition_id + + +class BIM_UL_zones(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + if item: + row = layout.row(align=True) + row.label(text=item.name) diff --git a/src/blenderbim/blenderbim/bim/ui.py b/src/blenderbim/blenderbim/bim/ui.py index 53fb4dcb71..29b120db5a 100644 --- a/src/blenderbim/blenderbim/bim/ui.py +++ b/src/blenderbim/blenderbim/bim/ui.py @@ -495,6 +495,20 @@ class BIM_PT_tab_services(Panel): pass +class BIM_PT_tab_zones(Panel): + bl_label = "Zones" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + + @classmethod + def poll(cls, context): + return tool.Blender.is_tab(context, "SERVICES") and tool.Ifc.get() + + def draw(self, context): + pass + + class BIM_PT_tab_quality_control(Panel): bl_label = "Quality Control" bl_space_type = "PROPERTIES"