Support immutable routes during trace simplification - #1780
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Benchmark This PRRun benchmarks by commenting on this PR: Comment Everything after Use Any PR whose title contains |
| test("trace simplification avoids immutable routed traces without emitting or mutating them", () => { | ||
| const immutableSnapshot = structuredClone(immutableRoute) | ||
|
|
||
| const routesWithoutFixedCopper = solve() | ||
| expect(routesWithoutFixedCopper).toHaveLength(1) | ||
| expect( | ||
| getMinimumRouteDistance(routesWithoutFixedCopper[0]!, immutableRoute), | ||
| ).toBe(0) | ||
|
|
||
| const routesWithFixedCopper = solve([immutableRoute]) | ||
| expect(routesWithFixedCopper).toHaveLength(1) | ||
| expect(routesWithFixedCopper[0]!.connectionName).toBe("editable") | ||
| expect( | ||
| getMinimumRouteDistance(routesWithFixedCopper[0]!, immutableRoute), | ||
| ).toBeGreaterThanOrEqual(0.25) | ||
| expect(immutableRoute).toEqual(immutableSnapshot) | ||
| }) | ||
|
|
||
| test("trace simplification visualizes immutable routed peers", () => { | ||
| const solver = createTraceSimplifier([immutableRoute]) | ||
| solver.solve() | ||
|
|
||
| expect(solver.failed).toBe(false) | ||
| const simplifiedRoute = solver.simplifiedHdRoutes[0]! | ||
| expect(editableRoute.route).toHaveLength(9) | ||
| expect(simplifiedRoute.route).toHaveLength(5) | ||
|
|
||
| const immutableGraphics = getRouteGraphics({ | ||
| route: immutableRoute, | ||
| strokeColor: "#3f3f46", | ||
| strokeWidth: 0.16, | ||
| strokeDash: [0.06, 0.06], | ||
| pointFill: "#d4d4d8", | ||
| }) | ||
| const inputGraphics = mergeGraphics( | ||
| getRouteGraphics({ | ||
| route: editableRoute, | ||
| strokeColor: "#d97706", | ||
| strokeWidth: 0.12, | ||
| pointFill: "#ffedd5", | ||
| }), | ||
| immutableGraphics, | ||
| ) | ||
| const outputGraphics = mergeGraphics( | ||
| getRouteGraphics({ | ||
| route: editableRoute, | ||
| strokeColor: "rgba(217, 119, 6, 0.28)", | ||
| strokeWidth: 0.06, | ||
| strokeDash: [0.05, 0.05], | ||
| }), | ||
| { | ||
| lines: [ | ||
| { | ||
| points: [ | ||
| { x: -2, y: 0 }, | ||
| { x: 2, y: 0 }, | ||
| ], | ||
| strokeColor: "#dc2626", | ||
| strokeWidth: 0.045, | ||
| strokeDash: [0.08, 0.08], | ||
| }, | ||
| { | ||
| points: [ | ||
| { x: -0.11, y: -0.11 }, | ||
| { x: 0.11, y: 0.11 }, | ||
| ], | ||
| strokeColor: "#dc2626", | ||
| strokeWidth: 0.055, | ||
| }, | ||
| { | ||
| points: [ | ||
| { x: -0.11, y: 0.11 }, | ||
| { x: 0.11, y: -0.11 }, | ||
| ], | ||
| strokeColor: "#dc2626", | ||
| strokeWidth: 0.055, | ||
| }, | ||
| ], | ||
| }, | ||
| getRouteGraphics({ | ||
| route: simplifiedRoute, | ||
| strokeColor: "#16803c", | ||
| strokeWidth: 0.14, | ||
| pointFill: "#dcfce7", | ||
| }), | ||
| immutableGraphics, | ||
| ) | ||
|
|
||
| const renderPanel = (graphics: GraphicsObject) => | ||
| getSvgFromGraphicsObject(graphics, { | ||
| backgroundColor: "white", | ||
| svgWidth: 560, | ||
| svgHeight: 420, | ||
| hideInlineLabels: true, | ||
| }) | ||
|
|
||
| expect( | ||
| stackSvgsHorizontally( | ||
| [ | ||
| addPanelHeader({ | ||
| svg: renderPanel(inputGraphics), | ||
| title: "BEFORE • UNSIMPLIFIED EDITABLE ROUTE", | ||
| details: [ | ||
| "9 orange points are eligible for simplification.", | ||
| "Gray dashed copper is pre-routed and read-only.", | ||
| ], | ||
| }), | ||
| addPanelHeader({ | ||
| svg: renderPanel(outputGraphics), | ||
| title: "AFTER • SIMPLIFIED AROUND FIXED COPPER", | ||
| details: [ | ||
| "5 green points remain (4 removed); faint orange = input.", | ||
| "Red direct shortcut was rejected; gray copper is unchanged.", | ||
| ], | ||
| }), | ||
| ], | ||
| { gap: 12, normalizeSize: false }, | ||
| ), | ||
| ).toMatchSvgSnapshot(import.meta.path, { | ||
| svgName: "immutable-routed-peer", | ||
| }) | ||
| }) |
There was a problem hiding this comment.
This file contains 4 test(...) calls (at lines 164, 182, 287, and 329), but the rule states that a *.test.ts file may have AT MOST one test(...). After the first test, the remaining tests should be split into separate, numbered files. For example: trace-simplification-immutable-routes1.test.ts, trace-simplification-immutable-routes2.test.ts, trace-simplification-immutable-routes3.test.ts, and trace-simplification-immutable-routes4.test.ts.
| test("trace simplification avoids immutable routed traces without emitting or mutating them", () => { | |
| const immutableSnapshot = structuredClone(immutableRoute) | |
| const routesWithoutFixedCopper = solve() | |
| expect(routesWithoutFixedCopper).toHaveLength(1) | |
| expect( | |
| getMinimumRouteDistance(routesWithoutFixedCopper[0]!, immutableRoute), | |
| ).toBe(0) | |
| const routesWithFixedCopper = solve([immutableRoute]) | |
| expect(routesWithFixedCopper).toHaveLength(1) | |
| expect(routesWithFixedCopper[0]!.connectionName).toBe("editable") | |
| expect( | |
| getMinimumRouteDistance(routesWithFixedCopper[0]!, immutableRoute), | |
| ).toBeGreaterThanOrEqual(0.25) | |
| expect(immutableRoute).toEqual(immutableSnapshot) | |
| }) | |
| test("trace simplification visualizes immutable routed peers", () => { | |
| const solver = createTraceSimplifier([immutableRoute]) | |
| solver.solve() | |
| expect(solver.failed).toBe(false) | |
| const simplifiedRoute = solver.simplifiedHdRoutes[0]! | |
| expect(editableRoute.route).toHaveLength(9) | |
| expect(simplifiedRoute.route).toHaveLength(5) | |
| const immutableGraphics = getRouteGraphics({ | |
| route: immutableRoute, | |
| strokeColor: "#3f3f46", | |
| strokeWidth: 0.16, | |
| strokeDash: [0.06, 0.06], | |
| pointFill: "#d4d4d8", | |
| }) | |
| const inputGraphics = mergeGraphics( | |
| getRouteGraphics({ | |
| route: editableRoute, | |
| strokeColor: "#d97706", | |
| strokeWidth: 0.12, | |
| pointFill: "#ffedd5", | |
| }), | |
| immutableGraphics, | |
| ) | |
| const outputGraphics = mergeGraphics( | |
| getRouteGraphics({ | |
| route: editableRoute, | |
| strokeColor: "rgba(217, 119, 6, 0.28)", | |
| strokeWidth: 0.06, | |
| strokeDash: [0.05, 0.05], | |
| }), | |
| { | |
| lines: [ | |
| { | |
| points: [ | |
| { x: -2, y: 0 }, | |
| { x: 2, y: 0 }, | |
| ], | |
| strokeColor: "#dc2626", | |
| strokeWidth: 0.045, | |
| strokeDash: [0.08, 0.08], | |
| }, | |
| { | |
| points: [ | |
| { x: -0.11, y: -0.11 }, | |
| { x: 0.11, y: 0.11 }, | |
| ], | |
| strokeColor: "#dc2626", | |
| strokeWidth: 0.055, | |
| }, | |
| { | |
| points: [ | |
| { x: -0.11, y: 0.11 }, | |
| { x: 0.11, y: -0.11 }, | |
| ], | |
| strokeColor: "#dc2626", | |
| strokeWidth: 0.055, | |
| }, | |
| ], | |
| }, | |
| getRouteGraphics({ | |
| route: simplifiedRoute, | |
| strokeColor: "#16803c", | |
| strokeWidth: 0.14, | |
| pointFill: "#dcfce7", | |
| }), | |
| immutableGraphics, | |
| ) | |
| const renderPanel = (graphics: GraphicsObject) => | |
| getSvgFromGraphicsObject(graphics, { | |
| backgroundColor: "white", | |
| svgWidth: 560, | |
| svgHeight: 420, | |
| hideInlineLabels: true, | |
| }) | |
| expect( | |
| stackSvgsHorizontally( | |
| [ | |
| addPanelHeader({ | |
| svg: renderPanel(inputGraphics), | |
| title: "BEFORE • UNSIMPLIFIED EDITABLE ROUTE", | |
| details: [ | |
| "9 orange points are eligible for simplification.", | |
| "Gray dashed copper is pre-routed and read-only.", | |
| ], | |
| }), | |
| addPanelHeader({ | |
| svg: renderPanel(outputGraphics), | |
| title: "AFTER • SIMPLIFIED AROUND FIXED COPPER", | |
| details: [ | |
| "5 green points remain (4 removed); faint orange = input.", | |
| "Red direct shortcut was rejected; gray copper is unchanged.", | |
| ], | |
| }), | |
| ], | |
| { gap: 12, normalizeSize: false }, | |
| ), | |
| ).toMatchSvgSnapshot(import.meta.path, { | |
| svgName: "immutable-routed-peer", | |
| }) | |
| }) | |
| test("trace simplification avoids immutable routed traces without emitting or mutating them", () => { | |
| const immutableSnapshot = structuredClone(immutableRoute) | |
| const routesWithoutFixedCopper = solve() | |
| expect(routesWithoutFixedCopper).toHaveLength(1) | |
| expect( | |
| getMinimumRouteDistance(routesWithoutFixedCopper[0]!, immutableRoute), | |
| ).toBe(0) | |
| const routesWithFixedCopper = solve([immutableRoute]) | |
| expect(routesWithFixedCopper).toHaveLength(1) | |
| expect(routesWithFixedCopper[0]!.connectionName).toBe("editable") | |
| expect( | |
| getMinimumRouteDistance(routesWithFixedCopper[0]!, immutableRoute), | |
| ).toBeGreaterThanOrEqual(0.25) | |
| expect(immutableRoute).toEqual(immutableSnapshot) | |
| }) | |
Spotted by Graphite (based on custom rule: Custom rule)
Is this helpful? React 👍 or 👎 to let us know.
|
Thank you for your contribution! 🎉 PR Rating: ⭐⭐⭐ Track your contributions and see the leaderboard at: tscircuit Contribution Tracker |
Summary
otherHdRoutesinput toTraceSimplificationSolverrootConnectionNameWhy
A simplifier sometimes needs to avoid copper that is already routed but must not modify it. Previously the only way for a route to participate in simplification collision checks was to include it in the editable route list. Pipeline9 exposed this gap for preloaded traces, but this change is generic and has no Pipeline9 dependency.
This is the first independently mergeable prerequisite extracted from #1755.
Validation
bun test tests/features/trace-simplification-immutable-routes.test.ts tests/bugs/simplification-trace-overlap-repro.test.ts tests/solvers/same-net-via-merger-direct-overlap-star.test.ts tests/pipeline7-trace-simplification-net-colors.test.tsbunx tsc --noEmitbun run buildgit diff --checktrace-simplification-immutable-routes-immutable-routed-peer.snap.svgThe focused regression separately verifies path shortening, two-via layer-collapse removal, same-net via merging, immutability, output exclusion, and completed visualization.