diff --git a/src/bonsai/bonsai/bim/module/system/operator.py b/src/bonsai/bonsai/bim/module/system/operator.py index e60f4500ec..7f298064fc 100644 --- a/src/bonsai/bonsai/bim/module/system/operator.py +++ b/src/bonsai/bonsai/bim/module/system/operator.py @@ -265,37 +265,31 @@ class ConnectPort(bpy.types.Operator, tool.Ifc.Operator): class AddRelatedPortConnection(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_related_port_connection" bl_label = "Connect Port" - bl_description = "Click to select a port to connect to" bl_options = {"REGISTER", "UNDO"} - - element_id: bpy.props.IntProperty(default=0, options={"SKIP_SAVE"}) - + + relating_port_id: bpy.props.IntProperty() + def invoke(self, context, event): - if self.element_id == 0: - self.report({'ERROR'}, "No port specified") - return {'CANCELLED'} - context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} - - def modal(self, context, event): - if event.type == 'LEFTMOUSE' and event.value == 'PRESS': - if context.active_object: - target_element = tool.Ifc.get_entity(context.active_object) - if target_element and target_element.is_a("IfcDistributionPort"): - source_element = tool.Ifc.get().by_id(self.element_id) - core.connect_port(tool.Ifc, port1=source_element, port2=target_element) - self.report({'INFO'}, "Ports connected") - return {'FINISHED'} - else: - self.report({'WARNING'}, "Selected object is not a port") - return {'RUNNING_MODAL'} - elif event.type in {'RIGHTMOUSE', 'ESC'}: - self.report({'INFO'}, "Cancelled") - return {'CANCELLED'} - return {'RUNNING_MODAL'} - + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + props = tool.System.get_system_props() + self.layout.prop(props, "related_port", text="Select Port") + def _execute(self, context): - return {'CANCELLED'} + props = tool.System.get_system_props() + + if not props.related_port or props.related_port == "NONE": + return {"CANCELLED"} + + port_obj = bpy.data.objects.get(props.related_port) + related_port = tool.Ifc.get_entity(port_obj) + relating_port = tool.Ifc.get().by_id(self.relating_port_id) + + core.connect_port(tool.Ifc, port1=relating_port, port2=related_port) + PortData.is_loaded = False + + return {"FINISHED"} class DisconnectPort(bpy.types.Operator, tool.Ifc.Operator): @@ -454,19 +448,19 @@ class CycleFlowDirection(bpy.types.Operator, tool.Ifc.Operator): return {"CANCELLED"} current_direction = port.FlowDirection or "NOTDEFINED" - flow_cycle = ["SOURCE", "SINK", "SOURCEANDSINK", "NOTDEFINED"] - try: - current_index = flow_cycle.index(current_direction) - next_direction = flow_cycle[(current_index + 1) % len(flow_cycle)] - except ValueError: - next_direction = "SOURCE" + flow_cycle_map = { + "SOURCE": "SINK", + "SINK": "SOURCEANDSINK", + "SOURCEANDSINK": "NOTDEFINED", + "NOTDEFINED": "SOURCE" + } + next_direction = flow_cycle_map.get(current_direction, "SOURCE") tool.Ifc.run("attribute.edit_attributes", product=port, attributes={"FlowDirection": next_direction}) connected_port = tool.System.get_connected_port(port) if connected_port: - # Map flow direction to connected port's complementary direction connected_direction_map = { "SOURCE": "SINK", "SINK": "SOURCE", diff --git a/src/bonsai/bonsai/bim/module/system/prop.py b/src/bonsai/bonsai/bim/module/system/prop.py index 6e9681694b..bb72fda1a9 100644 --- a/src/bonsai/bonsai/bim/module/system/prop.py +++ b/src/bonsai/bonsai/bim/module/system/prop.py @@ -34,7 +34,7 @@ from bpy.props import ( FloatVectorProperty, CollectionProperty, ) -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING, Union, Optional def get_system_class(self: "BIMSystemProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]: @@ -93,51 +93,24 @@ def toggle_decorations(self: "BIMSystemProperties", context: bpy.types.Context) decorator.SystemDecorator.uninstall() -def is_port_available_for_connection(self: "BIMSystemProperties", obj: bpy.types.Object) -> bool: - element = tool.Ifc.get_entity(obj) - if not element or not element.is_a("IfcDistributionPort"): - return False - connected_port = tool.System.get_connected_port(element) - if connected_port is not None: - return False +def get_available_ports_for_connection(self: "BIMSystemProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]: + items = [] + active_object_ports = set(tool.System.get_ports(tool.Ifc.get_entity(context.active_object))) - if bpy.context.active_object: - active_element = tool.Ifc.get_entity(bpy.context.active_object) - if active_element: - active_ports = tool.System.get_ports(active_element) - if element in active_ports: - return False + ifc_file = tool.Ifc.get() + for ifc_port in ifc_file.by_type("IfcDistributionPort"): + port = tool.Ifc.get_object(ifc_port) + if not port: + continue + + if tool.System.get_connected_port(ifc_port) is not None or ifc_port in active_object_ports: + continue + + port_object = tool.Ifc.get_object(tool.System.get_port_relating_element(ifc_port)) + suggestion = f"{port_object.name} > {port.name}" + items.append((port.name, suggestion, "")) - return True - - -def update_related_port_object(self: "BIMSystemProperties", context: bpy.types.Context) -> None: - if self.related_port_object is None: - return - if not context.active_object: - return - - source_element = tool.Ifc.get_entity(context.active_object) - target_element = tool.Ifc.get_entity(self.related_port_object) - - if not source_element or not target_element: - return - - if not target_element.is_a("IfcDistributionPort"): - return - - source_port = None - for port in tool.System.get_ports(source_element): - if not tool.System.get_connected_port(port): - source_port = port - break - - if not source_port: - return - - core.connect_port(tool.Ifc, port1=source_port, port2=target_element) - PortData.is_loaded = False - self.related_port_object = None + return items if items else [("NONE", "Ports are hidden or not available", "")] class BIMSystemProperties(PropertyGroup): @@ -155,12 +128,10 @@ class BIMSystemProperties(PropertyGroup): should_draw_decorations: BoolProperty( name="Should Draw Decorations", description="Toggle system decorations", update=toggle_decorations ) - related_port_object: PointerProperty( - type=bpy.types.Object, + related_port: EnumProperty( name="Connect To Port", - description="Select a port to connect to. Use eyedropper to pick a port object", - poll=is_port_available_for_connection, - update=update_related_port_object, + description="Select a port to connect to", + items=get_available_ports_for_connection, ) if TYPE_CHECKING: @@ -174,7 +145,7 @@ class BIMSystemProperties(PropertyGroup): edited_system_id: int system_class: str should_draw_decorations: bool - related_port_object: Union[bpy.types.Object, None] + related_port: str @property def active_system_ui_item(self) -> Union[System, None]: diff --git a/src/bonsai/bonsai/bim/module/system/ui.py b/src/bonsai/bonsai/bim/module/system/ui.py index 6e71e5486f..c4b142c31f 100644 --- a/src/bonsai/bonsai/bim/module/system/ui.py +++ b/src/bonsai/bonsai/bim/module/system/ui.py @@ -181,9 +181,8 @@ class BIM_PT_ports(Panel): op = cols[1].operator("bim.cycle_flow_direction", text="", icon=flow_direction_icon, emboss=True) op.port_id = port_data["id"] else: - blank0 = cols[0].column(align=True) - blank0.scale_x = 0.1 - blank0.label(text="", icon="BLANK1") + op = cols[0].operator("bim.add_related_port_connection", text="", icon='PLUGIN') + op.relating_port_id = port_data["id"] op = cols[1].operator("bim.cycle_flow_direction", text="", icon=flow_direction_icon, emboss=True) op.port_id = port_data["id"] @@ -236,15 +235,14 @@ class BIM_PT_ports(Panel): blank5.scale_x = 0.1 blank5.label(text="", icon="BLANK1") - col6_row = cols[6].row(align=True) - col6_row.alignment = 'LEFT' - col6_row.prop(props, "related_port_object", text="", icon='OBJECT_DATA') - col6_row.scale_x = 0.35 + cols[6].label(text="Port is disconnected") blank7 = cols[7].column(align=True) blank7.scale_x = 0.1 blank7.label(text="", icon="BLANK1") - cols[8].label(text="Port is disconnected") + blank8 = cols[8].column(align=True) + blank8.scale_x = 0.1 + blank8.label(text="", icon="BLANK1") class BIM_PT_port(Panel): diff --git a/src/bonsai/bonsai/tool/system.py b/src/bonsai/bonsai/tool/system.py index 38a93a3649..0236b40fbd 100644 --- a/src/bonsai/bonsai/tool/system.py +++ b/src/bonsai/bonsai/tool/system.py @@ -117,14 +117,8 @@ class System(bonsai.core.tool.System): port.PredefinedType = "USERDEFINED" systems = ifcopenshell.util.system.get_element_systems(element) - if systems: - system = systems[0] - if hasattr(system, "PredefinedType") and system.PredefinedType: - port.SystemType = system.PredefinedType - else: - port.SystemType = "USERDEFINED" - else: - port.SystemType = "USERDEFINED" + system = systems[0] if systems else None + port.SystemType = getattr(system, "PredefinedType", None) or "USERDEFINED" matrix = element_obj.matrix_world.copy() matrix.translation = bpy.context.scene.cursor.matrix.translation