@tscircuit/schematic-trace-solver 0.0.93 → 0.0.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +12 -6
- package/dist/index.js +71 -16
- package/lib/solvers/Example28Solver/reroute.ts +71 -7
- package/lib/solvers/{VccNetLabelCornerPlacementSolver/VccNetLabelCornerPlacementSolver.ts → RailNetLabelCornerPlacementSolver/RailNetLabelCornerPlacementSolver.ts} +27 -9
- package/lib/solvers/{VccNetLabelCornerPlacementSolver → RailNetLabelCornerPlacementSolver}/types.ts +7 -1
- package/lib/solvers/{VccNetLabelCornerPlacementSolver → RailNetLabelCornerPlacementSolver}/visualize.ts +1 -1
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +6 -5
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +5 -5
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +314 -314
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +4 -4
- package/tests/bug-reports/bug-report-20260707T134722Z/__snapshots__/bug-report-20260707T134722Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +8 -8
- package/tests/bug-reports/bug-report-20260707T141421Z/__snapshots__/bug-report-20260707T141421Z.snap.svg +4 -4
- package/tests/bug-reports/bug-report-20260707T230831Z/__snapshots__/bug-report-20260707T230831Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +2 -2
- package/tests/examples/__snapshots__/example01.snap.svg +2 -2
- package/tests/examples/__snapshots__/example02.snap.svg +4 -4
- package/tests/examples/__snapshots__/example07.snap.svg +2 -2
- package/tests/examples/__snapshots__/example12.snap.svg +2 -2
- package/tests/examples/__snapshots__/example14.snap.svg +36 -36
- package/tests/examples/__snapshots__/example15.snap.svg +2 -2
- package/tests/examples/__snapshots__/example18.snap.svg +4 -4
- package/tests/examples/__snapshots__/example19.snap.svg +22 -22
- package/tests/examples/__snapshots__/example21.snap.svg +4 -4
- package/tests/examples/__snapshots__/example25.snap.svg +35 -35
- package/tests/examples/__snapshots__/example29.snap.svg +10 -10
- package/tests/examples/__snapshots__/example31.snap.svg +6 -6
- package/tests/examples/__snapshots__/example32.snap.svg +2 -2
- package/tests/examples/__snapshots__/example46.snap.svg +2 -2
- package/tests/examples/__snapshots__/example47.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-ina237-current-monitor.snap.svg +4 -4
- package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +2 -2
- package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +2 -2
- /package/lib/solvers/{VccNetLabelCornerPlacementSolver → RailNetLabelCornerPlacementSolver}/geometry.ts +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -788,7 +788,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
788
788
|
visualize(): GraphicsObject;
|
|
789
789
|
}
|
|
790
790
|
|
|
791
|
-
interface
|
|
791
|
+
interface RailNetLabelCornerPlacementSolverParams {
|
|
792
792
|
inputProblem: InputProblem;
|
|
793
793
|
traces: SolvedTracePath[];
|
|
794
794
|
netLabelPlacements: NetLabelPlacement[];
|
|
@@ -797,6 +797,12 @@ type TraceCornerCandidate = {
|
|
|
797
797
|
anchorPoint: Point;
|
|
798
798
|
traceId: string;
|
|
799
799
|
distance: number;
|
|
800
|
+
/**
|
|
801
|
+
* True when the corner lines up with one of the trace's pins along the
|
|
802
|
+
* label's stub axis (same x for a vertical rail label), so the label reads as
|
|
803
|
+
* a clean stub off that pin rather than floating over a mid-trace bend.
|
|
804
|
+
*/
|
|
805
|
+
pinAligned: boolean;
|
|
800
806
|
};
|
|
801
807
|
type CornerCandidateStatus = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "netlabel-collision";
|
|
802
808
|
type EvaluatedCornerCandidate = TraceCornerCandidate & {
|
|
@@ -807,7 +813,7 @@ type EvaluatedCornerCandidate = TraceCornerCandidate & {
|
|
|
807
813
|
selected: boolean;
|
|
808
814
|
};
|
|
809
815
|
|
|
810
|
-
declare class
|
|
816
|
+
declare class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
811
817
|
inputProblem: InputProblem;
|
|
812
818
|
traces: SolvedTracePath[];
|
|
813
819
|
netLabelPlacements: NetLabelPlacement[];
|
|
@@ -819,8 +825,8 @@ declare class VccNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
819
825
|
private queuedCornerCandidates;
|
|
820
826
|
private shouldAdvanceToNextLabel;
|
|
821
827
|
private traceMap;
|
|
822
|
-
constructor(params:
|
|
823
|
-
getConstructorParams(): ConstructorParameters<typeof
|
|
828
|
+
constructor(params: RailNetLabelCornerPlacementSolverParams);
|
|
829
|
+
getConstructorParams(): ConstructorParameters<typeof RailNetLabelCornerPlacementSolver>[0];
|
|
824
830
|
_step(): void;
|
|
825
831
|
getOutput(): {
|
|
826
832
|
netLabelPlacements: NetLabelPlacement[];
|
|
@@ -1021,7 +1027,7 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
1021
1027
|
traceCleanupSolver?: TraceCleanupSolver;
|
|
1022
1028
|
example28Solver?: Example28Solver;
|
|
1023
1029
|
availableNetOrientationSolver?: AvailableNetOrientationSolver;
|
|
1024
|
-
|
|
1030
|
+
railNetLabelCornerPlacementSolver?: RailNetLabelCornerPlacementSolver;
|
|
1025
1031
|
traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver;
|
|
1026
1032
|
netLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver;
|
|
1027
1033
|
netLabelNetLabelCollisionSolver?: NetLabelNetLabelCollisionSolver;
|
|
@@ -1030,7 +1036,7 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
1030
1036
|
timeSpentOnPhase: Record<string, number>;
|
|
1031
1037
|
firstIterationOfPhase: Record<string, number>;
|
|
1032
1038
|
inputProblem: InputProblem;
|
|
1033
|
-
pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver> | PipelineStep<typeof TraceCleanupSolver> | PipelineStep<typeof Example28Solver> | PipelineStep<typeof AvailableNetOrientationSolver> | PipelineStep<typeof
|
|
1039
|
+
pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver> | PipelineStep<typeof TraceCleanupSolver> | PipelineStep<typeof Example28Solver> | PipelineStep<typeof AvailableNetOrientationSolver> | PipelineStep<typeof RailNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver> | PipelineStep<typeof NetLabelTraceCollisionSolver> | PipelineStep<typeof NetLabelNetLabelCollisionSolver>)[];
|
|
1034
1040
|
constructor(inputProblem: InputProblem);
|
|
1035
1041
|
getConstructorParams(): ConstructorParameters<typeof SchematicTracePipelineSolver>[0];
|
|
1036
1042
|
currentPipelineStepIndex: number;
|
package/dist/index.js
CHANGED
|
@@ -5653,6 +5653,30 @@ var getSegmentOverlapWithRectSpan = (start, end, rect) => {
|
|
|
5653
5653
|
|
|
5654
5654
|
// lib/solvers/Example28Solver/reroute.ts
|
|
5655
5655
|
var LABEL_CLEARANCE = 0.1;
|
|
5656
|
+
var MAX_CORRIDOR_SHIFTS = 16;
|
|
5657
|
+
var corridorHasOtherNetTrace = ({
|
|
5658
|
+
x,
|
|
5659
|
+
lowY,
|
|
5660
|
+
highY,
|
|
5661
|
+
outputTraces,
|
|
5662
|
+
netId
|
|
5663
|
+
}) => {
|
|
5664
|
+
for (const other of outputTraces) {
|
|
5665
|
+
if (other.globalConnNetId === netId) continue;
|
|
5666
|
+
const path = other.tracePath;
|
|
5667
|
+
for (let i = 0; i + 1 < path.length; i++) {
|
|
5668
|
+
const start = path[i];
|
|
5669
|
+
const end = path[i + 1];
|
|
5670
|
+
if (Math.abs(start.x - end.x) >= 1e-9) continue;
|
|
5671
|
+
if (Math.abs(start.x - x) >= LABEL_CLEARANCE / 2) continue;
|
|
5672
|
+
const segLowY = Math.min(start.y, end.y);
|
|
5673
|
+
const segHighY = Math.max(start.y, end.y);
|
|
5674
|
+
if (Math.min(highY, segHighY) - Math.max(lowY, segLowY) > 1e-6)
|
|
5675
|
+
return true;
|
|
5676
|
+
}
|
|
5677
|
+
}
|
|
5678
|
+
return false;
|
|
5679
|
+
};
|
|
5656
5680
|
var findBestReroutePath = ({
|
|
5657
5681
|
trace,
|
|
5658
5682
|
obstacleLabel,
|
|
@@ -5691,8 +5715,11 @@ var generateRerouteCandidateResults = ({
|
|
|
5691
5715
|
const seen = /* @__PURE__ */ new Set();
|
|
5692
5716
|
const candidateResults = [];
|
|
5693
5717
|
const horizontalSegmentPushCandidate = generateHorizontalSegmentPushCandidate(
|
|
5694
|
-
|
|
5695
|
-
|
|
5718
|
+
{
|
|
5719
|
+
trace,
|
|
5720
|
+
label,
|
|
5721
|
+
outputTraces
|
|
5722
|
+
}
|
|
5696
5723
|
);
|
|
5697
5724
|
if (horizontalSegmentPushCandidate) {
|
|
5698
5725
|
candidateResults.push(
|
|
@@ -5886,7 +5913,11 @@ var selectBestReroutePath = ({
|
|
|
5886
5913
|
}
|
|
5887
5914
|
return bestPath;
|
|
5888
5915
|
};
|
|
5889
|
-
var generateHorizontalSegmentPushCandidate = (
|
|
5916
|
+
var generateHorizontalSegmentPushCandidate = ({
|
|
5917
|
+
trace,
|
|
5918
|
+
label,
|
|
5919
|
+
outputTraces
|
|
5920
|
+
}) => {
|
|
5890
5921
|
const labelDirection = dir(label.orientation);
|
|
5891
5922
|
if (labelDirection.x === 0) return null;
|
|
5892
5923
|
const path = trace.tracePath;
|
|
@@ -5911,6 +5942,20 @@ var generateHorizontalSegmentPushCandidate = (trace, label) => {
|
|
|
5911
5942
|
if (labelDirection.x > 0) {
|
|
5912
5943
|
segmentPushX = bounds.maxX + LABEL_CLEARANCE;
|
|
5913
5944
|
}
|
|
5945
|
+
const corridorStep = labelDirection.x > 0 ? LABEL_CLEARANCE : -LABEL_CLEARANCE;
|
|
5946
|
+
const verticalLowY = Math.min(verticalStart.y, verticalEnd.y);
|
|
5947
|
+
const verticalHighY = Math.max(verticalStart.y, verticalEnd.y);
|
|
5948
|
+
for (let shift = 0; shift < MAX_CORRIDOR_SHIFTS; shift++) {
|
|
5949
|
+
const occupied = corridorHasOtherNetTrace({
|
|
5950
|
+
x: segmentPushX,
|
|
5951
|
+
lowY: verticalLowY,
|
|
5952
|
+
highY: verticalHighY,
|
|
5953
|
+
outputTraces,
|
|
5954
|
+
netId: trace.globalConnNetId
|
|
5955
|
+
});
|
|
5956
|
+
if (!occupied) break;
|
|
5957
|
+
segmentPushX += corridorStep;
|
|
5958
|
+
}
|
|
5914
5959
|
const segmentPushStartY = getClearedHorizontalY({
|
|
5915
5960
|
start: previousAnchor,
|
|
5916
5961
|
end: { x: segmentPushX, y: verticalStart.y },
|
|
@@ -7257,7 +7302,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
7257
7302
|
}
|
|
7258
7303
|
};
|
|
7259
7304
|
|
|
7260
|
-
// lib/solvers/
|
|
7305
|
+
// lib/solvers/RailNetLabelCornerPlacementSolver/geometry.ts
|
|
7261
7306
|
var EPS9 = 1e-6;
|
|
7262
7307
|
var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
|
|
7263
7308
|
var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
|
|
@@ -7290,10 +7335,10 @@ var isPointOnSegment = (point, start, end) => {
|
|
|
7290
7335
|
};
|
|
7291
7336
|
var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS9 ? "horizontal" : "vertical";
|
|
7292
7337
|
|
|
7293
|
-
// lib/solvers/
|
|
7338
|
+
// lib/solvers/RailNetLabelCornerPlacementSolver/visualize.ts
|
|
7294
7339
|
var CANDIDATE_SELECTED_COLOR3 = "blue";
|
|
7295
7340
|
var CANDIDATE_REJECTED_COLOR3 = "red";
|
|
7296
|
-
var
|
|
7341
|
+
var visualizeRailNetLabelCornerPlacementSolver = (state) => {
|
|
7297
7342
|
const graphics = visualizeInputProblem(state.inputProblem);
|
|
7298
7343
|
ensureGraphicsArrays2(graphics);
|
|
7299
7344
|
drawTraces3(graphics, state.traces);
|
|
@@ -7374,8 +7419,8 @@ var ensureGraphicsArrays2 = (graphics) => {
|
|
|
7374
7419
|
if (!graphics.texts) graphics.texts = [];
|
|
7375
7420
|
};
|
|
7376
7421
|
|
|
7377
|
-
// lib/solvers/
|
|
7378
|
-
var
|
|
7422
|
+
// lib/solvers/RailNetLabelCornerPlacementSolver/RailNetLabelCornerPlacementSolver.ts
|
|
7423
|
+
var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
7379
7424
|
inputProblem;
|
|
7380
7425
|
traces;
|
|
7381
7426
|
netLabelPlacements;
|
|
@@ -7507,7 +7552,8 @@ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
7507
7552
|
};
|
|
7508
7553
|
}
|
|
7509
7554
|
shouldProcessLabel(label) {
|
|
7510
|
-
|
|
7555
|
+
const isVerticalRailLabel = label.orientation === "y+" || label.orientation === "y-";
|
|
7556
|
+
return isVerticalRailLabel && this.getCornerCandidatesForLabel(label).length > 0;
|
|
7511
7557
|
}
|
|
7512
7558
|
intersectsAnyChip(bounds) {
|
|
7513
7559
|
return this.inputProblem.chips.some(
|
|
@@ -7524,17 +7570,26 @@ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
7524
7570
|
});
|
|
7525
7571
|
}
|
|
7526
7572
|
getCornerCandidatesForLabel(label) {
|
|
7573
|
+
const isVertical4 = label.orientation === "y+" || label.orientation === "y-";
|
|
7527
7574
|
const candidates = [];
|
|
7528
7575
|
const seenCornerKeys = /* @__PURE__ */ new Set();
|
|
7529
7576
|
for (const trace of this.getTraceLinesForLabel(label)) {
|
|
7530
|
-
|
|
7577
|
+
const path = trace.tracePath;
|
|
7578
|
+
const pins = [path[0], path[path.length - 1]];
|
|
7579
|
+
for (const anchorPoint of getTraceCorners(path)) {
|
|
7531
7580
|
const key = `${anchorPoint.x}:${anchorPoint.y}`;
|
|
7532
7581
|
if (seenCornerKeys.has(key)) continue;
|
|
7533
7582
|
seenCornerKeys.add(key);
|
|
7583
|
+
const pinAligned = pins.some((pin) => {
|
|
7584
|
+
if (isVertical4) return Math.abs(pin.x - anchorPoint.x) < EPS9;
|
|
7585
|
+
return Math.abs(pin.y - anchorPoint.y) < EPS9;
|
|
7586
|
+
});
|
|
7587
|
+
if (!pinAligned) continue;
|
|
7534
7588
|
candidates.push({
|
|
7535
7589
|
anchorPoint,
|
|
7536
7590
|
traceId: trace.mspPairId,
|
|
7537
|
-
distance: getDistance2(anchorPoint, label.anchorPoint)
|
|
7591
|
+
distance: getDistance2(anchorPoint, label.anchorPoint),
|
|
7592
|
+
pinAligned
|
|
7538
7593
|
});
|
|
7539
7594
|
}
|
|
7540
7595
|
}
|
|
@@ -7556,7 +7611,7 @@ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
7556
7611
|
return trace.globalConnNetId === label.globalConnNetId;
|
|
7557
7612
|
}
|
|
7558
7613
|
visualize() {
|
|
7559
|
-
return
|
|
7614
|
+
return visualizeRailNetLabelCornerPlacementSolver({
|
|
7560
7615
|
inputProblem: this.inputProblem,
|
|
7561
7616
|
traces: this.traces,
|
|
7562
7617
|
outputNetLabelPlacements: this.outputNetLabelPlacements,
|
|
@@ -8970,7 +9025,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
8970
9025
|
traceCleanupSolver;
|
|
8971
9026
|
example28Solver;
|
|
8972
9027
|
availableNetOrientationSolver;
|
|
8973
|
-
|
|
9028
|
+
railNetLabelCornerPlacementSolver;
|
|
8974
9029
|
traceAnchoredNetLabelOverlapSolver;
|
|
8975
9030
|
netLabelTraceCollisionSolver;
|
|
8976
9031
|
netLabelNetLabelCollisionSolver;
|
|
@@ -9132,8 +9187,8 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
9132
9187
|
]
|
|
9133
9188
|
),
|
|
9134
9189
|
definePipelineStep(
|
|
9135
|
-
"
|
|
9136
|
-
|
|
9190
|
+
"railNetLabelCornerPlacementSolver",
|
|
9191
|
+
RailNetLabelCornerPlacementSolver,
|
|
9137
9192
|
(instance) => {
|
|
9138
9193
|
return [
|
|
9139
9194
|
{
|
|
@@ -9151,7 +9206,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
9151
9206
|
{
|
|
9152
9207
|
inputProblem: instance.inputProblem,
|
|
9153
9208
|
traces: instance.availableNetOrientationSolver.traces,
|
|
9154
|
-
netLabelPlacements: instance.
|
|
9209
|
+
netLabelPlacements: instance.railNetLabelCornerPlacementSolver.outputNetLabelPlacements
|
|
9155
9210
|
}
|
|
9156
9211
|
]
|
|
9157
9212
|
),
|
|
@@ -8,13 +8,13 @@ import { detectTraceLabelOverlap } from "lib/solvers/TraceLabelOverlapAvoidanceS
|
|
|
8
8
|
import { generateRerouteCandidates } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace"
|
|
9
9
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
10
10
|
import { dir } from "lib/utils/dir"
|
|
11
|
+
import { getChipBoundaryOverlap } from "./doesPathRunAlongChipBoundary"
|
|
11
12
|
import {
|
|
12
13
|
countPathIntersections,
|
|
13
14
|
getPathKey,
|
|
14
15
|
getPathLength,
|
|
15
16
|
isPathCollidingWithChipInterior,
|
|
16
17
|
} from "./geometry"
|
|
17
|
-
import { getChipBoundaryOverlap } from "./doesPathRunAlongChipBoundary"
|
|
18
18
|
import type {
|
|
19
19
|
ChipObstacle,
|
|
20
20
|
RerouteCandidateResult,
|
|
@@ -22,6 +22,43 @@ import type {
|
|
|
22
22
|
} from "./types"
|
|
23
23
|
|
|
24
24
|
const LABEL_CLEARANCE = 0.1
|
|
25
|
+
// How many LABEL_CLEARANCE-wide corridors to try before giving up on finding a
|
|
26
|
+
// free one for a pushed segment.
|
|
27
|
+
const MAX_CORRIDOR_SHIFTS = 16
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Whether a vertical trace segment of a different net already runs along the
|
|
31
|
+
* corridor at `x`, overlapping the [lowY, highY] span (within a corridor width).
|
|
32
|
+
*/
|
|
33
|
+
const corridorHasOtherNetTrace = ({
|
|
34
|
+
x,
|
|
35
|
+
lowY,
|
|
36
|
+
highY,
|
|
37
|
+
outputTraces,
|
|
38
|
+
netId,
|
|
39
|
+
}: {
|
|
40
|
+
x: number
|
|
41
|
+
lowY: number
|
|
42
|
+
highY: number
|
|
43
|
+
outputTraces: SolvedTracePath[]
|
|
44
|
+
netId: string
|
|
45
|
+
}): boolean => {
|
|
46
|
+
for (const other of outputTraces) {
|
|
47
|
+
if (other.globalConnNetId === netId) continue
|
|
48
|
+
const path = other.tracePath
|
|
49
|
+
for (let i = 0; i + 1 < path.length; i++) {
|
|
50
|
+
const start = path[i]!
|
|
51
|
+
const end = path[i + 1]!
|
|
52
|
+
if (Math.abs(start.x - end.x) >= 1e-9) continue
|
|
53
|
+
if (Math.abs(start.x - x) >= LABEL_CLEARANCE / 2) continue
|
|
54
|
+
const segLowY = Math.min(start.y, end.y)
|
|
55
|
+
const segHighY = Math.max(start.y, end.y)
|
|
56
|
+
if (Math.min(highY, segHighY) - Math.max(lowY, segLowY) > 1e-6)
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
25
62
|
|
|
26
63
|
export const findBestReroutePath = ({
|
|
27
64
|
trace,
|
|
@@ -78,8 +115,11 @@ export const generateRerouteCandidateResults = ({
|
|
|
78
115
|
const seen = new Set<string>()
|
|
79
116
|
const candidateResults: RerouteCandidateResult[] = []
|
|
80
117
|
const horizontalSegmentPushCandidate = generateHorizontalSegmentPushCandidate(
|
|
81
|
-
|
|
82
|
-
|
|
118
|
+
{
|
|
119
|
+
trace,
|
|
120
|
+
label,
|
|
121
|
+
outputTraces,
|
|
122
|
+
},
|
|
83
123
|
)
|
|
84
124
|
|
|
85
125
|
if (horizontalSegmentPushCandidate) {
|
|
@@ -328,10 +368,15 @@ const selectBestReroutePath = ({
|
|
|
328
368
|
return bestPath
|
|
329
369
|
}
|
|
330
370
|
|
|
331
|
-
const generateHorizontalSegmentPushCandidate = (
|
|
332
|
-
trace
|
|
333
|
-
label
|
|
334
|
-
|
|
371
|
+
const generateHorizontalSegmentPushCandidate = ({
|
|
372
|
+
trace,
|
|
373
|
+
label,
|
|
374
|
+
outputTraces,
|
|
375
|
+
}: {
|
|
376
|
+
trace: SolvedTracePath
|
|
377
|
+
label: NetLabelPlacement
|
|
378
|
+
outputTraces: SolvedTracePath[]
|
|
379
|
+
}): Point[] | null => {
|
|
335
380
|
const labelDirection = dir(label.orientation)
|
|
336
381
|
if (labelDirection.x === 0) return null
|
|
337
382
|
|
|
@@ -367,6 +412,25 @@ const generateHorizontalSegmentPushCandidate = (
|
|
|
367
412
|
if (labelDirection.x > 0) {
|
|
368
413
|
segmentPushX = bounds.maxX + LABEL_CLEARANCE
|
|
369
414
|
}
|
|
415
|
+
|
|
416
|
+
// Two traces escaping the same label block would otherwise be pushed to the
|
|
417
|
+
// identical corridor and stack on one line. Slide the corridor further out
|
|
418
|
+
// until it no longer coincides with a different-net trace already routed
|
|
419
|
+
// there, giving each escaping trace its own parallel corridor.
|
|
420
|
+
const corridorStep = labelDirection.x > 0 ? LABEL_CLEARANCE : -LABEL_CLEARANCE
|
|
421
|
+
const verticalLowY = Math.min(verticalStart.y, verticalEnd.y)
|
|
422
|
+
const verticalHighY = Math.max(verticalStart.y, verticalEnd.y)
|
|
423
|
+
for (let shift = 0; shift < MAX_CORRIDOR_SHIFTS; shift++) {
|
|
424
|
+
const occupied = corridorHasOtherNetTrace({
|
|
425
|
+
x: segmentPushX,
|
|
426
|
+
lowY: verticalLowY,
|
|
427
|
+
highY: verticalHighY,
|
|
428
|
+
outputTraces,
|
|
429
|
+
netId: trace.globalConnNetId,
|
|
430
|
+
})
|
|
431
|
+
if (!occupied) break
|
|
432
|
+
segmentPushX += corridorStep
|
|
433
|
+
}
|
|
370
434
|
const segmentPushStartY = getClearedHorizontalY({
|
|
371
435
|
start: previousAnchor,
|
|
372
436
|
end: { x: segmentPushX, y: verticalStart.y },
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
10
10
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
11
11
|
import {
|
|
12
|
+
EPS,
|
|
12
13
|
getDistance,
|
|
13
14
|
getTraceCorners,
|
|
14
15
|
isTraceLine,
|
|
@@ -20,12 +21,12 @@ import type {
|
|
|
20
21
|
CornerCandidateStatus,
|
|
21
22
|
EvaluatedCornerCandidate,
|
|
22
23
|
TraceCornerCandidate,
|
|
23
|
-
|
|
24
|
+
RailNetLabelCornerPlacementSolverParams,
|
|
24
25
|
} from "./types"
|
|
25
|
-
import {
|
|
26
|
+
import { visualizeRailNetLabelCornerPlacementSolver } from "./visualize"
|
|
26
27
|
import { rectIntersectsAnyTextBox } from "lib/utils/textBoxBounds"
|
|
27
28
|
|
|
28
|
-
export class
|
|
29
|
+
export class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
29
30
|
inputProblem: InputProblem
|
|
30
31
|
traces: SolvedTracePath[]
|
|
31
32
|
netLabelPlacements: NetLabelPlacement[]
|
|
@@ -40,7 +41,7 @@ export class VccNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
40
41
|
private shouldAdvanceToNextLabel = false
|
|
41
42
|
private traceMap: Record<string, SolvedTracePath>
|
|
42
43
|
|
|
43
|
-
constructor(params:
|
|
44
|
+
constructor(params: RailNetLabelCornerPlacementSolverParams) {
|
|
44
45
|
super()
|
|
45
46
|
this.inputProblem = params.inputProblem
|
|
46
47
|
this.traces = params.traces
|
|
@@ -54,7 +55,7 @@ export class VccNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
override getConstructorParams(): ConstructorParameters<
|
|
57
|
-
typeof
|
|
58
|
+
typeof RailNetLabelCornerPlacementSolver
|
|
58
59
|
>[0] {
|
|
59
60
|
return {
|
|
60
61
|
inputProblem: this.inputProblem,
|
|
@@ -197,9 +198,13 @@ export class VccNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
private shouldProcessLabel(label: NetLabelPlacement) {
|
|
201
|
+
// Power/ground rail labels (VCC, GND, V3_3, ...) have a fixed vertical
|
|
202
|
+
// orientation and read best snapped to a trace corner rather than floating
|
|
203
|
+
// mid-segment. Signal labels (x+/x-) are left where they are.
|
|
204
|
+
const isVerticalRailLabel =
|
|
205
|
+
label.orientation === "y+" || label.orientation === "y-"
|
|
200
206
|
return (
|
|
201
|
-
label.
|
|
202
|
-
this.getCornerCandidatesForLabel(label).length > 0
|
|
207
|
+
isVerticalRailLabel && this.getCornerCandidatesForLabel(label).length > 0
|
|
203
208
|
)
|
|
204
209
|
}
|
|
205
210
|
|
|
@@ -220,18 +225,31 @@ export class VccNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
220
225
|
}
|
|
221
226
|
|
|
222
227
|
private getCornerCandidatesForLabel(label: NetLabelPlacement) {
|
|
228
|
+
const isVertical = label.orientation === "y+" || label.orientation === "y-"
|
|
223
229
|
const candidates: TraceCornerCandidate[] = []
|
|
224
230
|
const seenCornerKeys = new Set<string>()
|
|
225
231
|
|
|
226
232
|
for (const trace of this.getTraceLinesForLabel(label)) {
|
|
227
|
-
|
|
233
|
+
const path = trace.tracePath
|
|
234
|
+
const pins = [path[0]!, path[path.length - 1]!]
|
|
235
|
+
for (const anchorPoint of getTraceCorners(path)) {
|
|
228
236
|
const key = `${anchorPoint.x}:${anchorPoint.y}`
|
|
229
237
|
if (seenCornerKeys.has(key)) continue
|
|
230
238
|
seenCornerKeys.add(key)
|
|
239
|
+
const pinAligned = pins.some((pin) => {
|
|
240
|
+
if (isVertical) return Math.abs(pin.x - anchorPoint.x) < EPS
|
|
241
|
+
return Math.abs(pin.y - anchorPoint.y) < EPS
|
|
242
|
+
})
|
|
243
|
+
// Only pin-aligned corners are worth snapping a rail label to (a clean
|
|
244
|
+
// stub off a pin). Non-aligned corners are no better than where the
|
|
245
|
+
// label already is, so they aren't offered as candidates — this keeps
|
|
246
|
+
// already-well-placed labels untouched.
|
|
247
|
+
if (!pinAligned) continue
|
|
231
248
|
candidates.push({
|
|
232
249
|
anchorPoint,
|
|
233
250
|
traceId: trace.mspPairId,
|
|
234
251
|
distance: getDistance(anchorPoint, label.anchorPoint),
|
|
252
|
+
pinAligned,
|
|
235
253
|
})
|
|
236
254
|
}
|
|
237
255
|
}
|
|
@@ -259,7 +277,7 @@ export class VccNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
259
277
|
}
|
|
260
278
|
|
|
261
279
|
override visualize(): GraphicsObject {
|
|
262
|
-
return
|
|
280
|
+
return visualizeRailNetLabelCornerPlacementSolver({
|
|
263
281
|
inputProblem: this.inputProblem,
|
|
264
282
|
traces: this.traces,
|
|
265
283
|
outputNetLabelPlacements: this.outputNetLabelPlacements,
|
package/lib/solvers/{VccNetLabelCornerPlacementSolver → RailNetLabelCornerPlacementSolver}/types.ts
RENAMED
|
@@ -3,7 +3,7 @@ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetL
|
|
|
3
3
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
4
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
5
5
|
|
|
6
|
-
export interface
|
|
6
|
+
export interface RailNetLabelCornerPlacementSolverParams {
|
|
7
7
|
inputProblem: InputProblem
|
|
8
8
|
traces: SolvedTracePath[]
|
|
9
9
|
netLabelPlacements: NetLabelPlacement[]
|
|
@@ -20,6 +20,12 @@ export type TraceCornerCandidate = {
|
|
|
20
20
|
anchorPoint: Point
|
|
21
21
|
traceId: string
|
|
22
22
|
distance: number
|
|
23
|
+
/**
|
|
24
|
+
* True when the corner lines up with one of the trace's pins along the
|
|
25
|
+
* label's stub axis (same x for a vertical rail label), so the label reads as
|
|
26
|
+
* a clean stub off that pin rather than floating over a mid-trace bend.
|
|
27
|
+
*/
|
|
28
|
+
pinAligned: boolean
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export type CornerCandidateStatus =
|
|
@@ -9,7 +9,7 @@ import type { EvaluatedCornerCandidate } from "./types"
|
|
|
9
9
|
const CANDIDATE_SELECTED_COLOR = "blue"
|
|
10
10
|
const CANDIDATE_REJECTED_COLOR = "red"
|
|
11
11
|
|
|
12
|
-
export const
|
|
12
|
+
export const visualizeRailNetLabelCornerPlacementSolver = (state: {
|
|
13
13
|
inputProblem: InputProblem
|
|
14
14
|
traces: SolvedTracePath[]
|
|
15
15
|
outputNetLabelPlacements: NetLabelPlacement[]
|
|
@@ -23,7 +23,7 @@ import { MergedNetLabelObstacleSolver } from "../TraceLabelOverlapAvoidanceSolve
|
|
|
23
23
|
import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver"
|
|
24
24
|
import { Example28Solver } from "../Example28Solver/Example28Solver"
|
|
25
25
|
import { AvailableNetOrientationSolver } from "../AvailableNetOrientationSolver/AvailableNetOrientationSolver"
|
|
26
|
-
import {
|
|
26
|
+
import { RailNetLabelCornerPlacementSolver } from "../RailNetLabelCornerPlacementSolver/RailNetLabelCornerPlacementSolver"
|
|
27
27
|
import { TraceAnchoredNetLabelOverlapSolver } from "../TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver"
|
|
28
28
|
import { NetLabelTraceCollisionSolver } from "../NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver"
|
|
29
29
|
import { NetLabelNetLabelCollisionSolver } from "../NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver"
|
|
@@ -78,7 +78,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
78
78
|
traceCleanupSolver?: TraceCleanupSolver
|
|
79
79
|
example28Solver?: Example28Solver
|
|
80
80
|
availableNetOrientationSolver?: AvailableNetOrientationSolver
|
|
81
|
-
|
|
81
|
+
railNetLabelCornerPlacementSolver?: RailNetLabelCornerPlacementSolver
|
|
82
82
|
traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver
|
|
83
83
|
netLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver
|
|
84
84
|
netLabelNetLabelCollisionSolver?: NetLabelNetLabelCollisionSolver
|
|
@@ -264,8 +264,8 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
264
264
|
],
|
|
265
265
|
),
|
|
266
266
|
definePipelineStep(
|
|
267
|
-
"
|
|
268
|
-
|
|
267
|
+
"railNetLabelCornerPlacementSolver",
|
|
268
|
+
RailNetLabelCornerPlacementSolver,
|
|
269
269
|
(instance) => {
|
|
270
270
|
return [
|
|
271
271
|
{
|
|
@@ -285,7 +285,8 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
285
285
|
inputProblem: instance.inputProblem,
|
|
286
286
|
traces: instance.availableNetOrientationSolver!.traces,
|
|
287
287
|
netLabelPlacements:
|
|
288
|
-
instance.
|
|
288
|
+
instance.railNetLabelCornerPlacementSolver!
|
|
289
|
+
.outputNetLabelPlacements,
|
|
289
290
|
},
|
|
290
291
|
],
|
|
291
292
|
),
|