Skip to content

Commit bbda8d2

Browse files
authored
Merge pull request #7543 from falken10vdl/MEP-ports
Simplified handling of Ports in MEP Addresses https://community.osarch.org/discussion/comment/27740#Comment_27740
2 parents c2cc6f8 + 96a0e9f commit bbda8d2

11 files changed

Lines changed: 317 additions & 89 deletions

File tree

src/bonsai/bonsai/bim/module/system/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121

2222
classes = (
2323
operator.AddPort,
24+
operator.AddRelatedPortConnection,
2425
operator.AddSystem,
2526
operator.AddZone,
2627
operator.AssignSystem,
2728
operator.AssignUnassignFlowControl,
2829
operator.ConnectPort,
30+
operator.CycleFlowDirection,
2931
operator.DisableEditingSystem,
3032
operator.DisableEditingZone,
3133
operator.DisableSystemEditingUI,

src/bonsai/bonsai/bim/module/system/operator.py

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,16 @@ def poll(cls, context):
198198

199199
def _execute(self, context):
200200
# Ifc.Operator - as operator will sync object's position with IFC.
201-
core.show_ports(tool.Ifc, tool.System, tool.Spatial, element=tool.Ifc.get_entity(context.active_object))
201+
element = tool.Ifc.get_entity(context.active_object)
202+
core.show_ports(tool.Ifc, tool.System, tool.Spatial, element=element)
203+
204+
for port in tool.System.get_ports(element):
205+
connected_port = tool.System.get_connected_port(port)
206+
if connected_port:
207+
connected_port_obj = tool.Ifc.get_object(connected_port)
208+
if not connected_port_obj:
209+
parent_element = tool.System.get_port_relating_element(connected_port)
210+
core.show_ports(tool.Ifc, tool.System, tool.Spatial, element=parent_element)
202211

203212

204213
class HidePorts(bpy.types.Operator, tool.Ifc.Operator):
@@ -217,7 +226,7 @@ def _execute(self, context):
217226

218227
class AddPort(bpy.types.Operator, tool.Ifc.Operator):
219228
bl_idname = "bim.add_port"
220-
bl_description = "Add port at current cursor position"
229+
bl_description = "Add USERDEFINED port at current cursor position"
221230
bl_label = "Add Port"
222231
bl_options = {"REGISTER", "UNDO"}
223232

@@ -246,7 +255,41 @@ def poll(cls, context):
246255
def _execute(self, context):
247256
obj1 = context.active_object
248257
obj2 = context.selected_objects[0] if context.selected_objects[1] == obj1 else context.selected_objects[1]
249-
core.connect_port(tool.Ifc, port1=tool.Ifc.get_entity(obj1), port2=tool.Ifc.get_entity(obj2))
258+
direction = tool.Ifc.get_entity(obj1).FlowDirection or "NOTDEFINED"
259+
core.connect_port(
260+
tool.Ifc, port1=tool.Ifc.get_entity(obj1), port2=tool.Ifc.get_entity(obj2), direction=direction
261+
)
262+
263+
264+
class AddRelatedPortConnection(bpy.types.Operator, tool.Ifc.Operator):
265+
bl_idname = "bim.add_related_port_connection"
266+
bl_label = "Connect Port"
267+
bl_options = {"REGISTER", "UNDO"}
268+
269+
relating_port_id: bpy.props.IntProperty()
270+
271+
def invoke(self, context, event):
272+
return context.window_manager.invoke_props_dialog(self)
273+
274+
def draw(self, context):
275+
props = tool.System.get_system_props()
276+
self.layout.prop(props, "related_port", text="Select Port")
277+
278+
def _execute(self, context):
279+
props = tool.System.get_system_props()
280+
281+
if not props.related_port or props.related_port == "NONE":
282+
return {"CANCELLED"}
283+
284+
port_obj = bpy.data.objects.get(props.related_port)
285+
related_port = tool.Ifc.get_entity(port_obj)
286+
relating_port = tool.Ifc.get().by_id(self.relating_port_id)
287+
288+
direction = relating_port.FlowDirection or "NOTDEFINED"
289+
core.connect_port(tool.Ifc, port1=relating_port, port2=related_port, direction=direction)
290+
PortData.is_loaded = False
291+
292+
return {"FINISHED"}
250293

251294

252295
class DisconnectPort(bpy.types.Operator, tool.Ifc.Operator):
@@ -256,6 +299,9 @@ class DisconnectPort(bpy.types.Operator, tool.Ifc.Operator):
256299

257300
element_id: bpy.props.IntProperty(default=0, options={"SKIP_SAVE"})
258301

302+
def invoke(self, context, event):
303+
return context.window_manager.invoke_confirm(self, event)
304+
259305
def _execute(self, context):
260306
if self.element_id != 0:
261307
element = tool.Ifc.get().by_id(self.element_id)
@@ -310,7 +356,8 @@ def _execute(self, context):
310356
ports_distance[(port1, port2)] = distance
311357

312358
closest_ports = min(ports_distance, key=lambda x: ports_distance[x])
313-
core.connect_port(tool.Ifc, *closest_ports)
359+
direction = closest_ports[0].FlowDirection or "NOTDEFINED"
360+
core.connect_port(tool.Ifc, *closest_ports, direction=direction)
314361
bpy.ops.bim.regenerate_distribution_element()
315362
return {"FINISHED"}
316363

@@ -379,6 +426,55 @@ def _execute(self, context):
379426
return {"CANCELLED"}
380427

381428

429+
class CycleFlowDirection(bpy.types.Operator, tool.Ifc.Operator):
430+
bl_idname = "bim.cycle_flow_direction"
431+
bl_label = "Cycle Flow Direction"
432+
bl_options = {"REGISTER", "UNDO"}
433+
port_id: bpy.props.IntProperty()
434+
435+
@classmethod
436+
def description(cls, context, operator):
437+
port = tool.Ifc.get().by_id(operator.port_id)
438+
if port and port.is_a("IfcDistributionPort"):
439+
current_direction = port.FlowDirection or "NOTDEFINED"
440+
return f"Current flow direction: {current_direction}. Click to cycle: SOURCE → SINK → SOURCEANDSINK → NOTDEFINED"
441+
return "Cycle through flow directions: SOURCE → SINK → SOURCEANDSINK → NOTDEFINED → SOURCE..."
442+
443+
def _execute(self, context):
444+
port = tool.Ifc.get().by_id(self.port_id)
445+
if not port or not port.is_a("IfcDistributionPort"):
446+
return {"CANCELLED"}
447+
448+
current_direction = port.FlowDirection or "NOTDEFINED"
449+
450+
flow_cycle_map = {
451+
"SOURCE": "SINK",
452+
"SINK": "SOURCEANDSINK",
453+
"SOURCEANDSINK": "NOTDEFINED",
454+
"NOTDEFINED": "SOURCE",
455+
}
456+
next_direction = flow_cycle_map.get(current_direction, "SOURCE")
457+
458+
tool.Ifc.run("attribute.edit_attributes", product=port, attributes={"FlowDirection": next_direction})
459+
460+
connected_port = tool.System.get_connected_port(port)
461+
if connected_port:
462+
connected_direction_map = {
463+
"SOURCE": "SINK",
464+
"SINK": "SOURCE",
465+
"SOURCEANDSINK": "SOURCEANDSINK",
466+
"NOTDEFINED": "NOTDEFINED",
467+
}
468+
connected_direction = connected_direction_map.get(next_direction, "NOTDEFINED")
469+
tool.Ifc.run(
470+
"attribute.edit_attributes", product=connected_port, attributes={"FlowDirection": connected_direction}
471+
)
472+
473+
PortData.is_loaded = False
474+
475+
return {"FINISHED"}
476+
477+
382478
class LoadZones(bpy.types.Operator):
383479
bl_idname = "bim.load_zones"
384480
bl_label = "Load Zones"

src/bonsai/bonsai/bim/module/system/prop.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,28 @@ def toggle_decorations(self: "BIMSystemProperties", context: bpy.types.Context)
9292
decorator.SystemDecorator.uninstall()
9393

9494

95+
def get_available_ports_for_connection(
96+
self: "BIMSystemProperties", context: bpy.types.Context
97+
) -> list[tuple[str, str, str]]:
98+
items = []
99+
active_object_ports = set(tool.System.get_ports(tool.Ifc.get_entity(context.active_object)))
100+
101+
ifc_file = tool.Ifc.get()
102+
for ifc_port in ifc_file.by_type("IfcDistributionPort"):
103+
port = tool.Ifc.get_object(ifc_port)
104+
if not port:
105+
continue
106+
107+
if tool.System.get_connected_port(ifc_port) is not None or ifc_port in active_object_ports:
108+
continue
109+
110+
port_object = tool.Ifc.get_object(tool.System.get_port_relating_element(ifc_port))
111+
suggestion = f"{port_object.name} > {port.name}"
112+
items.append((port.name, suggestion, ""))
113+
114+
return items if items else [("NONE", "Ports are hidden or not available", "")]
115+
116+
95117
class BIMSystemProperties(PropertyGroup):
96118
system_attributes: CollectionProperty(name="System Attributes", type=Attribute)
97119
is_editing: BoolProperty(name="Is Editing", default=False)
@@ -107,6 +129,11 @@ class BIMSystemProperties(PropertyGroup):
107129
should_draw_decorations: BoolProperty(
108130
name="Should Draw Decorations", description="Toggle system decorations", update=toggle_decorations
109131
)
132+
related_port: EnumProperty(
133+
name="Connect To Port",
134+
description="Select a port to connect to",
135+
items=get_available_ports_for_connection,
136+
)
110137

111138
if TYPE_CHECKING:
112139
system_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
@@ -119,6 +146,7 @@ class BIMSystemProperties(PropertyGroup):
119146
edited_system_id: int
120147
system_class: str
121148
should_draw_decorations: bool
149+
related_port: str
122150

123151
@property
124152
def active_system_ui_item(self) -> Union[System, None]:

0 commit comments

Comments
 (0)