Cube-view: 90° router mesh links, 45° component connectors

Router-router mesh links remain straight (horizontal/vertical).
All component→router connectors use 45° L-bend polylines:
- PE blocks: vertical then 45° diagonal to router
- M_CPU/SRAM: horizontal then 45° diagonal to router
- PE→HBM port group: vertical then 45° diagonal
- UCIe port→router: direction-aware 45° bend

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-05 00:20:28 -07:00
parent df81835d84
commit d3de982ea4
2 changed files with 106 additions and 63 deletions
+72 -29
View File
@@ -632,23 +632,49 @@ def _render_cube_view_svg(view: ViewGraph, spec: dict) -> str:
f'text-anchor="middle" font-family="monospace" font-size="{font_sz}" '
f'font-weight="bold" fill="{style["text"]}">{_escape(label)}</text>'
)
# Connector line: block → router
# Connector line: block → router (45° L-bend)
sc = style["stroke"]
if kind in ("mcpu", "sram"):
# Horizontal connector (block right edge → router left edge)
# From block right edge, 45° bend to router
x1 = bx + blk_w
y1 = by + blk_h / 2
x2 = px - r_size
y2 = py
# 45° bend: go horizontal first, then diagonal
mid_x = x2 - abs(y2 - y1)
if mid_x < x1:
mid_x = (x1 + x2) / 2
parts.append(
f' <line x1="{bx + blk_w:.0f}" y1="{by + blk_h / 2:.0f}" '
f'x2="{px - r_size:.0f}" y2="{py:.0f}" '
f'stroke="{style["stroke"]}" stroke-width="1" opacity="0.6"/>'
f' <polyline points="{x1:.0f},{y1:.0f} {mid_x:.0f},{y1:.0f} {x2:.0f},{y2:.0f}" '
f'fill="none" stroke="{sc}" stroke-width="1" opacity="0.6"/>'
)
else:
# Vertical connector (PE above/below router)
ly1 = by + blk_h if is_top else by
ly2 = py - r_size if is_top else py + r_size
parts.append(
f' <line x1="{px + offset_x:.0f}" y1="{ly1:.0f}" '
f'x2="{px:.0f}" y2="{ly2:.0f}" '
f'stroke="{style["stroke"]}" stroke-width="1" opacity="0.6"/>'
)
# PE: from block bottom/top edge, 45° bend to router
if is_top:
x1 = bx + blk_w / 2 + offset_x
y1 = by + blk_h
x2 = px
y2 = py - r_size
# 45° bend: go vertical first, then diagonal
mid_y = y2 - abs(x2 - x1)
if mid_y < y1:
mid_y = (y1 + y2) / 2
parts.append(
f' <polyline points="{x1:.0f},{y1:.0f} {x1:.0f},{mid_y:.0f} {x2:.0f},{y2:.0f}" '
f'fill="none" stroke="{sc}" stroke-width="1" opacity="0.6"/>'
)
else:
x1 = bx + blk_w / 2 + offset_x
y1 = by
x2 = px
y2 = py + r_size
mid_y = y2 + abs(x2 - x1)
if mid_y > y1:
mid_y = (y1 + y2) / 2
parts.append(
f' <polyline points="{x1:.0f},{y1:.0f} {x1:.0f},{mid_y:.0f} {x2:.0f},{y2:.0f}" '
f'fill="none" stroke="{sc}" stroke-width="1" opacity="0.6"/>'
)
# (PE→HBM BW annotation drawn in the PE→HBM port group section above)
@@ -668,10 +694,15 @@ def _render_cube_view_svg(view: ViewGraph, spec: dict) -> str:
rpx, rpy = mm2px(rx, ry)
tgx, tgy = _pe_hbm_targets[pe_id]
r_edge_y = rpy + r_size if rpy < hbm_y else rpy - r_size
# 45° L-bend: vertical from router, then diagonal to HBM port
mid_y = tgy - abs(tgx - rpx) if rpy < hbm_y else tgy + abs(tgx - rpx)
if rpy < hbm_y:
mid_y = max(mid_y, (r_edge_y + tgy) / 2)
else:
mid_y = min(mid_y, (r_edge_y + tgy) / 2)
parts.append(
f' <line x1="{rpx:.0f}" y1="{r_edge_y:.0f}" '
f'x2="{tgx:.0f}" y2="{tgy:.0f}" '
f'stroke="#10b981" stroke-width="1.5" opacity="0.6" '
f' <polyline points="{rpx:.0f},{r_edge_y:.0f} {rpx:.0f},{mid_y:.0f} {tgx:.0f},{tgy:.0f}" '
f'fill="none" stroke="#10b981" stroke-width="1.5" opacity="0.6" '
f'stroke-dasharray="4,3"/>'
)
# BW annotation at midpoint
@@ -765,31 +796,43 @@ def _render_cube_view_svg(view: ViewGraph, spec: dict) -> str:
f'{conn}</text>'
)
# Connector line: port → router
# Connector line: port → router (45° L-bend)
rpx, rpy = mm2px(crx, cry)
if direction == "N":
x1, y1 = lx, cy_box + ch
x2, y2 = rpx, rpy - r_size
mid_y = y2 - abs(x2 - x1)
mid_y = max(mid_y, (y1 + y2) / 2)
parts.append(
f' <line x1="{lx:.0f}" y1="{cy_box + ch:.0f}" '
f'x2="{rpx:.0f}" y2="{rpy - r_size:.0f}" '
f'stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
f' <polyline points="{x1:.0f},{y1:.0f} {x1:.0f},{mid_y:.0f} {x2:.0f},{y2:.0f}" '
f'fill="none" stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
)
elif direction == "S":
x1, y1 = lx, cy_box
x2, y2 = rpx, rpy + r_size
mid_y = y2 + abs(x2 - x1)
mid_y = min(mid_y, (y1 + y2) / 2)
parts.append(
f' <line x1="{lx:.0f}" y1="{cy_box:.0f}" '
f'x2="{rpx:.0f}" y2="{rpy + r_size:.0f}" '
f'stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
f' <polyline points="{x1:.0f},{y1:.0f} {x1:.0f},{mid_y:.0f} {x2:.0f},{y2:.0f}" '
f'fill="none" stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
)
elif direction == "W":
x1, y1 = cx + cw, cy_box + ch / 2
x2, y2 = rpx - r_size, rpy
mid_x = x2 - abs(y2 - y1)
mid_x = max(mid_x, (x1 + x2) / 2)
parts.append(
f' <line x1="{cx + cw:.0f}" y1="{cy_box + ch / 2:.0f}" '
f'x2="{rpx - r_size:.0f}" y2="{rpy:.0f}" '
f'stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
f' <polyline points="{x1:.0f},{y1:.0f} {mid_x:.0f},{y1:.0f} {x2:.0f},{y2:.0f}" '
f'fill="none" stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
)
elif direction == "E":
x1, y1 = cx, cy_box + ch / 2
x2, y2 = rpx + r_size, rpy
mid_x = x2 + abs(y2 - y1)
mid_x = min(mid_x, (x1 + x2) / 2)
parts.append(
f' <line x1="{cx:.0f}" y1="{cy_box + ch / 2:.0f}" '
f'x2="{rpx + r_size:.0f}" y2="{rpy:.0f}" '
f'stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
f' <polyline points="{x1:.0f},{y1:.0f} {mid_x:.0f},{y1:.0f} {x2:.0f},{y2:.0f}" '
f'fill="none" stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
)
# ── Legend ──