From 5477c344eec1aa725ec5fb2387a6a1c1c70bbd13 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Thu, 6 Aug 2026 21:56:35 +0530 Subject: [PATCH 1/6] up --- .../getComponentSideRailSegments.ts | 5 +- .../sameNetRailAlignment/getRailGroups.ts | 23 +++++++-- .../sameNetRailAlignment/types.ts | 1 + .../alignSameNetRails-vertical.test.ts | 51 ++++++++++++++++++- 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts b/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts index 5daf89127..9de2cdfaa 100644 --- a/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts +++ b/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts @@ -7,7 +7,7 @@ import type { RailSegment } from "./types" type UnassociatedRailSegment = Omit< RailSegment, - "componentId" | "componentFacingDirection" + "componentId" | "componentFacingDirection" | "isIntraComponentConnection" > const getMovableRailSegments = ( @@ -102,6 +102,9 @@ export const getComponentSideRailSegments = ( { componentId: chip.chipId, componentFacingDirection, + isIntraComponentConnection: trace.pins.every( + (tracePin) => tracePin.chipId === chip.chipId, + ), distanceFromEndpoint: pinIndex === 0 ? segment.segmentIndex diff --git a/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts b/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts index 61d7e698a..cab51db5c 100644 --- a/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts +++ b/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailGroups.ts @@ -61,6 +61,18 @@ const canJoinRailGroup = ( tracesSharePin(current, candidate, traceMap)) && corridorIsClear(current, candidate, obstacles) +const omitLocalLoopWhenExternalRailsCanAlign = (group: RailSegment[]) => { + const externalSegments = group.filter( + (segment) => !segment.isIntraComponentConnection, + ) + const externalTraceCount = new Set( + externalSegments.map((segment) => segment.traceId), + ).size + if (externalTraceCount < 2) return group + + return externalSegments +} + export const getRailGroups = ( traces: SolvedTracePath[], eligibleTraceIds: ReadonlySet, @@ -103,11 +115,14 @@ export const getRailGroups = ( } } - const traceCount = new Set(group.map((segment) => segment.traceId)).size - const hasDifferentCoordinates = group.some( - (segment) => !nearlyEqual(segment.coordinate, group[0]!.coordinate), + const candidateGroup = omitLocalLoopWhenExternalRailsCanAlign(group) + const traceCount = new Set(candidateGroup.map((segment) => segment.traceId)) + .size + const hasDifferentCoordinates = candidateGroup.some( + (segment) => + !nearlyEqual(segment.coordinate, candidateGroup[0]!.coordinate), ) - if (traceCount >= 2 && hasDifferentCoordinates) groups.push(group) + if (traceCount >= 2 && hasDifferentCoordinates) groups.push(candidateGroup) } return groups diff --git a/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/types.ts b/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/types.ts index beaf91d86..c5c3a8ac4 100644 --- a/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/types.ts +++ b/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/types.ts @@ -13,6 +13,7 @@ export interface RailSegment { maxAlong: number componentId: string componentFacingDirection: FacingDirection + isIntraComponentConnection: boolean } export interface TraceGeometryMetrics { diff --git a/tests/solvers/TraceCleanupSolver/alignSameNetRails-vertical.test.ts b/tests/solvers/TraceCleanupSolver/alignSameNetRails-vertical.test.ts index 041767ea5..f0e901ad5 100644 --- a/tests/solvers/TraceCleanupSolver/alignSameNetRails-vertical.test.ts +++ b/tests/solvers/TraceCleanupSolver/alignSameNetRails-vertical.test.ts @@ -1,5 +1,10 @@ import { expect, test } from "bun:test" -import { align, getVerticalRailTraces } from "./fixtures/alignSameNetRails" +import { + align, + createTrace, + getVerticalPin, + getVerticalRailTraces, +} from "./fixtures/alignSameNetRails" test("aligns same-net rails on one component side", () => { const result = align(getVerticalRailTraces()) @@ -23,3 +28,47 @@ test("aligns same-net rails on one component side", () => { ], ]) }) + +test("keeps a local pin loop separate from an external same-net rail", () => { + const localLoop = getVerticalRailTraces()[0]! + const upperExternal = createTrace( + "upper-external", + [ + { x: -1, y: 0 }, + { x: -2.5, y: 0 }, + { x: -2.5, y: 1 }, + { x: -4, y: 1 }, + ], + [getVerticalPin("U1.2"), { pinId: "C1.1", chipId: "C1", x: -4, y: 1 }], + ) + const lowerExternal = createTrace( + "lower-external", + [ + { x: -5, y: -1 }, + { x: -3, y: -1 }, + { x: -3, y: 0 }, + { x: -1, y: 0 }, + ], + [{ pinId: "C2.1", chipId: "C2", x: -5, y: -1 }, getVerticalPin("U1.2")], + ) + + const result = align([localLoop, upperExternal, lowerExternal]) + const alignedLocalLoop = result.traces.find( + (trace) => trace.mspPairId === localLoop.mspPairId, + )! + const alignedUpperExternal = result.traces.find( + (trace) => trace.mspPairId === upperExternal.mspPairId, + )! + const alignedLowerExternal = result.traces.find( + (trace) => trace.mspPairId === lowerExternal.mspPairId, + )! + + expect(result.alignedRailGroupCount).toBe(1) + expect(alignedLocalLoop.tracePath[1]!.x).toBe(-2) + expect(alignedUpperExternal.tracePath[1]!.x).toBe( + alignedLowerExternal.tracePath[2]!.x, + ) + expect(alignedUpperExternal.tracePath[1]!.x).not.toBe( + alignedLocalLoop.tracePath[1]!.x, + ) +}) From 456489a5b0804896d71cf8c0dc99bf22d90fdcd5 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Thu, 6 Aug 2026 22:02:21 +0530 Subject: [PATCH 2/6] up --- .../repro-focusbeam-v5-junctions.snap.svg | 99 ++++ .../repro-focusbeam-v5-junctions.input.json | 538 ++++++++++++++++++ .../repro-focusbeam-v5-junctions.test.ts | 31 + 3 files changed, 668 insertions(+) create mode 100644 tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg create mode 100644 tests/repros/assets/repro-focusbeam-v5-junctions.input.json create mode 100644 tests/repros/repro-focusbeam-v5-junctions.test.ts diff --git a/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg b/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg new file mode 100644 index 000000000..b589fed91 --- /dev/null +++ b/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg @@ -0,0 +1,99 @@ + \ No newline at end of file diff --git a/tests/repros/assets/repro-focusbeam-v5-junctions.input.json b/tests/repros/assets/repro-focusbeam-v5-junctions.input.json new file mode 100644 index 000000000..869419c8c --- /dev/null +++ b/tests/repros/assets/repro-focusbeam-v5-junctions.input.json @@ -0,0 +1,538 @@ +{ + "chips": [ + { + "chipId": "schematic_component_0", + "center": { + "x": -2.5299999999999994, + "y": 3.43 + }, + "width": 1.0400000000000005, + "height": 0.7600000000000007, + "pins": [ + { + "pinId": "schematic_port_0", + "x": -2.7249999999999996, + "y": 3.8100000000000005, + "_facingDirection": "y+" + }, + { + "pinId": "schematic_port_1", + "x": -2.7249999999999996, + "y": 3.05, + "_facingDirection": "y-" + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_1", + "center": { + "x": -3.3099999999999996, + "y": 5.535 + }, + "width": 2, + "height": 0.40000000000000036, + "pins": [ + { + "pinId": "schematic_port_2", + "x": -4.31, + "y": 5.535 + }, + { + "pinId": "schematic_port_3", + "x": -2.3099999999999996, + "y": 5.535 + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_2", + "center": { + "x": -2.1, + "y": 1.125 + }, + "width": 0.6000000000000005, + "height": 0.6799999999999999, + "pins": [ + { + "pinId": "schematic_port_4", + "x": -1.7999999999999998, + "y": 1.1250000000000002 + }, + { + "pinId": "schematic_port_5", + "x": -2.4000000000000004, + "y": 1.1250000000000002 + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_3", + "center": { + "x": 0.9074999999999999, + "y": -1.625 + }, + "width": 0.845, + "height": 0.6000000000000001, + "pins": [ + { + "pinId": "schematic_port_6", + "x": 0.8099999999999998, + "y": -1.325, + "_facingDirection": "y+" + }, + { + "pinId": "schematic_port_7", + "x": 0.8099999999999998, + "y": -1.925, + "_facingDirection": "y-" + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_4", + "center": { + "x": -1.9925, + "y": -0.575 + }, + "width": 0.8450000000000002, + "height": 0.6000000000000001, + "pins": [ + { + "pinId": "schematic_port_8", + "x": -2.09, + "y": -0.2749999999999999, + "_facingDirection": "y+" + }, + { + "pinId": "schematic_port_9", + "x": -2.09, + "y": -0.875, + "_facingDirection": "y-" + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_5", + "center": { + "x": -0.09999999999999964, + "y": 3.825 + }, + "width": 2, + "height": 0.5999999999999996, + "pins": [ + { + "pinId": "schematic_port_10", + "x": -1.0999999999999996, + "y": 4.025 + }, + { + "pinId": "schematic_port_11", + "x": -1.0999999999999996, + "y": 3.825 + }, + { + "pinId": "schematic_port_12", + "x": -1.0999999999999996, + "y": 3.625 + }, + { + "pinId": "schematic_port_13", + "x": 0.9000000000000004, + "y": 3.725 + }, + { + "pinId": "schematic_port_14", + "x": 0.9000000000000004, + "y": 3.9250000000000003 + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_6", + "center": { + "x": -0.5499999999999996, + "y": 6.029999999999999 + }, + "width": 0.9199999999999999, + "height": 0.7599999999999998, + "pins": [ + { + "pinId": "schematic_port_15", + "x": -0.6849999999999996, + "y": 6.409999999999999, + "_facingDirection": "y+" + }, + { + "pinId": "schematic_port_16", + "x": -0.6849999999999996, + "y": 5.6499999999999995, + "_facingDirection": "y-" + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_7", + "center": { + "x": 2.1, + "y": -0.6650000000000003 + }, + "width": 0.6000000000000005, + "height": 0.6800000000000002, + "pins": [ + { + "pinId": "schematic_port_17", + "x": 2.4000000000000004, + "y": -0.6650000000000003 + }, + { + "pinId": "schematic_port_18", + "x": 1.7999999999999998, + "y": -0.6650000000000003 + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_8", + "center": { + "x": -4.63, + "y": 3.4300000000000006 + }, + "width": 0.9199999999999999, + "height": 0.7600000000000007, + "pins": [ + { + "pinId": "schematic_port_19", + "x": -4.765, + "y": 3.810000000000001, + "_facingDirection": "y+" + }, + { + "pinId": "schematic_port_20", + "x": -4.765, + "y": 3.0500000000000003, + "_facingDirection": "y-" + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_9", + "center": { + "x": 0, + "y": 0.625 + }, + "width": 2.2, + "height": 1.7999999999999998, + "pins": [ + { + "pinId": "schematic_port_21", + "x": -1.1, + "y": 1.325 + }, + { + "pinId": "schematic_port_22", + "x": -1.1, + "y": 1.125 + }, + { + "pinId": "schematic_port_23", + "x": -1.1, + "y": 0.9249999999999999 + }, + { + "pinId": "schematic_port_24", + "x": -1.1, + "y": 0.7249999999999999 + }, + { + "pinId": "schematic_port_25", + "x": -1.1, + "y": 0.5249999999999999 + }, + { + "pinId": "schematic_port_26", + "x": -1.1, + "y": 0.32499999999999996 + }, + { + "pinId": "schematic_port_27", + "x": -1.1, + "y": 0.125 + }, + { + "pinId": "schematic_port_28", + "x": -1.1, + "y": -0.07499999999999996 + }, + { + "pinId": "schematic_port_29", + "x": 1.1, + "y": -0.07499999999999996 + }, + { + "pinId": "schematic_port_30", + "x": 1.1, + "y": 0.12500000000000006 + }, + { + "pinId": "schematic_port_31", + "x": 1.1, + "y": 0.32500000000000007 + }, + { + "pinId": "schematic_port_32", + "x": 1.1, + "y": 0.5250000000000001 + }, + { + "pinId": "schematic_port_33", + "x": 1.1, + "y": 0.7250000000000001 + }, + { + "pinId": "schematic_port_34", + "x": 1.1, + "y": 0.925 + }, + { + "pinId": "schematic_port_35", + "x": 1.1, + "y": 1.125 + }, + { + "pinId": "schematic_port_36", + "x": 1.1, + "y": 1.325 + } + ], + "sectionId": "PowerInput" + }, + { + "chipId": "schematic_component_10", + "center": { + "x": 2.1875, + "y": 0.625 + }, + "width": 0.845, + "height": 0.6000000000000001, + "pins": [ + { + "pinId": "schematic_port_37", + "x": 2.09, + "y": 0.925, + "_facingDirection": "y+" + }, + { + "pinId": "schematic_port_38", + "x": 2.09, + "y": 0.32499999999999996, + "_facingDirection": "y-" + } + ], + "sectionId": "PowerInput" + } + ], + "directConnections": [ + { + "netId": ".R16 > .pin1 to .P1 > .S1", + "pinIds": [ + "schematic_port_4", + "schematic_port_21" + ] + }, + { + "netId": ".R22 > .pin1 to .P1 > .A7", + "pinIds": [ + "schematic_port_6", + "schematic_port_31" + ] + }, + { + "netId": ".R21 > .pin2 to .P1 > .A6", + "pinIds": [ + "schematic_port_18", + "schematic_port_30" + ] + }, + { + "netId": ".P1 > .A5 to .R17 > .pin1", + "pinIds": [ + "schematic_port_28", + "schematic_port_8" + ] + }, + { + "netId": ".P1 > .B5 to .R18 > .pin1", + "pinIds": [ + "schematic_port_34", + "schematic_port_37" + ] + }, + { + "netId": ".P1 > .B6 to .P1 > .A6", + "pinIds": [ + "schematic_port_32", + "schematic_port_30" + ] + }, + { + "netId": ".P1 > .B7 to .P1 > .A7", + "pinIds": [ + "schematic_port_29", + "schematic_port_31" + ] + } + ], + "netConnections": [ + { + "netId": "V5", + "netLabelWidth": 0.42, + "netLabelHeight": 0.36, + "pinIds": [ + "schematic_port_0", + "schematic_port_2", + "schematic_port_10", + "schematic_port_12", + "schematic_port_19", + "schematic_port_26", + "schematic_port_35" + ] + }, + { + "netId": "GND", + "netLabelWidth": 0.42, + "netLabelHeight": 0.48, + "pinIds": [ + "schematic_port_1", + "schematic_port_3", + "schematic_port_4", + "schematic_port_5", + "schematic_port_9", + "schematic_port_11", + "schematic_port_16", + "schematic_port_20", + "schematic_port_21", + "schematic_port_22", + "schematic_port_23", + "schematic_port_24", + "schematic_port_25", + "schematic_port_36", + "schematic_port_38" + ] + }, + { + "netId": "FB_N20", + "netLabelWidth": 0.84, + "pinIds": [ + "schematic_port_7" + ] + }, + { + "netId": "V3V3", + "netLabelWidth": 0.42, + "netLabelHeight": 0.6, + "pinIds": [ + "schematic_port_14", + "schematic_port_15" + ] + }, + { + "netId": "FB_N44", + "netLabelWidth": 0.84, + "pinIds": [ + "schematic_port_17" + ] + } + ], + "textBoxes": [ + { + "chipId": "schematic_component_1", + "center": { + "x": -3.0699999999999994, + "y": 5.205 + }, + "width": 1.6800000000000002, + "height": 0.17999999999999972, + "text": "TPD1E10B06DPYR" + }, + { + "chipId": "schematic_component_1", + "center": { + "x": -3.79, + "y": 5.85 + }, + "width": 0.3599999999999999, + "height": 0.2499999999999991, + "text": "D5" + }, + { + "chipId": "schematic_component_5", + "center": { + "x": -0.039999999999999813, + "y": 3.395 + }, + "width": 1.3199999999999998, + "height": 0.17999999999999972, + "text": "AP2205-W5-7" + }, + { + "chipId": "schematic_component_5", + "center": { + "x": -0.5799999999999997, + "y": 4.24 + }, + "width": 0.36000000000000004, + "height": 0.2499999999999991, + "text": "U6" + }, + { + "chipId": "schematic_component_9", + "center": { + "x": 0.25999999999999984, + "y": -0.4049999999999998 + }, + "width": 1.92, + "height": 0.17999999999999994, + "text": "USB4105-GF-A-060" + }, + { + "chipId": "schematic_component_9", + "center": { + "x": -0.5800000000000001, + "y": 1.6400000000000001 + }, + "width": 0.35999999999999993, + "height": 0.2500000000000002, + "text": "P1" + } + ], + "availableNetLabelOrientations": { + "GND": [ + "y-" + ], + "V5": [ + "y+" + ], + "V3V3": [ + "y+" + ], + "FB_N20": [ + "x-", + "x+" + ], + "FB_N44": [ + "x-", + "x+" + ] + }, + "maxMspPairDistance": 5, + "_hideRatsNet": false +} diff --git a/tests/repros/repro-focusbeam-v5-junctions.test.ts b/tests/repros/repro-focusbeam-v5-junctions.test.ts new file mode 100644 index 000000000..48f0affb1 --- /dev/null +++ b/tests/repros/repro-focusbeam-v5-junctions.test.ts @@ -0,0 +1,31 @@ +import { expect, test } from "bun:test" +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" +import type { InputProblem } from "lib/types/InputProblem" +import inputProblemJson from "./assets/repro-focusbeam-v5-junctions.input.json" +import "tests/fixtures/matcher" + +const LOCAL_V5_PAIR_ID = "schematic_port_10-schematic_port_12" +const EXTERNAL_V5_PAIR_ID = "schematic_port_12-schematic_port_0" + +test("keeps the FocusBeam local V5 loop separate from external rails", () => { + const inputProblem: InputProblem = JSON.parse( + JSON.stringify(inputProblemJson), + ) + const solver = new SchematicTracePipelineSolver(inputProblem) + + solver.solve() + + const outputTraces = solver.sameNetJunctionAlignmentSolver!.outputTraces + const localV5Trace = outputTraces.find( + (trace) => trace.mspPairId === LOCAL_V5_PAIR_ID, + )! + const externalV5Trace = outputTraces.find( + (trace) => trace.mspPairId === EXTERNAL_V5_PAIR_ID, + )! + + expect(localV5Trace.tracePath[1]!.x).toBeCloseTo(-1.2) + expect(externalV5Trace.tracePath[1]!.x).not.toBeCloseTo( + localV5Trace.tracePath[1]!.x, + ) + expect(solver).toMatchSolverSnapshot(import.meta.path) +}) From 52ab3cf4b855ec624d4b6e2d79c4805018e1a602 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Thu, 6 Aug 2026 22:22:48 +0530 Subject: [PATCH 3/6] up --- .../repro-focusbeam-v5-junctions.snap.svg | 59 +------------------ .../repro-focusbeam-v5-junctions.test.ts | 44 +++++++++++++- 2 files changed, 45 insertions(+), 58 deletions(-) diff --git a/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg b/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg index b589fed91..19ef11faa 100644 --- a/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg +++ b/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg @@ -1,59 +1,4 @@ - \ No newline at end of file + ]]> From 3a3aca709678b60ac5a6738905c20a243b3e14d1 Mon Sep 17 00:00:00 2001 From: mohan-bee Date: Thu, 6 Aug 2026 22:48:47 +0530 Subject: [PATCH 6/6] up --- .../__snapshots__/component-5-v5.snap.svg | 44 +++++++++ .../__snapshots__/component-9-gnd.snap.svg | 44 +++++++++ .../repro-focusbeam-v5-junctions.snap.svg | 44 --------- .../repro-focusbeam-v5-junctions.test.ts | 97 +++++++++++++------ 4 files changed, 157 insertions(+), 72 deletions(-) create mode 100644 tests/repros/__snapshots__/component-5-v5.snap.svg create mode 100644 tests/repros/__snapshots__/component-9-gnd.snap.svg delete mode 100644 tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg diff --git a/tests/repros/__snapshots__/component-5-v5.snap.svg b/tests/repros/__snapshots__/component-5-v5.snap.svg new file mode 100644 index 000000000..95e22c8cf --- /dev/null +++ b/tests/repros/__snapshots__/component-5-v5.snap.svg @@ -0,0 +1,44 @@ +Component 5V5: red = replacement, green = hub branchport 10port 12 diff --git a/tests/repros/__snapshots__/component-9-gnd.snap.svg b/tests/repros/__snapshots__/component-9-gnd.snap.svg new file mode 100644 index 000000000..02fae8d3f --- /dev/null +++ b/tests/repros/__snapshots__/component-9-gnd.snap.svg @@ -0,0 +1,44 @@ +Component 9GND: red = replacement, green = hub branchport 21port 22 diff --git a/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg b/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg deleted file mode 100644 index 0c0373150..000000000 --- a/tests/repros/__snapshots__/repro-focusbeam-v5-junctions.snap.svg +++ /dev/null @@ -1,44 +0,0 @@ - diff --git a/tests/repros/repro-focusbeam-v5-junctions.test.ts b/tests/repros/repro-focusbeam-v5-junctions.test.ts index 5747b3a5d..a20534dfa 100644 --- a/tests/repros/repro-focusbeam-v5-junctions.test.ts +++ b/tests/repros/repro-focusbeam-v5-junctions.test.ts @@ -2,7 +2,7 @@ import { expect, test } from "bun:test" import { getSvgFromGraphicsObject, type GraphicsObject } from "graphics-debug" import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver" -import type { InputProblem } from "lib/types/InputProblem" +import type { InputChip, InputProblem, PinId } from "lib/types/InputProblem" import inputProblemJson from "./assets/repro-focusbeam-v5-junctions.input.json" import "tests/fixtures/matcher" @@ -14,43 +14,68 @@ const REPLACEMENT_PAIR_IDS = new Set([ "schematic_port_10-schematic_port_0", "schematic_port_21-schematic_port_4", ]) -const FOCUS_COMPONENT_IDS = new Set([ - "schematic_component_5", - "schematic_component_9", -]) - -const getFocusedTraceColor = (trace: SolvedTracePath) => { - if (REPLACEMENT_PAIR_IDS.has(trace.mspPairId)) return "#dc2626" - return "#15803d" -} - -const getFocusedSvg = ({ - inputProblem, +const getComponentNetSvg = ({ + chip, + netId, + focusPinIds, traces, }: { - inputProblem: InputProblem + chip: InputChip + netId: string + focusPinIds: Set traces: SolvedTracePath[] }) => { + const focusPins = chip.pins.filter((pin) => focusPinIds.has(pin.pinId)) + const focusTraces = traces.filter((trace) => + trace.pins.some((pin) => focusPinIds.has(pin.pinId)), + ) const graphics: GraphicsObject = { - rects: inputProblem.chips - .filter((chip) => FOCUS_COMPONENT_IDS.has(chip.chipId)) - .map((chip) => ({ + rects: [ + { center: chip.center, width: chip.width, height: chip.height, fill: "#fff7ed", - strokeColor: "#9a3412", - label: chip.chipId, - })), - lines: traces - .filter((trace) => - trace.pins.some((pin) => FOCUS_COMPONENT_IDS.has(pin.chipId)), - ) - .map((trace) => ({ + }, + ], + lines: focusTraces.map((trace) => { + let strokeColor = "#15803d" + if (REPLACEMENT_PAIR_IDS.has(trace.mspPairId)) strokeColor = "#dc2626" + return { points: trace.tracePath, - strokeColor: getFocusedTraceColor(trace), + strokeColor, + strokeWidth: 0.025, label: trace.mspPairId, + } + }), + circles: focusPins.map((pin) => ({ + center: pin, + radius: 0.035, + fill: "#111827", + })), + texts: [ + { + x: chip.center.x, + y: chip.center.y, + text: chip.chipId.replace("schematic_component_", "Component "), + fontSize: 0.14, + color: "#7c2d12", + }, + { + x: chip.center.x, + y: chip.center.y + chip.height / 2 + 0.22, + text: `${netId}: red = replacement, green = hub branch`, + fontSize: 0.12, + color: "#111827", + }, + ...focusPins.map((pin) => ({ + x: pin.x - 0.08, + y: pin.y + 0.06, + text: pin.pinId.replace("schematic_port_", "port "), + fontSize: 0.1, + color: "#111827", })), + ], } return getSvgFromGraphicsObject(graphics, { backgroundColor: "white" }) @@ -74,6 +99,22 @@ test("replaces FocusBeam local leaf-to-junction loops", () => { expect(localTraces).toHaveLength(0) expect(replacementTraces).toHaveLength(2) - const focusedSvg = getFocusedSvg({ inputProblem, traces: outputTraces }) - expect(focusedSvg).toMatchSvgSnapshot(import.meta.path) + const component5Svg = getComponentNetSvg({ + chip: inputProblem.chips.find( + (chip) => chip.chipId === "schematic_component_5", + )!, + netId: "V5", + focusPinIds: new Set(["schematic_port_10", "schematic_port_12"]), + traces: outputTraces, + }) + const component9Svg = getComponentNetSvg({ + chip: inputProblem.chips.find( + (chip) => chip.chipId === "schematic_component_9", + )!, + netId: "GND", + focusPinIds: new Set(["schematic_port_21", "schematic_port_22"]), + traces: outputTraces, + }) + expect(component5Svg).toMatchSvgSnapshot(import.meta.path, "component-5-v5") + expect(component9Svg).toMatchSvgSnapshot(import.meta.path, "component-9-gnd") })