From ca9aec8f12098ab1471c164d0bbd168542fe0027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=B8=80=E8=AF=BA?= Date: Fri, 10 Apr 2026 16:50:18 +0800 Subject: [PATCH 1/2] fix: skip MSP pair generation for net-label-only nets (issue #79) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nets that exist only in netConnections and have availableNetLabelOrientations were incorrectly being queued for wire-trace generation, producing spurious traces alongside the net labels that represent them. Root cause: MspConnectionPairSolver used Object.keys(netConnMap.netMap) to build queuedDcNetIds, which includes all nets — including ones that should only be shown as net labels. Fix: filter queuedDcNetIds to only include nets that need wire traces: - nets with at least one pin in directConnections (always need a trace), OR - nets with no configured label orientation (no label → trace is the only representation of the connection) Nets that are purely in netConnections AND have availableNetLabelOrientations are skipped — NetLabelPlacementSolver handles them instead. This is stricter than filtering on directlyWiredPinIds alone (as done in PR #162): that approach incorrectly removes traces for netConnections nets that have no label orientation configured, leaving the connection invisible. Changes: - MspConnectionPairSolver.ts: add directlyWiredPinIds + netLabelOrientedNets filter for queuedDcNetIds - MspConnectionPairSolver_repro1.test.ts: update expected pair count 4 → 2 (GND is net-label-only in that fixture) - Add MspConnectionPairSolver_issue79.test.ts: 3 targeted unit tests - Add SchematicTracePipelineSolver_repro_issue79.test.ts: full pipeline repro - Add SchematicTracePipelineSolver_issue79.page.tsx: demo page - Update 7 SVG snapshots (spurious GND/VCC traces removed) Co-Authored-By: Claude Sonnet 4.6 --- .../MspConnectionPairSolver.ts | 35 ++++- ...ematicTracePipelineSolver_issue79.page.tsx | 64 +++++++++ .../examples/__snapshots__/example01.snap.svg | 9 +- .../examples/__snapshots__/example12.snap.svg | 60 ++++---- .../examples/__snapshots__/example13.snap.svg | 23 +-- .../examples/__snapshots__/example15.snap.svg | 56 +++++--- .../examples/__snapshots__/example16.snap.svg | 57 ++++---- .../examples/__snapshots__/example21.snap.svg | 133 +++++++++--------- .../examples/__snapshots__/example22.snap.svg | 46 +++--- .../MspConnectionPairSolver_issue79.test.ts | 110 +++++++++++++++ .../MspConnectionPairSolver_repro1.test.ts | 5 +- ...cTracePipelineSolver_repro_issue79.test.ts | 116 +++++++++++++++ 12 files changed, 532 insertions(+), 182 deletions(-) create mode 100644 site/SchematicTracePipelineSolver/SchematicTracePipelineSolver_issue79.page.tsx create mode 100644 tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_issue79.test.ts create mode 100644 tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro_issue79.test.ts diff --git a/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts b/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts index 48b46c90..d7b53162 100644 --- a/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +++ b/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts @@ -74,7 +74,40 @@ export class MspConnectionPairSolver extends BaseSolver { } } - this.queuedDcNetIds = Object.keys(netConnMap.netMap) + // Build a set of all pinIds that have explicit direct wire connections. + const directlyWiredPinIds = new Set() + for (const dc of inputProblem.directConnections) { + for (const pid of dc.pinIds) { + directlyWiredPinIds.add(pid) + } + } + + // Build a set of nets that have configured net-label orientations. + // These nets will be represented by net labels in the schematic, not wire + // traces — but only when none of their pins appear in directConnections. + const netLabelOrientedNets = new Set( + Object.keys(inputProblem.availableNetLabelOrientations ?? {}), + ) + + // Only queue nets that need wire traces: + // • Nets with at least one directly-wired pin always need a trace. + // • Nets with NO directly-wired pins but also NO label orientations still + // need a trace (there is no other way to show the connection). + // • Nets with NO directly-wired pins AND a label orientation are skipped + // because NetLabelPlacementSolver will place the label instead. + this.queuedDcNetIds = Object.keys(netConnMap.netMap).filter((netId) => { + const connectedIds = netConnMap.getIdsConnectedToNet(netId) as string[] + const hasDirect = connectedIds.some((id) => directlyWiredPinIds.has(id)) + if (hasDirect) return true + // connectedIds includes the user-provided net label string (e.g. "GND") + // because ConnectivityMap treats the net label as just another member of + // the group. Check whether any connected ID is a net with a configured + // label orientation — if so, NetLabelPlacementSolver will handle it. + const hasLabelOrientation = connectedIds.some((id) => + netLabelOrientedNets.has(id), + ) + return !hasLabelOrientation + }) } override getConstructorParams(): ConstructorParameters< diff --git a/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver_issue79.page.tsx b/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver_issue79.page.tsx new file mode 100644 index 00000000..97492684 --- /dev/null +++ b/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver_issue79.page.tsx @@ -0,0 +1,64 @@ +/** + * Demo page for issue #79: + * "Fix extra net label in repro61, or remove trace" + * + * This circuit has VCC and GND connected exclusively via netConnections + * with availableNetLabelOrientations. Before the fix both nets also + * produced spurious wire traces on top of their net labels. After the + * fix only the two signal traces (U1.out→R1.pin1, U1.sigA→R1.pin2) + * are drawn; VCC and GND are shown by net labels only. + */ +import { PipelineDebugger } from "site/components/PipelineDebugger" +import type { InputProblem } from "lib/types/InputProblem" + +export const inputProblem: InputProblem = { + chips: [ + { + chipId: "U1", + center: { x: 0, y: 0 }, + width: 2.4, + height: 1.0, + pins: [ + { pinId: "U1.1", x: -1.2, y: 0.3 }, + { pinId: "U1.2", x: -1.2, y: 0.1 }, + { pinId: "U1.3", x: -1.2, y: -0.3 }, + { pinId: "U1.4", x: 1.2, y: 0.3 }, + ], + }, + { + chipId: "R1", + center: { x: 2.5, y: 0.3 }, + width: 1.0, + height: 0.4, + pins: [ + { pinId: "R1.1", x: 2.0, y: 0.3 }, + { pinId: "R1.2", x: 3.0, y: 0.3 }, + ], + }, + { + chipId: "C1", + center: { x: -2.5, y: -0.3 }, + width: 0.5, + height: 0.8, + pins: [ + { pinId: "C1.1", x: -2.5, y: 0.1 }, + { pinId: "C1.2", x: -2.5, y: -0.7 }, + ], + }, + ], + directConnections: [ + { pinIds: ["U1.4", "R1.1"], netId: "U1.out to R1.pin1" }, + { pinIds: ["U1.2", "R1.2"], netId: "U1.sigA to R1.pin2" }, + ], + netConnections: [ + { netId: "VCC", pinIds: ["U1.1", "C1.1"] }, + { netId: "GND", pinIds: ["U1.3", "C1.2"] }, + ], + availableNetLabelOrientations: { + VCC: ["y+"], + GND: ["y-"], + }, + maxMspPairDistance: 5, +} + +export default () => diff --git a/tests/examples/__snapshots__/example01.snap.svg b/tests/examples/__snapshots__/example01.snap.svg index 293bf05a..8f364a31 100644 --- a/tests/examples/__snapshots__/example01.snap.svg +++ b/tests/examples/__snapshots__/example01.snap.svg @@ -49,6 +49,9 @@ y-" data-x="-4" data-y="-0.5" cx="67.72277227722776" cy="356.03960396039605" r=" + + + @@ -73,9 +76,6 @@ y-" data-x="-4" data-y="-0.5" cx="67.72277227722776" cy="356.03960396039605" r=" - - - @@ -94,6 +94,9 @@ y-" data-x="-4" data-y="-0.5" cx="67.72277227722776" cy="356.03960396039605" r=" + + + - + + + + - + diff --git a/tests/examples/__snapshots__/example15.snap.svg b/tests/examples/__snapshots__/example15.snap.svg index fa7b2fbe..8bf97bb0 100644 --- a/tests/examples/__snapshots__/example15.snap.svg +++ b/tests/examples/__snapshots__/example15.snap.svg @@ -335,13 +335,25 @@ y-" data-x="-2.025" data-y="-2.7" cx="318.5204755614267" cy="526.0237780713342" - + - + - + + + + + + + + + + + + + @@ -812,28 +824,16 @@ y-" data-x="-2.025" data-y="-2.7" cx="318.5204755614267" cy="526.0237780713342" - - - - - - - + - + - + - - - - - - - + @@ -890,13 +890,25 @@ y-" data-x="-2.025" data-y="-2.7" cx="318.5204755614267" cy="526.0237780713342" - + + + + + + + + + + + + + - + - +