mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 23:36:20 +00:00
Refactor port connection logic and UI integration for improved usability
This commit is contained in:
@@ -265,37 +265,31 @@ class ConnectPort(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
class AddRelatedPortConnection(bpy.types.Operator, tool.Ifc.Operator):
|
class AddRelatedPortConnection(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
bl_idname = "bim.add_related_port_connection"
|
bl_idname = "bim.add_related_port_connection"
|
||||||
bl_label = "Connect Port"
|
bl_label = "Connect Port"
|
||||||
bl_description = "Click to select a port to connect to"
|
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
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):
|
def invoke(self, context, event):
|
||||||
if self.element_id == 0:
|
return context.window_manager.invoke_props_dialog(self)
|
||||||
self.report({'ERROR'}, "No port specified")
|
|
||||||
return {'CANCELLED'}
|
def draw(self, context):
|
||||||
context.window_manager.modal_handler_add(self)
|
props = tool.System.get_system_props()
|
||||||
return {'RUNNING_MODAL'}
|
self.layout.prop(props, "related_port", text="Select Port")
|
||||||
|
|
||||||
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'}
|
|
||||||
|
|
||||||
def _execute(self, context):
|
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):
|
class DisconnectPort(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -454,19 +448,19 @@ class CycleFlowDirection(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
current_direction = port.FlowDirection or "NOTDEFINED"
|
current_direction = port.FlowDirection or "NOTDEFINED"
|
||||||
flow_cycle = ["SOURCE", "SINK", "SOURCEANDSINK", "NOTDEFINED"]
|
|
||||||
|
|
||||||
try:
|
flow_cycle_map = {
|
||||||
current_index = flow_cycle.index(current_direction)
|
"SOURCE": "SINK",
|
||||||
next_direction = flow_cycle[(current_index + 1) % len(flow_cycle)]
|
"SINK": "SOURCEANDSINK",
|
||||||
except ValueError:
|
"SOURCEANDSINK": "NOTDEFINED",
|
||||||
next_direction = "SOURCE"
|
"NOTDEFINED": "SOURCE"
|
||||||
|
}
|
||||||
|
next_direction = flow_cycle_map.get(current_direction, "SOURCE")
|
||||||
|
|
||||||
tool.Ifc.run("attribute.edit_attributes", product=port, attributes={"FlowDirection": next_direction})
|
tool.Ifc.run("attribute.edit_attributes", product=port, attributes={"FlowDirection": next_direction})
|
||||||
|
|
||||||
connected_port = tool.System.get_connected_port(port)
|
connected_port = tool.System.get_connected_port(port)
|
||||||
if connected_port:
|
if connected_port:
|
||||||
# Map flow direction to connected port's complementary direction
|
|
||||||
connected_direction_map = {
|
connected_direction_map = {
|
||||||
"SOURCE": "SINK",
|
"SOURCE": "SINK",
|
||||||
"SINK": "SOURCE",
|
"SINK": "SOURCE",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from bpy.props import (
|
|||||||
FloatVectorProperty,
|
FloatVectorProperty,
|
||||||
CollectionProperty,
|
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]]:
|
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()
|
decorator.SystemDecorator.uninstall()
|
||||||
|
|
||||||
|
|
||||||
def is_port_available_for_connection(self: "BIMSystemProperties", obj: bpy.types.Object) -> bool:
|
def get_available_ports_for_connection(self: "BIMSystemProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||||
element = tool.Ifc.get_entity(obj)
|
items = []
|
||||||
if not element or not element.is_a("IfcDistributionPort"):
|
active_object_ports = set(tool.System.get_ports(tool.Ifc.get_entity(context.active_object)))
|
||||||
return False
|
|
||||||
connected_port = tool.System.get_connected_port(element)
|
|
||||||
if connected_port is not None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if bpy.context.active_object:
|
ifc_file = tool.Ifc.get()
|
||||||
active_element = tool.Ifc.get_entity(bpy.context.active_object)
|
for ifc_port in ifc_file.by_type("IfcDistributionPort"):
|
||||||
if active_element:
|
port = tool.Ifc.get_object(ifc_port)
|
||||||
active_ports = tool.System.get_ports(active_element)
|
if not port:
|
||||||
if element in active_ports:
|
continue
|
||||||
return False
|
|
||||||
|
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
|
return items if items else [("NONE", "Ports are hidden or not available", "")]
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
class BIMSystemProperties(PropertyGroup):
|
class BIMSystemProperties(PropertyGroup):
|
||||||
@@ -155,12 +128,10 @@ class BIMSystemProperties(PropertyGroup):
|
|||||||
should_draw_decorations: BoolProperty(
|
should_draw_decorations: BoolProperty(
|
||||||
name="Should Draw Decorations", description="Toggle system decorations", update=toggle_decorations
|
name="Should Draw Decorations", description="Toggle system decorations", update=toggle_decorations
|
||||||
)
|
)
|
||||||
related_port_object: PointerProperty(
|
related_port: EnumProperty(
|
||||||
type=bpy.types.Object,
|
|
||||||
name="Connect To Port",
|
name="Connect To Port",
|
||||||
description="Select a port to connect to. Use eyedropper to pick a port object",
|
description="Select a port to connect to",
|
||||||
poll=is_port_available_for_connection,
|
items=get_available_ports_for_connection,
|
||||||
update=update_related_port_object,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -174,7 +145,7 @@ class BIMSystemProperties(PropertyGroup):
|
|||||||
edited_system_id: int
|
edited_system_id: int
|
||||||
system_class: str
|
system_class: str
|
||||||
should_draw_decorations: bool
|
should_draw_decorations: bool
|
||||||
related_port_object: Union[bpy.types.Object, None]
|
related_port: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_system_ui_item(self) -> Union[System, None]:
|
def active_system_ui_item(self) -> Union[System, None]:
|
||||||
|
|||||||
@@ -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 = cols[1].operator("bim.cycle_flow_direction", text="", icon=flow_direction_icon, emboss=True)
|
||||||
op.port_id = port_data["id"]
|
op.port_id = port_data["id"]
|
||||||
else:
|
else:
|
||||||
blank0 = cols[0].column(align=True)
|
op = cols[0].operator("bim.add_related_port_connection", text="", icon='PLUGIN')
|
||||||
blank0.scale_x = 0.1
|
op.relating_port_id = port_data["id"]
|
||||||
blank0.label(text="", icon="BLANK1")
|
|
||||||
op = cols[1].operator("bim.cycle_flow_direction", text="", icon=flow_direction_icon, emboss=True)
|
op = cols[1].operator("bim.cycle_flow_direction", text="", icon=flow_direction_icon, emboss=True)
|
||||||
op.port_id = port_data["id"]
|
op.port_id = port_data["id"]
|
||||||
|
|
||||||
@@ -236,15 +235,14 @@ class BIM_PT_ports(Panel):
|
|||||||
blank5.scale_x = 0.1
|
blank5.scale_x = 0.1
|
||||||
blank5.label(text="", icon="BLANK1")
|
blank5.label(text="", icon="BLANK1")
|
||||||
|
|
||||||
col6_row = cols[6].row(align=True)
|
cols[6].label(text="Port is disconnected")
|
||||||
col6_row.alignment = 'LEFT'
|
|
||||||
col6_row.prop(props, "related_port_object", text="", icon='OBJECT_DATA')
|
|
||||||
col6_row.scale_x = 0.35
|
|
||||||
|
|
||||||
blank7 = cols[7].column(align=True)
|
blank7 = cols[7].column(align=True)
|
||||||
blank7.scale_x = 0.1
|
blank7.scale_x = 0.1
|
||||||
blank7.label(text="", icon="BLANK1")
|
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):
|
class BIM_PT_port(Panel):
|
||||||
|
|||||||
@@ -117,14 +117,8 @@ class System(bonsai.core.tool.System):
|
|||||||
port.PredefinedType = "USERDEFINED"
|
port.PredefinedType = "USERDEFINED"
|
||||||
|
|
||||||
systems = ifcopenshell.util.system.get_element_systems(element)
|
systems = ifcopenshell.util.system.get_element_systems(element)
|
||||||
if systems:
|
system = systems[0] if systems else None
|
||||||
system = systems[0]
|
port.SystemType = getattr(system, "PredefinedType", None) or "USERDEFINED"
|
||||||
if hasattr(system, "PredefinedType") and system.PredefinedType:
|
|
||||||
port.SystemType = system.PredefinedType
|
|
||||||
else:
|
|
||||||
port.SystemType = "USERDEFINED"
|
|
||||||
else:
|
|
||||||
port.SystemType = "USERDEFINED"
|
|
||||||
|
|
||||||
matrix = element_obj.matrix_world.copy()
|
matrix = element_obj.matrix_world.copy()
|
||||||
matrix.translation = bpy.context.scene.cursor.matrix.translation
|
matrix.translation = bpy.context.scene.cursor.matrix.translation
|
||||||
|
|||||||
Reference in New Issue
Block a user