MultilayerIjump (infgrid-ijump-astar 0.0.35) emits jumps mirrored away from the goal; fix + repro on a fork since tscircuit/autorouting is archived
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 58
- Forks
- 203
- Avg merge
- 7h 39m
- Merged PRs (30d)
- 286
Description
Where this bites in core
Trace_doInitialSchematicTraceRender.ts and Trace_doInitialPcbTraceRender.ts import MultilayerIjump from @tscircuit/infgrid-ijump-astar (^0.0.35). That package was last published from tscircuit/autorouting, which was archived on 2025-08-15 with tscircuit/autorouting#92 ("Multilayer Ijump: Remove wild trace jumps") still open. The repo is read-only, so the fix can't be PR'd or /attempted there; I'm filing here because core is the live consumer. Fix branch (1 commit, 3 files): https://github.com/tscircuit/autorouting/compare/main...twzrd-sol:autorouting:fix/multilayer-ijump-wild-jumps
Mechanism
In MultilayerIjump.getNeighbors, when a node has just hit a wall (node.obstacleHit set) and a perpendicular direction is open, this branch runs:
if (travelDir.wallDistance === Infinity) {
travelDirs3.push({ ...travelDir, travelDistance: goalDistAlongTravelDir, ... })
}
distAlongDir is an absolute value and, unlike the goal-axis branch above it, this push has no isGoalInTravelDir guard. The earlier branch already handles the goal being ahead along that direction, so by the time this runs the goal is behind (or level): the neighbor lands at node + dir * |goal - node|, mirrored AWAY from the goal by the full goal distance. Whenever the nearby overcome neighbors dead-end, A* expands that far node and the final trace contains the leap. The fix removes the push and keeps the !== Infinity guard on the wall-approach tiers that followed it (10 lines).
Repro (hand-built SimpleRouteJson, in the branch as a snapshot test)
Start pad at (0,0), goal at (8,-8), 2 layers, OBSTACLE_MARGIN default:
- W: wall, x in [-2, 38], y in [-1.1, -0.9], both layers
- F: block, x in [-6, -1], y in [-6, -1.3], both layers
- V: seals the right side, x = 11, y in [-10.5, 2.5], both layers
Before: (0,0) -> (0,-0.75) -> (-8,-0.75) -> (-8,-8) -> (8,-8) — a leap of exactly |8 - 0| = 8 to the left, overshooting F's face by 2mm and W's end by 6mm, then 16mm back. Move the goal to x=20 and the router instead hugs F, so route quality depended on where the goal happened to sit on the other axis.
After: (0,0) -> (0,-0.75) -> (-3,-0.75) -> (-3,-1.15) -> (-7,-1.15) -> (-7,-8) -> (8,-8) (hugs F at the wide-margin berth).
A second test records every neighbor getNeighbors emits and asserts none lands more than largestMargin + OBSTACLE_MARGIN outside the problem bounds; before the fix it caught e.g. (-3.00,-1.15) -> (-14.00,-1.15) and (8.00,0.00) -> (8.00,8.00).
Verification on the repo's dataset generators
single-trace-multilayer x60, single-trace x60, traces x40, distant-single-trace x30, keyboards x12 (2 layers). A neighbor/route point counts as "wild" when it is more than 1.15 outside the problem bounds:
| before | after | |
|---|---|---|
| cases with wild neighbors | 118 | 11 |
| wild neighbors / points | 7121 | 2193 |
| final routes leaving bounds | 55 | 15 |
| total route length | 12057.2 | 11934.6 |
| A* iterations | 7395 | 7099 |
| unsolved connections | 29 | 32 |
The +3 unsolved are all in traces#5 (43 connections in ~5.6x15mm); the three the old code "solved" did so via routes that left the board. keyboards #8/#9/#10 all got shorter. All existing multilayer snapshot tests are byte-identical, and the full suite passes (29/29).
Two related changes I measured and deliberately left out
- Adding the 2D routers' final
isObstacleAtneighbor filter toMultilayerIjump:ObstacleList3d.isObstacleAtdefaults its margin toGRID_STEP(0.1), so withOBSTACLE_MARGIN <= 0.1it discards legitimate wall-hugging neighbors — it fails the existingshorten-path-with-shortcuts-5test (OBSTACLE_MARGIN: 0.1) and makes keyboards #8/#9 ~52mm longer. getDistanceToOvercomeObstaclecallsobstacles.getObstacleAt(x, y)with no layer;ObstacleList3d.getObstacleAttakes(x, y, l), so it always returns null and conjoined-obstacle detection is silently dead in the multilayer router. Passing the layer through re-enables it but produced more far neighbors (2193 -> 5856), one more unsolved connection and a +5.5mm route ontraces#34, with no measurable upside, so I'm just noting it.
Ask
How would you like this landed? (a) republish infgrid-ijump-astar from a new home (move algos/ into tscircuit-autorouter or core), (b) vendor MultilayerIjump into core, or (c) something else — happy to open the PR wherever the code should live. tscircuit/autorouting#92 carries an Algora bounty that the archive makes unclaimable through the normal flow; if this fix is useful I'd appreciate it being considered.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Trace_doInitialSchematicTraceRender.ts and Trace_doInitialPcbTraceRender.ts, then inspect MultilayerIjump.getNeighbors in the cited autorouting fix branch and its package source. Confirm the snapshot and dataset-generator results, then get a maintainer decision on republishing, vendoring, or another landing location; done means the wild-jump regression is covered and the relevant suite passes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100