Improve visualizations

This commit is contained in:
Jan Petykiewicz 2026-03-08 22:13:10 -07:00
commit 43a9a6cb3a
9 changed files with 35 additions and 9 deletions

View file

@ -49,7 +49,7 @@ def main() -> None:
print("Failed to route.") print("Failed to route.")
# 5. Visualize # 5. Visualize
fig, ax = plot_routing_results(results, [obstacle], bounds) fig, ax = plot_routing_results(results, [obstacle], bounds, netlist=netlist)
fig.savefig("examples/simple_route.png") fig.savefig("examples/simple_route.png")
print("Saved plot to examples/simple_route.png") print("Saved plot to examples/simple_route.png")

View file

@ -45,7 +45,7 @@ def main() -> None:
print(f" {nid}: valid={res.is_valid}, collisions={res.collisions}") print(f" {nid}: valid={res.is_valid}, collisions={res.collisions}")
# 5. Visualize # 5. Visualize
fig, ax = plot_routing_results(results, [], bounds) fig, ax = plot_routing_results(results, [], bounds, netlist=netlist)
fig.savefig("examples/congestion.png") fig.savefig("examples/congestion.png")
print("Saved plot to examples/congestion.png") print("Saved plot to examples/congestion.png")

View file

@ -59,16 +59,18 @@ def main() -> None:
print("Failed to route secondary net.") print("Failed to route secondary net.")
# 5. Visualize # 5. Visualize
# Combine results for plotting # Combine results and netlists for plotting
all_results = {**results1, **results2} all_results = {**results1, **results2}
all_netlists = {**netlist_phase1, **netlist_phase2}
# Note: 'critical_net' is now in engine.static_obstacles internally, # Note: 'critical_net' is now in engine.static_obstacles internally,
# but for visualization we plot it from the result object to see it clearly. # but for visualization we plot it from the result object to see it clearly.
# We pass an empty list for 'static_obstacles' to plot_routing_results # We pass an empty list for 'static_obstacles' to plot_routing_results
# because we want to see the path colored, not grayed out as an obstacle. # because we want to see the path colored, not grayed out as an obstacle.
fig, ax = plot_routing_results(all_results, [], bounds) fig, ax = plot_routing_results(all_results, [], bounds, netlist=all_netlists)
fig.savefig("examples/locked.png") fig.savefig("examples/locked.png")
print("Saved plot to examples/locked.png") print("Saved plot to examples/locked.png")

View file

@ -61,7 +61,7 @@ def main() -> None:
print(f"{nid}: {status}, collisions={res.collisions}") print(f"{nid}: {status}, collisions={res.collisions}")
# 6. Visualize # 6. Visualize
fig, ax = plot_routing_results(results, [], bounds) fig, ax = plot_routing_results(results, [], bounds, netlist=netlist)
fig.savefig("examples/sbends_radii.png") fig.savefig("examples/sbends_radii.png")
print("Saved plot to examples/sbends_radii.png") print("Saved plot to examples/sbends_radii.png")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before After
Before After

View file

@ -3,12 +3,14 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np
if TYPE_CHECKING: if TYPE_CHECKING:
from matplotlib.axes import Axes from matplotlib.axes import Axes
from matplotlib.figure import Figure from matplotlib.figure import Figure
from shapely.geometry import Polygon from shapely.geometry import Polygon
from inire.geometry.primitives import Port
from inire.router.pathfinder import RoutingResult from inire.router.pathfinder import RoutingResult
@ -16,6 +18,7 @@ def plot_routing_results(
results: dict[str, RoutingResult], results: dict[str, RoutingResult],
static_obstacles: list[Polygon], static_obstacles: list[Polygon],
bounds: tuple[float, float, float, float], bounds: tuple[float, float, float, float],
netlist: dict[str, tuple[Port, Port]] | None = None,
) -> tuple[Figure, Axes]: ) -> tuple[Figure, Axes]:
"""Plot obstacles and routed paths using matplotlib.""" """Plot obstacles and routed paths using matplotlib."""
fig, ax = plt.subplots(figsize=(10, 10)) fig, ax = plt.subplots(figsize=(10, 10))
@ -34,7 +37,8 @@ def plot_routing_results(
color = "red" # Highlight failing nets color = "red" # Highlight failing nets
label_added = False label_added = False
for comp in res.path: for j, comp in enumerate(res.path):
# 1. Plot geometry
for poly in comp.geometry: for poly in comp.geometry:
# Handle both Polygon and MultiPolygon (e.g. from SBend) # Handle both Polygon and MultiPolygon (e.g. from SBend)
geoms = [poly] if hasattr(poly, "exterior") else poly.geoms geoms = [poly] if hasattr(poly, "exterior") else poly.geoms
@ -43,6 +47,26 @@ def plot_routing_results(
ax.fill(x, y, alpha=0.7, fc=color, ec="black", label=net_id if not label_added else "") ax.fill(x, y, alpha=0.7, fc=color, ec="black", label=net_id if not label_added else "")
label_added = True label_added = True
# 2. Plot subtle port orientation arrow for internal ports
# (Every segment's end_port except possibly the last one if it matches target)
p = comp.end_port
rad = np.radians(p.orientation)
u = np.cos(rad)
v = np.sin(rad)
# Internal ports get smaller, narrower, semi-transparent arrows
ax.quiver(p.x, p.y, u, v, color="black", scale=40, width=0.003, alpha=0.3, pivot="tail", zorder=4)
# 3. Plot main arrows for netlist ports (if provided)
if netlist and net_id in netlist:
start_p, target_p = netlist[net_id]
for p in [start_p, target_p]:
rad = np.radians(p.orientation)
u = np.cos(rad)
v = np.sin(rad)
# Netlist ports get prominent arrows
ax.quiver(p.x, p.y, u, v, color="black", scale=25, width=0.005, pivot="tail", zorder=6)
ax.set_xlim(bounds[0], bounds[2]) ax.set_xlim(bounds[0], bounds[2])
ax.set_ylim(bounds[1], bounds[3]) ax.set_ylim(bounds[1], bounds[3])
ax.set_aspect("equal") ax.set_aspect("equal")
@ -51,5 +75,5 @@ def plot_routing_results(
handles, labels = ax.get_legend_handles_labels() handles, labels = ax.get_legend_handles_labels()
if labels: if labels:
ax.legend() ax.legend()
plt.grid(True) ax.grid(alpha=0.6)
return fig, ax return fig, ax