@tscircuit/capacity-autorouter
v0.0.938
Published
An MIT-licensed PCB autorouter made for usage with [tscircuit](https://github.com/tscircuit/tscircuit). This is the builtin autorouter.
Readme
@tscircuit/capacity-autorouter
An MIT-licensed PCB autorouter made for usage with tscircuit. This is the builtin autorouter.
The autorouter is composed of hundreds of algorithms organized in a Pipeline. The autorouter uses successive approximation and Hypergraphs rather than sequential routing.
View Online Playground · tscircuit docs · discord · twitter · try tscircuit online · Report/Debug Autorouter Bugs
Want to understand how the autorouter works? Read this blog post
How to file a bug report
- You should have created a bug report via the tscircuit errors tab
- Run
bun run bug-report <bug-report-url>to download the report and create a debugging fixture file in theexamples/bug-reportsdirectory, you can then find the bug report in the server (viabun run start) - Or run
bun run bug-report-with-test <bug-report-url>to download the report, create the fixture, and scaffold a matching snapshot test undertests/bugs
Or run the Create Bug Report workflow to automatically create a PR with the bug report (maintainers only)
Installation
bun add @tscircuit/capacity-autorouterUsage as a Library
Basic Usage
import { AutoroutingPipelineSolver } from "@tscircuit/capacity-autorouter"
// Create a solver with SimpleRouteJson input
const solver = new AutoroutingPipelineSolver(simpleRouteJson)
// Run the solver until completion
while (!solver.solved && !solver.failed) {
solver.step()
}
// Check if solving was successful
if (solver.failed) {
console.error("Routing failed:", solver.error)
} else {
// Get the routing results as SimpleRouteJson with traces
const resultWithRoutes = solver.getOutputSimpleRouteJson()
// Use the resulting routes in your application
console.log(
`Successfully routed ${resultWithRoutes.traces?.length} connections`
)
}Simplifying Existing Traces
Use AutoroutingPipelineSolver11_Simplification when the input already
contains routed traces and only needs post-route cleanup. This pipeline does
not route missing SRJ connections. Constant-width traces are simplified while
variable-width traces retain their exact copper widths and geometry.
import { AutoroutingPipelineSolver11_Simplification } from "@tscircuit/capacity-autorouter"
const solver = new AutoroutingPipelineSolver11_Simplification(simpleRouteJson)
solver.solve()
if (solver.failed) {
throw new Error(solver.error ?? "Trace simplification failed")
}
const simplified = solver.getOutputSimpleRouteJson()Input Format: SimpleRouteJson
The input to the autorouter is a SimpleRouteJson object with the following structure:
interface SimpleRouteJson {
layerCount: number
minTraceWidth: number
obstacles: Obstacle[]
connections: Array<SimpleRouteConnection>
buses?: Array<SimpleRouteBus>
allowViaInPad?: boolean
bounds: { minX: number; maxX: number; minY: number; maxY: number }
traces?: SimplifiedPcbTraces // Optional for input
}
interface Obstacle {
type: "rect"
layers: string[]
center: { x: number; y: number }
width: number
height: number
ccwRotationDegrees?: number
connectedTo: string[] // TraceIds
isCopperPour?: boolean
offBoardConnectsTo?: string[] // TraceIds connected off-board
}
interface SimpleRouteConnection {
name: string
pointsToConnect: Array<SingleLayerConnectionPoint | MultiLayerConnectionPoint>
}
type SingleLayerConnectionPoint = {
x: number
y: number
layer: string
layers?: never
}
type MultiLayerConnectionPoint = {
x: number
y: number
layers: string[]
layer?: never
}
interface SimpleRouteBus {
busId: string
connectionNames: string[] // Ordered SimpleRouteConnection names
maxLengthSkew?: number // Maximum routed-length difference in millimeters
traceWidth?: number // Resolved copper width in millimeters
allowedLayers?: string[] // Legal routing layers, including terminal layers
}
interface DifferentialPair {
connectionNames: [string, string]
lengthTolerance: number // Maximum pair skew in millimeters
traceGap?: number // Resolved edge-to-edge copper gap in millimeters
maxUncoupledLength?: number // Maximum uncoupled length in millimeters
}Pipelines 4–9 validate connection points in the first preprocessing stage.
On-board points must be inside bounds, including its edges. An outside point
stops routing with solved = false, failed = true, and an error identifying
the connection, point, coordinates, and bounds. Connections marked isOffBoard
are exempt.
Connection points use exactly one representation: layer for a fixed routing
layer, or layers for a terminal accessible on multiple routing layers. Never
include both fields. The optional never properties enforce this distinction
in TypeScript; they are not JSON fields to emit. For multilayer points, the first
entry is the primary layer. Obstacle and via layers arrays describe their
physical copper span and are separate from the connection-point representation.
maxLengthSkew records the maximum permitted routed-length difference for the
bus. Bus metadata is preserved in the output so routing implementations can
apply the constraint without losing the original membership or ordering.
traceWidth, traceGap, and allowedLayers are resolved routing geometry;
stackup-aware impedance targets should be converted to these dimensions before
creating SimpleRouteJson.
Via-in-pad repair is disabled by default because it generally requires filled
and capped vias. Set allowViaInPad: true only when the fabrication process
supports it.
Output Format
The getOutputSimpleRouteJson() method returns the original SimpleRouteJson with a populated traces property. The traces are represented as SimplifiedPcbTraces:
type SimplifiedPcbTraces = Array<{
type: "pcb_trace"
pcb_trace_id: string // TraceId
route: Array<
| {
route_type: "wire"
x: number
y: number
width: number
layer: string
}
| {
route_type: "via"
x: number
y: number
to_layer: string
from_layer: string
}
>
}>Advanced Configuration
You can provide optional configuration parameters to the solver:
const solver = new CapacityMeshSolver(simpleRouteJson, {
// Optional: Manually set capacity planning depth (otherwise automatically calculated)
capacityDepth: 7,
// Optional: Set the target minimum capacity for automatic depth calculation
// Lower values result in finer subdivisions (higher depth)
targetMinCapacity: 0.5,
})By default, the solver will automatically calculate the optimal capacityDepth to achieve a target minimum capacity of 0.5 based on the board dimensions. This automatic calculation ensures that the smallest subdivision cells have an appropriate capacity for routing.
Visualization Support
For debugging or interactive applications, you can use the visualize() method to get a visualization of the current routing state:
// Get visualization data that can be rendered with graphics-debug
const visualization = solver.visualize()Development
To work on this library:
# Install dependencies
bun install
# Start the interactive development environment
bun run start
# Run tests
bun test
# Build the library
bun run buildMaintainer resources
Track routing performance and benchmark results in the Autorouter Benchmark Dashboard.
DRC failure dataset (SRJ33)
dataset-srj33-drc-failures contains 37 distinct inputs with at least one measured Pipeline 9 relaxed DRC issue. The original benchmark retained 12 samples and excluded 19 DRC passes. An additional audit added 25 bug-report inputs that completed routing with DRC issues after the recent DRC fix. Original IDs remain 001–006, 010–013, 020, and 025; additions use 032–056.
bun scripts/run-sample.ts --pipeline 9 --dataset srj33 --sample 1Use srj33 in the benchmark workflow's dataset input, or open
benchmarks/dataset-srj33 in Cosmos. CLI --sample selects by position:
--sample 12 loads sample025, and --sample 37 loads sample056.
Cosmos uses the sample IDs. The dataset records source links, pinned revisions,
and Pipeline 9 selection evidence. Saved outputs for the original 12 are their
historical Pipeline 7 baseline; additions include Pipeline 9 outputs and exact
DRC errors.
