From c8a55350dcdf40a1cdcfe042f853e4da08e2f063 Mon Sep 17 00:00:00 2001 From: ShiboSoftwareDev Date: Thu, 27 Aug 2026 00:30:44 +0200 Subject: [PATCH] Untangle generated net-label connector crossings --- .../SchematicTracePipelineSolver.ts | 36 +++++++++++++++++-- .../TraceCleanupSolver/TraceCleanupSolver.ts | 2 ++ .../sub-solver/UntangleTraceSubsolver.ts | 6 ++++ .../repro-pmp11282-isolated-dcdc.snap.svg | 4 +-- ...-netlabel-connector-crosses-trace.snap.svg | 4 +-- ...2-netlabel-connector-crosses-trace.test.ts | 4 +-- 6 files changed, 47 insertions(+), 9 deletions(-) diff --git a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts index 6a7591d21..bca412ece 100644 --- a/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +++ b/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts @@ -93,6 +93,7 @@ export class SchematicTracePipelineSolver extends BaseSolver { example28Solver?: Example28Solver availableNetOrientationSolver?: AvailableNetOrientationSolver postLabelTraceOverlapShiftSolver?: TraceOverlapShiftSolver + postLabelTraceCleanupSolver?: TraceCleanupSolver railNetLabelCornerPlacementSolver?: RailNetLabelCornerPlacementSolver traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver preAlignmentNetLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver @@ -394,6 +395,37 @@ export class SchematicTracePipelineSolver extends BaseSolver { }, ], ), + definePipelineStep( + "postLabelTraceCleanupSolver", + TraceCleanupSolver, + (instance) => { + const traces = Object.values( + instance.postLabelTraceOverlapShiftSolver!.correctedTraceMap, + ) + const labelMergingOutput = + instance.traceLabelOverlapAvoidanceSolver!.labelMergingSolver!.getOutput() + + return [ + { + inputProblem: instance.inputProblem, + allTraces: traces, + allLabelPlacements: + instance.availableNetOrientationSolver!.outputNetLabelPlacements, + mergedLabelNetIdMap: labelMergingOutput.mergedLabelNetIdMap, + paddingBuffer: 0.1, + operations: ["untangling_traces"], + crossingsOnly: true, + eligibleTraceIds: new Set( + traces + .filter((trace) => + trace.mspPairId.startsWith("available-net-orientation-"), + ) + .map((trace) => trace.mspPairId), + ), + }, + ] + }, + ), definePipelineStep( "railNetLabelCornerPlacementSolver", RailNetLabelCornerPlacementSolver, @@ -401,9 +433,7 @@ export class SchematicTracePipelineSolver extends BaseSolver { return [ { inputProblem: instance.inputProblem, - traces: Object.values( - instance.postLabelTraceOverlapShiftSolver!.correctedTraceMap, - ), + traces: instance.postLabelTraceCleanupSolver!.getOutput().traces, netLabelPlacements: instance.availableNetOrientationSolver!.outputNetLabelPlacements, }, diff --git a/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts b/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts index da39fd852..fee2b254e 100644 --- a/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +++ b/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts @@ -25,6 +25,8 @@ export interface TraceCleanupSolverInput { paddingBuffer: number operations?: readonly TraceCleanupOperation[] eligibleTraceIds?: ReadonlySet + /** Stop untangling after strict crossings; skip the general L-shape pass. */ + crossingsOnly?: boolean } import { UntangleTraceSubsolver } from "./sub-solver/UntangleTraceSubsolver" diff --git a/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts b/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts index 552e36f59..f7db9251a 100644 --- a/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +++ b/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts @@ -55,6 +55,8 @@ export interface UntangleTraceSubsolverInput { mergedLabelNetIdMap: Record> paddingBuffer: number eligibleTraceIds?: ReadonlySet + /** Stop after resolving strict crossings; skip the general L-shape pass. */ + crossingsOnly?: boolean } /** @@ -137,6 +139,10 @@ export class UntangleTraceSubsolver extends BaseSolver { } return } + if (this.input.crossingsOnly) { + this.solved = true + return + } this.processingCrossings = false this._initializeLShapes() return diff --git a/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg b/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg index ab83a6a42..e54a8ccfc 100644 --- a/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +++ b/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg @@ -1,6 +1,6 @@ - - { +test("PMP11282 V20V2 label connector avoids a different-net trace", () => { const solver = new SchematicTracePipelineSolver(inputProblem, { hideRatsNet: true, }) @@ -80,6 +80,6 @@ test("PMP11282 V20V2 label connector crosses a different-net trace", () => { labelConnector.tracePath, crossedTrace.tracePath, ), - ).toHaveLength(1) + ).toHaveLength(0) expect(solver).toMatchSolverSnapshot(import.meta.path) })