java/com.sap.sailing.windestimation.lab/python/mst_graph_visualizer_graphviz.py
... ...
@@ -101,8 +101,9 @@ def format_distance(node):
101 101
def create_node_label_with_ports(node, best_type=None):
102 102
"""
103 103
Create HTML-like label for a node with 4 compartments.
104
- Each compartment has a PORT attribute for edge connections.
105
- Distance to parent is shown next to timestamp.
104
+ Each compartment has a PORT attribute for incoming edges (from above).
105
+ The footer row has additional ports for outgoing edges (going down).
106
+ This prevents edges from crossing through the timestamp/distance section.
106 107
"""
107 108
compartments = {c['type']: c for c in node['compartments']}
108 109
... ...
@@ -118,8 +119,9 @@ def create_node_label_with_ports(node, best_type=None):
118 119
119 120
# Build HTML table with PORT attributes
120 121
html = '<<TABLE BORDER="1" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">'
121
- html += '<TR>'
122 122
123
+ # Main row with compartments - ports here are for INCOMING edges (from above)
124
+ html += '<TR>'
123 125
for type_name in TYPE_ORDER:
124 126
comp = compartments.get(type_name)
125 127
if comp is None:
... ...
@@ -143,7 +145,7 @@ def create_node_label_with_ports(node, best_type=None):
143 145
wind_str = format_wind(comp)
144 146
abbrev = TYPE_ABBREV[type_name]
145 147
146
- # PORT attribute allows edges to connect to this specific cell
148
+ # PORT for incoming edges (named after type)
147 149
port_name = type_name
148 150
149 151
# Create cell content with PORT
... ...
@@ -154,14 +156,25 @@ def create_node_label_with_ports(node, best_type=None):
154 156
)
155 157
156 158
html += f'<TD PORT="{port_name}"{border_attr}>{cell_content}</TD>'
157
-
158 159
html += '</TR>'
159
- # Footer row with timestamp and distance to parent
160
+
161
+ # Footer row with timestamp/distance - spans all columns
160 162
if dist_str:
161 163
footer_content = f'{time_part} <FONT COLOR="blue">↑{dist_str}</FONT>'
162 164
else:
163 165
footer_content = time_part
164 166
html += f'<TR><TD COLSPAN="4" BGCOLOR="white"><FONT POINT-SIZE="9">{footer_content}</FONT></TD></TR>'
167
+
168
+ # Bottom row with ports for OUTGOING edges (minimal height)
169
+ # Each cell corresponds to one compartment position for proper horizontal alignment
170
+ html += '<TR>'
171
+ for type_name in TYPE_ORDER:
172
+ # Port for outgoing edges (named type_out)
173
+ out_port_name = f'{type_name}_out'
174
+ # Minimal height cells just for port positioning
175
+ html += f'<TD PORT="{out_port_name}" BGCOLOR="white" HEIGHT="1"></TD>'
176
+ html += '</TR>'
177
+
165 178
html += '</TABLE>>'
166 179
167 180
return html
... ...
@@ -279,8 +292,10 @@ def visualize_mst_graph(data, output_file=None, max_nodes=100, min_edge_prob=0.0
279 292
edge_label = f'{from_abbrev}→{to_abbrev}\\n{prob_str}'
280 293
281 294
# Use PORT to connect to specific compartments
282
- from_port = f'{from_id}:{from_type}:s' # :s = south (bottom) of cell
283
- to_port = f'{to_id}:{to_type}:n' # :n = north (top) of cell
295
+ # Outgoing edges use the _out ports in the footer row (below timestamp)
296
+ # Incoming edges use the compartment ports (top of compartment cells)
297
+ from_port = f'{from_id}:{from_type}_out:s' # :s = south (bottom) of footer cell
298
+ to_port = f'{to_id}:{to_type}:n' # :n = north (top) of compartment cell
284 299
285 300
dot.edge(from_port, to_port,
286 301
label=edge_label,