From 3e329cbf6b4bb2b7a26879cb912b56e4e2bc0308 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 7 Mar 2026 15:46:54 +0100 Subject: [PATCH] fix: route_pad_to_pad auto-inserts via when pads are on different copper layers - Detects F.Cu<->B.Cu layer mismatch in route_pad_to_pad - Splits route into: trace on start layer + via at midpoint + trace on end layer - route_trace description warns: use route_pad_to_pad for cross-layer routing - route_pad_to_pad description highlights automatic via insertion --- python/commands/routing.py | 80 +++++++++++++++++++++++++------------- src/tools/routing.ts | 4 +- 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/python/commands/routing.py b/python/commands/routing.py index befe72f..d630943 100644 --- a/python/commands/routing.py +++ b/python/commands/routing.py @@ -144,40 +144,64 @@ class RoutingCommands: if not net: net = start_pad.GetNetname() or end_pad.GetNetname() or "" - # Delegate to route_trace - result = self.route_trace( - { - "start": { - "x": start_pos.x / scale, - "y": start_pos.y / scale, - "unit": "mm", - }, - "end": { - "x": end_pos.x / scale, - "y": end_pos.y / scale, - "unit": "mm", - }, - "layer": layer, - "width": width, - "net": net, - } + # Detect if pads are on different copper layers → need via + start_layer = start_pad.GetLayerName() + end_layer = end_pad.GetLayerName() + copper_layers = {"F.Cu", "B.Cu"} + needs_via = ( + start_layer in copper_layers + and end_layer in copper_layers + and start_layer != end_layer ) + if needs_via: + # Place via at midpoint between the two pads + via_x = (start_pos.x + end_pos.x) / 2 / scale + via_y = (start_pos.y + end_pos.y) / 2 / scale + + # Trace on start layer: start_pad → via + r1 = self.route_trace({ + "start": {"x": start_pos.x / scale, "y": start_pos.y / scale, "unit": "mm"}, + "end": {"x": via_x, "y": via_y, "unit": "mm"}, + "layer": start_layer, "width": width, "net": net, + }) + # Via connecting both layers + self.add_via({ + "position": {"x": via_x, "y": via_y, "unit": "mm"}, + "net": net, + "from_layer": start_layer, + "to_layer": end_layer, + }) + # Trace on end layer: via → end_pad + r2 = self.route_trace({ + "start": {"x": via_x, "y": via_y, "unit": "mm"}, + "end": {"x": end_pos.x / scale, "y": end_pos.y / scale, "unit": "mm"}, + "layer": end_layer, "width": width, "net": net, + }) + success = r1.get("success") and r2.get("success") + result = { + "success": success, + "message": f"Routed {from_ref}.{from_pad} → via → {to_ref}.{to_pad} (net: {net}, via at {via_x:.2f},{via_y:.2f})", + "via_added": True, + "via_position": {"x": via_x, "y": via_y}, + } + else: + # Same layer — direct trace + result = self.route_trace({ + "start": {"x": start_pos.x / scale, "y": start_pos.y / scale, "unit": "mm"}, + "end": {"x": end_pos.x / scale, "y": end_pos.y / scale, "unit": "mm"}, + "layer": layer if layer else start_layer, + "width": width, "net": net, + }) + if result.get("success"): - result["message"] = ( - f"Routed {from_ref}.{from_pad} → {to_ref}.{to_pad} (net: {net or 'none'})" - ) result["fromPad"] = { - "ref": from_ref, - "pad": from_pad, - "x": start_pos.x / scale, - "y": start_pos.y / scale, + "ref": from_ref, "pad": from_pad, + "x": start_pos.x / scale, "y": start_pos.y / scale, } result["toPad"] = { - "ref": to_ref, - "pad": to_pad, - "x": end_pos.x / scale, - "y": end_pos.y / scale, + "ref": to_ref, "pad": to_pad, + "x": end_pos.x / scale, "y": end_pos.y / scale, } return result diff --git a/src/tools/routing.ts b/src/tools/routing.ts index f086608..309bd2d 100644 --- a/src/tools/routing.ts +++ b/src/tools/routing.ts @@ -33,7 +33,7 @@ export function registerRoutingTools( // Route trace tool server.tool( "route_trace", - "Route a trace between two points", + "Route a trace segment between two XY points on a fixed layer. WARNING: Does NOT handle layer changes — if start and end are on different copper layers, use route_pad_to_pad instead, which automatically inserts a via.", { start: z .object({ @@ -331,7 +331,7 @@ export function registerRoutingTools( // Route pad to pad tool server.tool( "route_pad_to_pad", - "Route a trace directly from one component pad to another without needing separate get_pad_position calls. Automatically looks up pad coordinates and uses the pad's net. Saves token usage compared to the 3-step get_pad_position + get_pad_position + route_trace sequence.", + "PREFERRED tool for pad-to-pad routing. Looks up pad positions automatically, detects the net from the pad, and — critically — if the two pads are on different copper layers (e.g. J1 on F.Cu and J2 on B.Cu) automatically inserts a via at the midpoint so the connection is complete. Always use this instead of route_trace when routing between named component pads.", { fromRef: z.string().describe("Reference of the source component (e.g. 'U2')"), fromPad: z.union([z.string(), z.number()]).describe("Pad number on the source component (e.g. '6' or 6)"),