@tscircuit/schematic-trace-solver 0.0.60 → 0.0.62
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 +77 -6
- package/dist/index.js +445 -8
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +131 -11
- package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +1 -1
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +1 -1
- package/lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts +441 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +15 -0
- package/package.json +1 -1
- package/site/examples/example37.page.tsx +4 -0
- package/site/examples/example38.page.tsx +4 -0
- package/site/examples/example39.page.tsx +4 -0
- package/site/examples/example40.page.tsx +4 -0
- package/site/examples/example41.page.tsx +4 -0
- package/tests/assets/example37.json +173 -0
- package/tests/assets/example38.json +32 -0
- package/tests/assets/example39.json +71 -0
- package/tests/assets/example40.json +157 -0
- package/tests/assets/example41.json +81 -0
- package/tests/examples/__snapshots__/example04.snap.svg +16 -17
- package/tests/examples/__snapshots__/example37.snap.svg +320 -0
- package/tests/examples/__snapshots__/example38.snap.svg +77 -0
- package/tests/examples/__snapshots__/example39.snap.svg +107 -0
- package/tests/examples/__snapshots__/example40.snap.svg +231 -0
- package/tests/examples/__snapshots__/example41.snap.svg +119 -0
- package/tests/examples/example37.test.ts +12 -0
- package/tests/examples/example38.test.ts +12 -0
- package/tests/examples/example39.test.ts +12 -0
- package/tests/examples/example40.test.ts +12 -0
- package/tests/examples/example41.test.ts +12 -0
package/dist/index.d.ts
CHANGED
|
@@ -647,10 +647,10 @@ type CandidateLabel = {
|
|
|
647
647
|
width: number;
|
|
648
648
|
height: number;
|
|
649
649
|
};
|
|
650
|
-
type CandidateStatus$
|
|
651
|
-
type CandidatePhase = "rotate" | "shift";
|
|
650
|
+
type CandidateStatus$2 = "valid" | "chip-collision" | "trace-collision" | "netlabel-collision";
|
|
651
|
+
type CandidatePhase = "rotate" | "shift" | "lateral-shift";
|
|
652
652
|
type EvaluatedCandidate = CandidateLabel & {
|
|
653
|
-
status: CandidateStatus$
|
|
653
|
+
status: CandidateStatus$2;
|
|
654
654
|
selected: boolean;
|
|
655
655
|
phase: CandidatePhase;
|
|
656
656
|
distance?: number;
|
|
@@ -688,12 +688,23 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
688
688
|
private findValidRotatedCandidate;
|
|
689
689
|
private getAvailableOrientations;
|
|
690
690
|
private findValidShiftedCandidate;
|
|
691
|
+
/**
|
|
692
|
+
* When all candidates fail for the current (unshifted) position, try
|
|
693
|
+
* shifting the label anchor laterally — x for y-orientations, y for
|
|
694
|
+
* x-orientations — and re-attempting the required orientation.
|
|
695
|
+
*
|
|
696
|
+
* Offsets are tried in alternating sign order:
|
|
697
|
+
* -1·step, +1·step, -2·step, +2·step, …
|
|
698
|
+
* so the nearest escape routes are tested first.
|
|
699
|
+
*/
|
|
700
|
+
private findValidLateralShiftedCandidate;
|
|
691
701
|
private findValidCandidateInShiftColumn;
|
|
692
702
|
private evaluateCandidate;
|
|
693
703
|
private getSearchStartAnchor;
|
|
694
704
|
private getWickOffsetAnchor;
|
|
695
705
|
private getSideOffsetAnchor;
|
|
696
706
|
private getSearchDistanceLimit;
|
|
707
|
+
private getLateralColumnMaxDistance;
|
|
697
708
|
private createCandidate;
|
|
698
709
|
private getNetLabelWidth;
|
|
699
710
|
private getCandidateStatus;
|
|
@@ -771,7 +782,7 @@ type LabelOverlap = {
|
|
|
771
782
|
firstLabelIndex: number;
|
|
772
783
|
secondLabelIndex: number;
|
|
773
784
|
};
|
|
774
|
-
type CandidateStatus = "valid" | "chip-collision" | "trace-collision" | "netlabel-collision";
|
|
785
|
+
type CandidateStatus$1 = "valid" | "chip-collision" | "trace-collision" | "netlabel-collision";
|
|
775
786
|
type LabelCandidate = {
|
|
776
787
|
anchorPoint: Point;
|
|
777
788
|
center: Point;
|
|
@@ -781,7 +792,7 @@ type LabelCandidate = {
|
|
|
781
792
|
traceId: string;
|
|
782
793
|
pathDistance: number;
|
|
783
794
|
distanceFromOriginal: number;
|
|
784
|
-
status: CandidateStatus;
|
|
795
|
+
status: CandidateStatus$1;
|
|
785
796
|
selected: boolean;
|
|
786
797
|
};
|
|
787
798
|
|
|
@@ -849,6 +860,65 @@ declare class NetLabelTraceCollisionSolver extends BaseSolver {
|
|
|
849
860
|
visualize(): GraphicsObject;
|
|
850
861
|
}
|
|
851
862
|
|
|
863
|
+
type CandidateStatus = "ok" | "chip-collision" | "trace-collision" | "label-collision";
|
|
864
|
+
type Candidate = {
|
|
865
|
+
orientation: FacingDirection;
|
|
866
|
+
anchor: {
|
|
867
|
+
x: number;
|
|
868
|
+
y: number;
|
|
869
|
+
};
|
|
870
|
+
center: {
|
|
871
|
+
x: number;
|
|
872
|
+
y: number;
|
|
873
|
+
};
|
|
874
|
+
width: number;
|
|
875
|
+
height: number;
|
|
876
|
+
bounds: {
|
|
877
|
+
minX: number;
|
|
878
|
+
minY: number;
|
|
879
|
+
maxX: number;
|
|
880
|
+
maxY: number;
|
|
881
|
+
};
|
|
882
|
+
hostPairId?: MspConnectionPairId;
|
|
883
|
+
hostSegIndex?: number;
|
|
884
|
+
status: CandidateStatus | null;
|
|
885
|
+
};
|
|
886
|
+
interface NetLabelNetLabelCollisionSolverParams {
|
|
887
|
+
inputProblem: InputProblem;
|
|
888
|
+
traces: SolvedTracePath[];
|
|
889
|
+
netLabelPlacements: NetLabelPlacement[];
|
|
890
|
+
}
|
|
891
|
+
declare class NetLabelNetLabelCollisionSolver extends BaseSolver {
|
|
892
|
+
inputProblem: InputProblem;
|
|
893
|
+
traces: SolvedTracePath[];
|
|
894
|
+
netLabelPlacements: NetLabelPlacement[];
|
|
895
|
+
outputNetLabelPlacements: NetLabelPlacement[];
|
|
896
|
+
currentCollision: [NetLabelPlacement, NetLabelPlacement] | null;
|
|
897
|
+
currentLabelToMove: NetLabelPlacement | null;
|
|
898
|
+
candidateResults: Candidate[];
|
|
899
|
+
private chipIndex;
|
|
900
|
+
private traceMap;
|
|
901
|
+
private skippedCollisionKeys;
|
|
902
|
+
private labelsToTry;
|
|
903
|
+
private candidateQueue;
|
|
904
|
+
private candidateIndex;
|
|
905
|
+
constructor(params: NetLabelNetLabelCollisionSolverParams);
|
|
906
|
+
getConstructorParams(): ConstructorParameters<typeof NetLabelNetLabelCollisionSolver>[0];
|
|
907
|
+
getOutput(): {
|
|
908
|
+
netLabelPlacements: NetLabelPlacement[];
|
|
909
|
+
};
|
|
910
|
+
private labelBounds;
|
|
911
|
+
private collisionKey;
|
|
912
|
+
private findNextCollidingPair;
|
|
913
|
+
private netLabelWidthOf;
|
|
914
|
+
private buildCandidatesForLabel;
|
|
915
|
+
private checkCandidate;
|
|
916
|
+
private beginSearchForLabel;
|
|
917
|
+
private clearActiveSearch;
|
|
918
|
+
_step(): void;
|
|
919
|
+
visualize(): GraphicsObject;
|
|
920
|
+
}
|
|
921
|
+
|
|
852
922
|
/**
|
|
853
923
|
* Pipeline solver that runs a series of solvers to find the best schematic layout.
|
|
854
924
|
* Coordinates the entire layout process from chip partitioning through final packing.
|
|
@@ -879,12 +949,13 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
879
949
|
vccNetLabelCornerPlacementSolver?: VccNetLabelCornerPlacementSolver;
|
|
880
950
|
traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver;
|
|
881
951
|
netLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver;
|
|
952
|
+
netLabelNetLabelCollisionSolver?: NetLabelNetLabelCollisionSolver;
|
|
882
953
|
startTimeOfPhase: Record<string, number>;
|
|
883
954
|
endTimeOfPhase: Record<string, number>;
|
|
884
955
|
timeSpentOnPhase: Record<string, number>;
|
|
885
956
|
firstIterationOfPhase: Record<string, number>;
|
|
886
957
|
inputProblem: InputProblem;
|
|
887
|
-
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 VccNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver> | PipelineStep<typeof NetLabelTraceCollisionSolver>)[];
|
|
958
|
+
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 VccNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver> | PipelineStep<typeof NetLabelTraceCollisionSolver> | PipelineStep<typeof NetLabelNetLabelCollisionSolver>)[];
|
|
888
959
|
constructor(inputProblem: InputProblem);
|
|
889
960
|
getConstructorParams(): ConstructorParameters<typeof SchematicTracePipelineSolver>[0];
|
|
890
961
|
currentPipelineStepIndex: number;
|
package/dist/index.js
CHANGED
|
@@ -5873,11 +5873,28 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
5873
5873
|
this.addConnectorTrace(label, candidate, labelIndex);
|
|
5874
5874
|
}
|
|
5875
5875
|
addConnectorTrace(label, candidate, labelIndex) {
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
candidate.
|
|
5879
|
-
|
|
5880
|
-
|
|
5876
|
+
let tracePath;
|
|
5877
|
+
if (candidate.phase === "lateral-shift") {
|
|
5878
|
+
const orientDir = dir(candidate.orientation);
|
|
5879
|
+
const kickedSource = {
|
|
5880
|
+
x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
|
|
5881
|
+
y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP
|
|
5882
|
+
};
|
|
5883
|
+
tracePath = simplifyOrthogonalPath([
|
|
5884
|
+
label.anchorPoint,
|
|
5885
|
+
...getConnectorTracePath(
|
|
5886
|
+
kickedSource,
|
|
5887
|
+
candidate.anchorPoint,
|
|
5888
|
+
candidate.orientation
|
|
5889
|
+
)
|
|
5890
|
+
]);
|
|
5891
|
+
} else {
|
|
5892
|
+
tracePath = getConnectorTracePath(
|
|
5893
|
+
label.anchorPoint,
|
|
5894
|
+
candidate.anchorPoint,
|
|
5895
|
+
candidate.orientation
|
|
5896
|
+
);
|
|
5897
|
+
}
|
|
5881
5898
|
if (tracePath.length < 2) return;
|
|
5882
5899
|
const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
|
|
5883
5900
|
const connectorTrace = {
|
|
@@ -5910,7 +5927,18 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
5910
5927
|
labelIndex,
|
|
5911
5928
|
orientations
|
|
5912
5929
|
);
|
|
5913
|
-
|
|
5930
|
+
if (rotatedCandidate) return rotatedCandidate;
|
|
5931
|
+
const shiftedCandidate = this.findValidShiftedCandidate(
|
|
5932
|
+
label,
|
|
5933
|
+
orientations[0],
|
|
5934
|
+
labelIndex
|
|
5935
|
+
);
|
|
5936
|
+
if (shiftedCandidate) return shiftedCandidate;
|
|
5937
|
+
return this.findValidLateralShiftedCandidate(
|
|
5938
|
+
label,
|
|
5939
|
+
orientations[0],
|
|
5940
|
+
labelIndex
|
|
5941
|
+
);
|
|
5914
5942
|
}
|
|
5915
5943
|
findValidRotatedCandidate(label, labelIndex, orientations) {
|
|
5916
5944
|
for (const orientation of orientations) {
|
|
@@ -5964,6 +5992,50 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
5964
5992
|
}
|
|
5965
5993
|
return null;
|
|
5966
5994
|
}
|
|
5995
|
+
/**
|
|
5996
|
+
* When all candidates fail for the current (unshifted) position, try
|
|
5997
|
+
* shifting the label anchor laterally — x for y-orientations, y for
|
|
5998
|
+
* x-orientations — and re-attempting the required orientation.
|
|
5999
|
+
*
|
|
6000
|
+
* Offsets are tried in alternating sign order:
|
|
6001
|
+
* -1·step, +1·step, -2·step, +2·step, …
|
|
6002
|
+
* so the nearest escape routes are tested first.
|
|
6003
|
+
*/
|
|
6004
|
+
findValidLateralShiftedCandidate(label, orientation, labelIndex) {
|
|
6005
|
+
const direction = dir(orientation);
|
|
6006
|
+
const initialBaseAnchor = this.getSearchStartAnchor(label, orientation);
|
|
6007
|
+
const lateralDir = {
|
|
6008
|
+
x: isYOrientation(orientation) ? 1 : 0,
|
|
6009
|
+
y: isXOrientation(orientation) ? 1 : 0
|
|
6010
|
+
};
|
|
6011
|
+
const maxSteps = Math.ceil(this.maxSearchDistance / LABEL_SEARCH_STEP);
|
|
6012
|
+
for (let step = 1; step <= maxSteps; step++) {
|
|
6013
|
+
for (const sign of [-1, 1]) {
|
|
6014
|
+
const lateralOffset = sign * step * LABEL_SEARCH_STEP;
|
|
6015
|
+
const baseAnchor = {
|
|
6016
|
+
x: initialBaseAnchor.x + lateralDir.x * lateralOffset,
|
|
6017
|
+
y: initialBaseAnchor.y + lateralDir.y * lateralOffset
|
|
6018
|
+
};
|
|
6019
|
+
const maxSearchDistance = this.getLateralColumnMaxDistance(
|
|
6020
|
+
label,
|
|
6021
|
+
orientation,
|
|
6022
|
+
baseAnchor
|
|
6023
|
+
);
|
|
6024
|
+
const candidate = this.findValidCandidateInShiftColumn({
|
|
6025
|
+
label,
|
|
6026
|
+
labelIndex,
|
|
6027
|
+
orientation,
|
|
6028
|
+
direction,
|
|
6029
|
+
baseAnchor,
|
|
6030
|
+
maxSearchDistance,
|
|
6031
|
+
outwardDistance: lateralOffset,
|
|
6032
|
+
phase: "lateral-shift"
|
|
6033
|
+
});
|
|
6034
|
+
if (candidate) return candidate;
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
6037
|
+
return null;
|
|
6038
|
+
}
|
|
5967
6039
|
findValidCandidateInShiftColumn(params) {
|
|
5968
6040
|
const {
|
|
5969
6041
|
label,
|
|
@@ -5972,7 +6044,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
5972
6044
|
direction,
|
|
5973
6045
|
baseAnchor,
|
|
5974
6046
|
maxSearchDistance,
|
|
5975
|
-
outwardDistance
|
|
6047
|
+
outwardDistance,
|
|
6048
|
+
phase = "shift"
|
|
5976
6049
|
} = params;
|
|
5977
6050
|
for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS7; distance3 += LABEL_SEARCH_STEP) {
|
|
5978
6051
|
const anchorPoint = {
|
|
@@ -5984,7 +6057,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
5984
6057
|
candidate,
|
|
5985
6058
|
label,
|
|
5986
6059
|
labelIndex,
|
|
5987
|
-
|
|
6060
|
+
phase,
|
|
5988
6061
|
distance3,
|
|
5989
6062
|
outwardDistance
|
|
5990
6063
|
);
|
|
@@ -6065,6 +6138,21 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6065
6138
|
const labelLength = orientation === "y+" || orientation === "y-" ? height : width;
|
|
6066
6139
|
return Math.min(this.maxSearchDistance, labelLength * 2);
|
|
6067
6140
|
}
|
|
6141
|
+
getLateralColumnMaxDistance(label, orientation, baseAnchor) {
|
|
6142
|
+
const chipId = label.pinIds.map((pid) => this.pinMap[pid]?.chipId).find(Boolean);
|
|
6143
|
+
const chip = chipId ? this.chipObstacleSpatialIndex.chips.find((c) => c.chipId === chipId) : null;
|
|
6144
|
+
if (chip) {
|
|
6145
|
+
if (orientation === "y-")
|
|
6146
|
+
return Math.max(0, baseAnchor.y - chip.bounds.minY);
|
|
6147
|
+
if (orientation === "y+")
|
|
6148
|
+
return Math.max(0, chip.bounds.maxY - baseAnchor.y);
|
|
6149
|
+
if (orientation === "x-")
|
|
6150
|
+
return Math.max(0, baseAnchor.x - chip.bounds.minX);
|
|
6151
|
+
if (orientation === "x+")
|
|
6152
|
+
return Math.max(0, chip.bounds.maxX - baseAnchor.x);
|
|
6153
|
+
}
|
|
6154
|
+
return this.getSearchDistanceLimit(label, orientation);
|
|
6155
|
+
}
|
|
6068
6156
|
createCandidate(label, anchorPoint, orientation) {
|
|
6069
6157
|
const { width, height } = getDimsForOrientation({
|
|
6070
6158
|
orientation,
|
|
@@ -6111,6 +6199,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6111
6199
|
if (tracePathCrossesAnyTrace(connectorTrace, this.traceMap)) {
|
|
6112
6200
|
return "trace-collision";
|
|
6113
6201
|
}
|
|
6202
|
+
for (const chip of this.chipObstacleSpatialIndex.chips) {
|
|
6203
|
+
if (tracePathCrossesAnyBounds(connectorTrace, chip.bounds)) {
|
|
6204
|
+
return "chip-collision";
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
6114
6207
|
for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
|
|
6115
6208
|
if (i === labelIndex) continue;
|
|
6116
6209
|
const otherLabel = this.outputNetLabelPlacements[i];
|
|
@@ -7512,6 +7605,338 @@ globalConnNetId: ${label.globalConnNetId}`
|
|
|
7512
7605
|
}
|
|
7513
7606
|
};
|
|
7514
7607
|
|
|
7608
|
+
// lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts
|
|
7609
|
+
var ANCHOR_TRACE_CLEARANCE = 1e-4;
|
|
7610
|
+
var SEGMENT_PARALLEL_EPS = 1e-6;
|
|
7611
|
+
var CANDIDATE_STEP2 = 0.1;
|
|
7612
|
+
var OUTWARD_DIR = {
|
|
7613
|
+
"x+": { x: 1, y: 0 },
|
|
7614
|
+
"x-": { x: -1, y: 0 },
|
|
7615
|
+
"y+": { x: 0, y: 1 },
|
|
7616
|
+
"y-": { x: 0, y: -1 }
|
|
7617
|
+
};
|
|
7618
|
+
var CANDIDATE_STATUS_COLOR = {
|
|
7619
|
+
ok: "green",
|
|
7620
|
+
"label-collision": "orange",
|
|
7621
|
+
"trace-collision": "darkorange",
|
|
7622
|
+
"chip-collision": "red"
|
|
7623
|
+
};
|
|
7624
|
+
var CANDIDATE_STATUS_FILL = {
|
|
7625
|
+
ok: "rgba(0, 200, 0, 0.25)",
|
|
7626
|
+
"label-collision": "rgba(255, 160, 0, 0.2)",
|
|
7627
|
+
"trace-collision": "rgba(200, 80, 0, 0.2)",
|
|
7628
|
+
"chip-collision": "rgba(220, 0, 0, 0.15)"
|
|
7629
|
+
};
|
|
7630
|
+
function boundsOverlap(a, b) {
|
|
7631
|
+
return a.minX < b.maxX - 1e-9 && a.maxX > b.minX + 1e-9 && a.minY < b.maxY - 1e-9 && a.maxY > b.minY + 1e-9;
|
|
7632
|
+
}
|
|
7633
|
+
function sampleAnchorsAlongSegment(a, b) {
|
|
7634
|
+
const dx = b.x - a.x;
|
|
7635
|
+
const dy = b.y - a.y;
|
|
7636
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
7637
|
+
const steps = Math.max(1, Math.round(len / CANDIDATE_STEP2));
|
|
7638
|
+
const anchors = [];
|
|
7639
|
+
for (let k = 0; k <= steps; k++) {
|
|
7640
|
+
const t = k / steps;
|
|
7641
|
+
anchors.push({ x: a.x + t * dx, y: a.y + t * dy });
|
|
7642
|
+
}
|
|
7643
|
+
return anchors;
|
|
7644
|
+
}
|
|
7645
|
+
var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
|
|
7646
|
+
inputProblem;
|
|
7647
|
+
traces;
|
|
7648
|
+
netLabelPlacements;
|
|
7649
|
+
outputNetLabelPlacements;
|
|
7650
|
+
currentCollision = null;
|
|
7651
|
+
currentLabelToMove = null;
|
|
7652
|
+
candidateResults = [];
|
|
7653
|
+
chipIndex;
|
|
7654
|
+
traceMap;
|
|
7655
|
+
skippedCollisionKeys = /* @__PURE__ */ new Set();
|
|
7656
|
+
labelsToTry = [];
|
|
7657
|
+
candidateQueue = [];
|
|
7658
|
+
candidateIndex = 0;
|
|
7659
|
+
constructor(params) {
|
|
7660
|
+
super();
|
|
7661
|
+
this.inputProblem = params.inputProblem;
|
|
7662
|
+
this.traces = params.traces;
|
|
7663
|
+
this.netLabelPlacements = params.netLabelPlacements;
|
|
7664
|
+
this.outputNetLabelPlacements = [...params.netLabelPlacements];
|
|
7665
|
+
this.chipIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
|
|
7666
|
+
this.traceMap = Object.fromEntries(
|
|
7667
|
+
params.traces.map((t) => [t.mspPairId, t])
|
|
7668
|
+
);
|
|
7669
|
+
}
|
|
7670
|
+
getConstructorParams() {
|
|
7671
|
+
return {
|
|
7672
|
+
inputProblem: this.inputProblem,
|
|
7673
|
+
traces: this.traces,
|
|
7674
|
+
netLabelPlacements: this.netLabelPlacements
|
|
7675
|
+
};
|
|
7676
|
+
}
|
|
7677
|
+
getOutput() {
|
|
7678
|
+
return { netLabelPlacements: this.outputNetLabelPlacements };
|
|
7679
|
+
}
|
|
7680
|
+
labelBounds(label) {
|
|
7681
|
+
return getRectBounds(label.center, label.width, label.height);
|
|
7682
|
+
}
|
|
7683
|
+
collisionKey(a, b) {
|
|
7684
|
+
return [a.globalConnNetId, b.globalConnNetId].sort().join("::");
|
|
7685
|
+
}
|
|
7686
|
+
findNextCollidingPair() {
|
|
7687
|
+
const labels = this.outputNetLabelPlacements;
|
|
7688
|
+
for (let i = 0; i < labels.length; i++) {
|
|
7689
|
+
for (let j = i + 1; j < labels.length; j++) {
|
|
7690
|
+
const a = labels[i];
|
|
7691
|
+
const b = labels[j];
|
|
7692
|
+
if (a.globalConnNetId === b.globalConnNetId) continue;
|
|
7693
|
+
if (this.skippedCollisionKeys.has(this.collisionKey(a, b))) continue;
|
|
7694
|
+
if (boundsOverlap(this.labelBounds(a), this.labelBounds(b)))
|
|
7695
|
+
return [a, b];
|
|
7696
|
+
}
|
|
7697
|
+
}
|
|
7698
|
+
return null;
|
|
7699
|
+
}
|
|
7700
|
+
netLabelWidthOf(label) {
|
|
7701
|
+
if (label.orientation === "x+" || label.orientation === "x-")
|
|
7702
|
+
return label.width;
|
|
7703
|
+
return label.height;
|
|
7704
|
+
}
|
|
7705
|
+
buildCandidatesForLabel(label) {
|
|
7706
|
+
const netLabelWidth = this.netLabelWidthOf(label);
|
|
7707
|
+
const candidates = [];
|
|
7708
|
+
const buildCandidate = (orientation, anchor, hostPairId, hostSegIndex) => {
|
|
7709
|
+
const { width, height } = getDimsForOrientation({
|
|
7710
|
+
orientation,
|
|
7711
|
+
netLabelWidth
|
|
7712
|
+
});
|
|
7713
|
+
const baseCenter = getCenterFromAnchor(anchor, orientation, width, height);
|
|
7714
|
+
const outwardDir = OUTWARD_DIR[orientation];
|
|
7715
|
+
const center = {
|
|
7716
|
+
x: baseCenter.x + outwardDir.x * ANCHOR_TRACE_CLEARANCE,
|
|
7717
|
+
y: baseCenter.y + outwardDir.y * ANCHOR_TRACE_CLEARANCE
|
|
7718
|
+
};
|
|
7719
|
+
return {
|
|
7720
|
+
orientation,
|
|
7721
|
+
anchor,
|
|
7722
|
+
center,
|
|
7723
|
+
width,
|
|
7724
|
+
height,
|
|
7725
|
+
bounds: getRectBounds(center, width, height),
|
|
7726
|
+
hostPairId,
|
|
7727
|
+
hostSegIndex,
|
|
7728
|
+
status: null
|
|
7729
|
+
};
|
|
7730
|
+
};
|
|
7731
|
+
const isPortOnly = label.mspConnectionPairIds.length === 0;
|
|
7732
|
+
if (isPortOnly) {
|
|
7733
|
+
const allOrientations = ["x+", "x-", "y+", "y-"];
|
|
7734
|
+
const orderedOrientations = [
|
|
7735
|
+
label.orientation,
|
|
7736
|
+
...allOrientations.filter((o) => o !== label.orientation)
|
|
7737
|
+
];
|
|
7738
|
+
for (const orientation of orderedOrientations) {
|
|
7739
|
+
candidates.push(buildCandidate(orientation, label.anchorPoint));
|
|
7740
|
+
}
|
|
7741
|
+
} else {
|
|
7742
|
+
for (const mspPairId of label.mspConnectionPairIds) {
|
|
7743
|
+
const trace = this.traceMap[mspPairId];
|
|
7744
|
+
if (!trace) continue;
|
|
7745
|
+
const pts = trace.tracePath;
|
|
7746
|
+
for (let si = 0; si < pts.length - 1; si++) {
|
|
7747
|
+
const segStart = pts[si];
|
|
7748
|
+
const segEnd = pts[si + 1];
|
|
7749
|
+
const isHorizontal3 = Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS;
|
|
7750
|
+
const isVertical4 = Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS;
|
|
7751
|
+
if (!isHorizontal3 && !isVertical4) continue;
|
|
7752
|
+
let perpendicularOrientations;
|
|
7753
|
+
if (isHorizontal3) {
|
|
7754
|
+
perpendicularOrientations = ["y+", "y-"];
|
|
7755
|
+
} else {
|
|
7756
|
+
perpendicularOrientations = ["x+", "x-"];
|
|
7757
|
+
}
|
|
7758
|
+
for (const anchor of sampleAnchorsAlongSegment(segStart, segEnd)) {
|
|
7759
|
+
for (const orientation of perpendicularOrientations) {
|
|
7760
|
+
candidates.push(
|
|
7761
|
+
buildCandidate(
|
|
7762
|
+
orientation,
|
|
7763
|
+
anchor,
|
|
7764
|
+
mspPairId,
|
|
7765
|
+
si
|
|
7766
|
+
)
|
|
7767
|
+
);
|
|
7768
|
+
}
|
|
7769
|
+
}
|
|
7770
|
+
}
|
|
7771
|
+
}
|
|
7772
|
+
}
|
|
7773
|
+
return candidates;
|
|
7774
|
+
}
|
|
7775
|
+
checkCandidate(candidate, movingLabelNetId, obstacleLabels) {
|
|
7776
|
+
const { bounds, hostPairId, hostSegIndex } = candidate;
|
|
7777
|
+
if (this.chipIndex.getChipsInBounds(bounds).length > 0)
|
|
7778
|
+
return "chip-collision";
|
|
7779
|
+
if (rectIntersectsAnyTrace(bounds, this.traceMap, hostPairId, hostSegIndex).hasIntersection) {
|
|
7780
|
+
return "trace-collision";
|
|
7781
|
+
}
|
|
7782
|
+
for (const obstacle of obstacleLabels) {
|
|
7783
|
+
if (obstacle.globalConnNetId === movingLabelNetId) continue;
|
|
7784
|
+
if (boundsOverlap(bounds, this.labelBounds(obstacle)))
|
|
7785
|
+
return "label-collision";
|
|
7786
|
+
}
|
|
7787
|
+
return "ok";
|
|
7788
|
+
}
|
|
7789
|
+
beginSearchForLabel(label) {
|
|
7790
|
+
this.currentLabelToMove = label;
|
|
7791
|
+
this.candidateQueue = this.buildCandidatesForLabel(label);
|
|
7792
|
+
this.candidateIndex = 0;
|
|
7793
|
+
this.candidateResults = [];
|
|
7794
|
+
}
|
|
7795
|
+
clearActiveSearch() {
|
|
7796
|
+
this.currentCollision = null;
|
|
7797
|
+
this.currentLabelToMove = null;
|
|
7798
|
+
this.labelsToTry = [];
|
|
7799
|
+
this.candidateQueue = [];
|
|
7800
|
+
this.candidateIndex = 0;
|
|
7801
|
+
this.candidateResults = [];
|
|
7802
|
+
}
|
|
7803
|
+
_step() {
|
|
7804
|
+
if (!this.currentCollision) {
|
|
7805
|
+
const pair = this.findNextCollidingPair();
|
|
7806
|
+
if (!pair) {
|
|
7807
|
+
this.solved = true;
|
|
7808
|
+
return;
|
|
7809
|
+
}
|
|
7810
|
+
this.currentCollision = pair;
|
|
7811
|
+
this.labelsToTry = [pair[1], pair[0]];
|
|
7812
|
+
this.beginSearchForLabel(this.labelsToTry.shift());
|
|
7813
|
+
return;
|
|
7814
|
+
}
|
|
7815
|
+
if (this.candidateIndex >= this.candidateQueue.length) {
|
|
7816
|
+
if (this.labelsToTry.length > 0) {
|
|
7817
|
+
this.beginSearchForLabel(this.labelsToTry.shift());
|
|
7818
|
+
} else {
|
|
7819
|
+
this.skippedCollisionKeys.add(
|
|
7820
|
+
this.collisionKey(this.currentCollision[0], this.currentCollision[1])
|
|
7821
|
+
);
|
|
7822
|
+
this.clearActiveSearch();
|
|
7823
|
+
}
|
|
7824
|
+
return;
|
|
7825
|
+
}
|
|
7826
|
+
const candidate = this.candidateQueue[this.candidateIndex++];
|
|
7827
|
+
const [labelA, labelB] = this.currentCollision;
|
|
7828
|
+
let fixedLabel;
|
|
7829
|
+
if (this.currentLabelToMove === labelB) {
|
|
7830
|
+
fixedLabel = labelA;
|
|
7831
|
+
} else {
|
|
7832
|
+
fixedLabel = labelB;
|
|
7833
|
+
}
|
|
7834
|
+
const obstacleLabels = [
|
|
7835
|
+
...this.outputNetLabelPlacements.filter(
|
|
7836
|
+
(l) => l !== labelA && l !== labelB
|
|
7837
|
+
),
|
|
7838
|
+
fixedLabel
|
|
7839
|
+
];
|
|
7840
|
+
const status = this.checkCandidate(
|
|
7841
|
+
candidate,
|
|
7842
|
+
this.currentLabelToMove.globalConnNetId,
|
|
7843
|
+
obstacleLabels
|
|
7844
|
+
);
|
|
7845
|
+
candidate.status = status;
|
|
7846
|
+
this.candidateResults.push({ ...candidate });
|
|
7847
|
+
if (status === "ok") {
|
|
7848
|
+
const idx = this.outputNetLabelPlacements.indexOf(
|
|
7849
|
+
this.currentLabelToMove
|
|
7850
|
+
);
|
|
7851
|
+
if (idx !== -1) {
|
|
7852
|
+
this.outputNetLabelPlacements[idx] = {
|
|
7853
|
+
...this.currentLabelToMove,
|
|
7854
|
+
orientation: candidate.orientation,
|
|
7855
|
+
anchorPoint: candidate.anchor,
|
|
7856
|
+
width: candidate.width,
|
|
7857
|
+
height: candidate.height,
|
|
7858
|
+
center: candidate.center
|
|
7859
|
+
};
|
|
7860
|
+
}
|
|
7861
|
+
this.clearActiveSearch();
|
|
7862
|
+
}
|
|
7863
|
+
}
|
|
7864
|
+
visualize() {
|
|
7865
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
7866
|
+
if (!graphics.lines) graphics.lines = [];
|
|
7867
|
+
if (!graphics.rects) graphics.rects = [];
|
|
7868
|
+
if (!graphics.points) graphics.points = [];
|
|
7869
|
+
for (const trace of this.traces) {
|
|
7870
|
+
graphics.lines.push({
|
|
7871
|
+
points: trace.tracePath,
|
|
7872
|
+
strokeColor: "purple"
|
|
7873
|
+
});
|
|
7874
|
+
}
|
|
7875
|
+
for (const label of this.outputNetLabelPlacements) {
|
|
7876
|
+
const isInActiveCollision = this.currentCollision != null && (label === this.currentCollision[0] || label === this.currentCollision[1]);
|
|
7877
|
+
let labelFill;
|
|
7878
|
+
let labelStroke;
|
|
7879
|
+
let labelText;
|
|
7880
|
+
let pointColor;
|
|
7881
|
+
if (isInActiveCollision) {
|
|
7882
|
+
labelFill = "rgba(255, 0, 0, 0.2)";
|
|
7883
|
+
labelStroke = "red";
|
|
7884
|
+
labelText = `netId: ${label.netId}
|
|
7885
|
+
globalConnNetId: ${label.globalConnNetId}
|
|
7886
|
+
\u26A0 COLLIDING`;
|
|
7887
|
+
pointColor = "red";
|
|
7888
|
+
} else {
|
|
7889
|
+
labelFill = getColorFromString(label.globalConnNetId, 0.35);
|
|
7890
|
+
labelStroke = getColorFromString(label.globalConnNetId, 0.9);
|
|
7891
|
+
labelText = `netId: ${label.netId}
|
|
7892
|
+
globalConnNetId: ${label.globalConnNetId}`;
|
|
7893
|
+
pointColor = getColorFromString(label.globalConnNetId, 0.9);
|
|
7894
|
+
}
|
|
7895
|
+
graphics.rects.push({
|
|
7896
|
+
center: label.center,
|
|
7897
|
+
width: label.width,
|
|
7898
|
+
height: label.height,
|
|
7899
|
+
fill: labelFill,
|
|
7900
|
+
strokeColor: labelStroke,
|
|
7901
|
+
label: labelText
|
|
7902
|
+
});
|
|
7903
|
+
graphics.points.push({
|
|
7904
|
+
x: label.anchorPoint.x,
|
|
7905
|
+
y: label.anchorPoint.y,
|
|
7906
|
+
color: pointColor,
|
|
7907
|
+
label: `anchorPoint
|
|
7908
|
+
orientation: ${label.orientation}`
|
|
7909
|
+
});
|
|
7910
|
+
}
|
|
7911
|
+
const movingNetId = this.currentLabelToMove ? this.currentLabelToMove.netId : "?";
|
|
7912
|
+
for (const c of this.candidateResults) {
|
|
7913
|
+
const statusColor = CANDIDATE_STATUS_COLOR[c.status];
|
|
7914
|
+
const statusFill = CANDIDATE_STATUS_FILL[c.status];
|
|
7915
|
+
let strokeDash;
|
|
7916
|
+
if (c.status !== "ok") strokeDash = "4 2";
|
|
7917
|
+
graphics.rects.push({
|
|
7918
|
+
center: c.center,
|
|
7919
|
+
width: c.width,
|
|
7920
|
+
height: c.height,
|
|
7921
|
+
fill: statusFill,
|
|
7922
|
+
strokeColor: statusColor,
|
|
7923
|
+
strokeDash,
|
|
7924
|
+
label: `candidate: ${c.status}
|
|
7925
|
+
orientation: ${c.orientation}
|
|
7926
|
+
moving: ${movingNetId}`
|
|
7927
|
+
});
|
|
7928
|
+
graphics.points.push({
|
|
7929
|
+
x: c.anchor.x,
|
|
7930
|
+
y: c.anchor.y,
|
|
7931
|
+
color: statusColor,
|
|
7932
|
+
label: `candidate anchor
|
|
7933
|
+
${c.status}`
|
|
7934
|
+
});
|
|
7935
|
+
}
|
|
7936
|
+
return graphics;
|
|
7937
|
+
}
|
|
7938
|
+
};
|
|
7939
|
+
|
|
7515
7940
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
7516
7941
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
7517
7942
|
return {
|
|
@@ -7537,6 +7962,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
7537
7962
|
vccNetLabelCornerPlacementSolver;
|
|
7538
7963
|
traceAnchoredNetLabelOverlapSolver;
|
|
7539
7964
|
netLabelTraceCollisionSolver;
|
|
7965
|
+
netLabelNetLabelCollisionSolver;
|
|
7540
7966
|
startTimeOfPhase;
|
|
7541
7967
|
endTimeOfPhase;
|
|
7542
7968
|
timeSpentOnPhase;
|
|
@@ -7728,6 +8154,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
7728
8154
|
netLabelPlacements: instance.traceAnchoredNetLabelOverlapSolver.outputNetLabelPlacements
|
|
7729
8155
|
}
|
|
7730
8156
|
]
|
|
8157
|
+
),
|
|
8158
|
+
definePipelineStep(
|
|
8159
|
+
"netLabelNetLabelCollisionSolver",
|
|
8160
|
+
NetLabelNetLabelCollisionSolver,
|
|
8161
|
+
(instance) => [
|
|
8162
|
+
{
|
|
8163
|
+
inputProblem: instance.inputProblem,
|
|
8164
|
+
traces: instance.netLabelTraceCollisionSolver.getOutput().traces,
|
|
8165
|
+
netLabelPlacements: instance.netLabelTraceCollisionSolver.getOutput().netLabelPlacements
|
|
8166
|
+
}
|
|
8167
|
+
]
|
|
7731
8168
|
)
|
|
7732
8169
|
];
|
|
7733
8170
|
constructor(inputProblem) {
|