@tscircuit/schematic-trace-solver 0.0.81 → 0.0.83
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/biome.json +5 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +253 -85
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +126 -12
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +5 -1
- package/lib/solvers/Example28Solver/Example28Solver.ts +114 -2
- package/lib/solvers/Example28Solver/reroute.ts +32 -21
- package/package.json +1 -1
- package/tests/examples/__snapshots__/example03.snap.svg +64 -64
- package/tests/examples/__snapshots__/example28.snap.svg +21 -18
- package/tests/examples/__snapshots__/example32.snap.svg +3 -3
- package/tests/examples/__snapshots__/example48.snap.svg +30 -30
- package/tests/repros/__snapshots__/repro51-overlap-junction-crossing.snap.svg +177 -0
- package/tests/repros/assets/repro51-overlap-junction-crossing.input.json +129 -0
- package/tests/repros/repro51-overlap-junction-crossing.test.ts +105 -0
- package/tests/repros/rotated-components-rail-label.test.ts +68 -0
package/biome.json
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -666,6 +666,7 @@ declare class Example28Solver extends BaseSolver {
|
|
|
666
666
|
currentOverlap: TraceLabelOverlap | null;
|
|
667
667
|
currentCandidateResults: RerouteCandidateResult[];
|
|
668
668
|
private chipObstacles;
|
|
669
|
+
private pinMap;
|
|
669
670
|
constructor(params: Example28SolverParams);
|
|
670
671
|
getConstructorParams(): ConstructorParameters<typeof Example28Solver>[0];
|
|
671
672
|
_step(): void;
|
|
@@ -679,6 +680,7 @@ declare class Example28Solver extends BaseSolver {
|
|
|
679
680
|
private isOverlapStillPresent;
|
|
680
681
|
private getCurrentTrace;
|
|
681
682
|
private rerouteOverlappingTrace;
|
|
683
|
+
private tryMoveLabelOutward;
|
|
682
684
|
visualize(): GraphicsObject;
|
|
683
685
|
}
|
|
684
686
|
|
|
@@ -695,7 +697,7 @@ type CandidateLabel = {
|
|
|
695
697
|
height: number;
|
|
696
698
|
};
|
|
697
699
|
type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
|
|
698
|
-
type CandidatePhase = "rotate" | "shift" | "lateral-shift";
|
|
700
|
+
type CandidatePhase = "rotate" | "trace-anchor" | "shift" | "lateral-shift";
|
|
699
701
|
type EvaluatedCandidate = CandidateLabel & {
|
|
700
702
|
status: CandidateStatus$2;
|
|
701
703
|
selected: boolean;
|
|
@@ -733,6 +735,8 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
733
735
|
private setCurrentLabel;
|
|
734
736
|
private findCorrectedCandidate;
|
|
735
737
|
private findValidRotatedCandidate;
|
|
738
|
+
private findValidTraceAnchorCandidate;
|
|
739
|
+
private getTraceAnchorCandidatePoints;
|
|
736
740
|
private getAvailableOrientations;
|
|
737
741
|
private findValidShiftedCandidate;
|
|
738
742
|
/**
|
|
@@ -757,6 +761,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
757
761
|
private getNetLabelWidth;
|
|
758
762
|
private getNetLabelHeight;
|
|
759
763
|
private getCandidateStatus;
|
|
764
|
+
private isAcceptableTraceAnchorChipCollision;
|
|
760
765
|
private getBoundsStatus;
|
|
761
766
|
private isTraceTooCloseToLabel;
|
|
762
767
|
private getLabelTraceClearanceBounds;
|
package/dist/index.js
CHANGED
|
@@ -5162,6 +5162,54 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
5162
5162
|
}
|
|
5163
5163
|
};
|
|
5164
5164
|
|
|
5165
|
+
// lib/solvers/AvailableNetOrientationSolver/traces.ts
|
|
5166
|
+
var getPinMap = (inputProblem) => {
|
|
5167
|
+
const pinMap = {};
|
|
5168
|
+
for (const chip of inputProblem.chips) {
|
|
5169
|
+
for (const pin of chip.pins) {
|
|
5170
|
+
pinMap[pin.pinId] = { ...pin, chipId: chip.chipId };
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
return pinMap;
|
|
5174
|
+
};
|
|
5175
|
+
var getTracePins = (label, pinMap) => {
|
|
5176
|
+
const pins = label.pinIds.flatMap((pinId) => {
|
|
5177
|
+
const pin = pinMap[pinId];
|
|
5178
|
+
return pin ? [pin] : [];
|
|
5179
|
+
});
|
|
5180
|
+
if (pins.length >= 2) return [pins[0], pins[1]];
|
|
5181
|
+
if (pins.length === 1) return [pins[0], pins[0]];
|
|
5182
|
+
const syntheticPin = {
|
|
5183
|
+
pinId: `${label.globalConnNetId}-netlabel-anchor`,
|
|
5184
|
+
x: label.anchorPoint.x,
|
|
5185
|
+
y: label.anchorPoint.y,
|
|
5186
|
+
chipId: "available-net-orientation"
|
|
5187
|
+
};
|
|
5188
|
+
return [syntheticPin, syntheticPin];
|
|
5189
|
+
};
|
|
5190
|
+
var toNetLabelPlacementPatch = (candidate) => ({
|
|
5191
|
+
orientation: candidate.orientation,
|
|
5192
|
+
anchorPoint: candidate.anchorPoint,
|
|
5193
|
+
center: candidate.center,
|
|
5194
|
+
width: candidate.width,
|
|
5195
|
+
height: candidate.height
|
|
5196
|
+
});
|
|
5197
|
+
|
|
5198
|
+
// lib/utils/dir.ts
|
|
5199
|
+
var dir = (facingDirection) => {
|
|
5200
|
+
switch (facingDirection) {
|
|
5201
|
+
case "x+":
|
|
5202
|
+
return { x: 1, y: 0 };
|
|
5203
|
+
case "x-":
|
|
5204
|
+
return { x: -1, y: 0 };
|
|
5205
|
+
case "y+":
|
|
5206
|
+
return { x: 0, y: 1 };
|
|
5207
|
+
case "y-":
|
|
5208
|
+
return { x: 0, y: -1 };
|
|
5209
|
+
}
|
|
5210
|
+
throw new Error(`Invalid facing direction: ${facingDirection}`);
|
|
5211
|
+
};
|
|
5212
|
+
|
|
5165
5213
|
// lib/solvers/Example28Solver/types.ts
|
|
5166
5214
|
var EPS7 = 1e-9;
|
|
5167
5215
|
|
|
@@ -5369,24 +5417,8 @@ var moveAttachedLabelsToReroutedTrace = ({
|
|
|
5369
5417
|
};
|
|
5370
5418
|
});
|
|
5371
5419
|
|
|
5372
|
-
// lib/utils/dir.ts
|
|
5373
|
-
var dir = (facingDirection) => {
|
|
5374
|
-
switch (facingDirection) {
|
|
5375
|
-
case "x+":
|
|
5376
|
-
return { x: 1, y: 0 };
|
|
5377
|
-
case "x-":
|
|
5378
|
-
return { x: -1, y: 0 };
|
|
5379
|
-
case "y+":
|
|
5380
|
-
return { x: 0, y: 1 };
|
|
5381
|
-
case "y-":
|
|
5382
|
-
return { x: 0, y: -1 };
|
|
5383
|
-
}
|
|
5384
|
-
throw new Error(`Invalid facing direction: ${facingDirection}`);
|
|
5385
|
-
};
|
|
5386
|
-
|
|
5387
5420
|
// lib/solvers/Example28Solver/reroute.ts
|
|
5388
|
-
var
|
|
5389
|
-
var LABEL_HUG_CLEARANCE = 1e-3;
|
|
5421
|
+
var LABEL_CLEARANCE = 0.1;
|
|
5390
5422
|
var findBestReroutePath = ({
|
|
5391
5423
|
trace,
|
|
5392
5424
|
obstacleLabel,
|
|
@@ -5447,7 +5479,7 @@ var generateRerouteCandidateResults = ({
|
|
|
5447
5479
|
trace,
|
|
5448
5480
|
label,
|
|
5449
5481
|
problem: inputProblem,
|
|
5450
|
-
paddingBuffer:
|
|
5482
|
+
paddingBuffer: LABEL_CLEARANCE,
|
|
5451
5483
|
detourCount: 0
|
|
5452
5484
|
}),
|
|
5453
5485
|
...generateEndpointDetourCandidates(trace, label)
|
|
@@ -5513,8 +5545,10 @@ var generateEndpointDetourCandidates = (trace, label) => {
|
|
|
5513
5545
|
const start = trace.tracePath[0];
|
|
5514
5546
|
const end = trace.tracePath[trace.tracePath.length - 1];
|
|
5515
5547
|
if (!start || !end) return [];
|
|
5548
|
+
const startExit = trace.tracePath[1] ?? start;
|
|
5549
|
+
const endEntry = trace.tracePath[trace.tracePath.length - 2] ?? end;
|
|
5516
5550
|
const bounds = getRectBounds(label.center, label.width, label.height);
|
|
5517
|
-
const padding =
|
|
5551
|
+
const padding = LABEL_CLEARANCE;
|
|
5518
5552
|
const labelDirection = dir(label.orientation);
|
|
5519
5553
|
const labelSideX = labelDirection.x < 0 ? bounds.minX - padding : bounds.maxX + padding;
|
|
5520
5554
|
const oppositeSideX = labelDirection.x < 0 ? bounds.maxX + padding : bounds.minX - padding;
|
|
@@ -5525,18 +5559,22 @@ var generateEndpointDetourCandidates = (trace, label) => {
|
|
|
5525
5559
|
candidates.push(
|
|
5526
5560
|
[
|
|
5527
5561
|
start,
|
|
5528
|
-
|
|
5562
|
+
startExit,
|
|
5563
|
+
{ x: startExit.x, y: topY },
|
|
5529
5564
|
{ x: sideX, y: topY },
|
|
5530
5565
|
{ x: sideX, y: bottomY },
|
|
5531
|
-
{ x:
|
|
5566
|
+
{ x: endEntry.x, y: bottomY },
|
|
5567
|
+
endEntry,
|
|
5532
5568
|
end
|
|
5533
5569
|
],
|
|
5534
5570
|
[
|
|
5535
5571
|
start,
|
|
5536
|
-
|
|
5572
|
+
startExit,
|
|
5573
|
+
{ x: startExit.x, y: bottomY },
|
|
5537
5574
|
{ x: sideX, y: bottomY },
|
|
5538
5575
|
{ x: sideX, y: topY },
|
|
5539
|
-
{ x:
|
|
5576
|
+
{ x: endEntry.x, y: topY },
|
|
5577
|
+
endEntry,
|
|
5540
5578
|
end
|
|
5541
5579
|
]
|
|
5542
5580
|
);
|
|
@@ -5547,30 +5585,36 @@ var generateLabelHugCandidates = (trace, label) => {
|
|
|
5547
5585
|
const start = trace.tracePath[0];
|
|
5548
5586
|
const end = trace.tracePath[trace.tracePath.length - 1];
|
|
5549
5587
|
if (!start || !end) return [];
|
|
5588
|
+
const startExit = trace.tracePath[1] ?? start;
|
|
5589
|
+
const endEntry = trace.tracePath[trace.tracePath.length - 2] ?? end;
|
|
5550
5590
|
const labelDirection = dir(label.orientation);
|
|
5551
5591
|
if (labelDirection.x === 0) return [];
|
|
5552
5592
|
const bounds = getRectBounds(label.center, label.width, label.height);
|
|
5553
|
-
const sideX = labelDirection.x < 0 ? bounds.minX -
|
|
5554
|
-
const topY = bounds.maxY +
|
|
5555
|
-
const bottomY = bounds.minY -
|
|
5556
|
-
const startsAboveEnd =
|
|
5593
|
+
const sideX = labelDirection.x < 0 ? bounds.minX - LABEL_CLEARANCE : bounds.maxX + LABEL_CLEARANCE;
|
|
5594
|
+
const topY = bounds.maxY + LABEL_CLEARANCE;
|
|
5595
|
+
const bottomY = bounds.minY - LABEL_CLEARANCE;
|
|
5596
|
+
const startsAboveEnd = startExit.y >= endEntry.y;
|
|
5557
5597
|
const firstY = startsAboveEnd ? topY : bottomY;
|
|
5558
5598
|
const secondY = startsAboveEnd ? bottomY : topY;
|
|
5559
5599
|
return [
|
|
5560
5600
|
[
|
|
5561
5601
|
start,
|
|
5562
|
-
|
|
5602
|
+
startExit,
|
|
5603
|
+
{ x: startExit.x, y: firstY },
|
|
5563
5604
|
{ x: sideX, y: firstY },
|
|
5564
5605
|
{ x: sideX, y: secondY },
|
|
5565
|
-
{ x:
|
|
5606
|
+
{ x: endEntry.x, y: secondY },
|
|
5607
|
+
endEntry,
|
|
5566
5608
|
end
|
|
5567
5609
|
],
|
|
5568
5610
|
[
|
|
5569
5611
|
start,
|
|
5570
|
-
|
|
5612
|
+
startExit,
|
|
5613
|
+
{ x: startExit.x, y: secondY },
|
|
5571
5614
|
{ x: sideX, y: secondY },
|
|
5572
5615
|
{ x: sideX, y: firstY },
|
|
5573
|
-
{ x:
|
|
5616
|
+
{ x: endEntry.x, y: firstY },
|
|
5617
|
+
endEntry,
|
|
5574
5618
|
end
|
|
5575
5619
|
]
|
|
5576
5620
|
];
|
|
@@ -5625,9 +5669,9 @@ var generateHorizontalSegmentPushCandidate = (trace, label) => {
|
|
|
5625
5669
|
if (Math.abs(previousAnchor.y - verticalStart.y) >= 1e-9 || Math.abs(verticalEnd.y - nextAnchor.y) >= 1e-9) {
|
|
5626
5670
|
return null;
|
|
5627
5671
|
}
|
|
5628
|
-
let segmentPushX = bounds.minX -
|
|
5672
|
+
let segmentPushX = bounds.minX - LABEL_CLEARANCE;
|
|
5629
5673
|
if (labelDirection.x > 0) {
|
|
5630
|
-
segmentPushX = bounds.maxX +
|
|
5674
|
+
segmentPushX = bounds.maxX + LABEL_CLEARANCE;
|
|
5631
5675
|
}
|
|
5632
5676
|
const segmentPushStartY = getClearedHorizontalY({
|
|
5633
5677
|
start: previousAnchor,
|
|
@@ -5665,9 +5709,9 @@ var getClearedHorizontalY = ({
|
|
|
5665
5709
|
if (!segmentIntersectsRect2(start, end, labelBounds)) return start.y;
|
|
5666
5710
|
const labelCenterY = (labelBounds.minY + labelBounds.maxY) / 2;
|
|
5667
5711
|
if (start.y >= labelCenterY) {
|
|
5668
|
-
return labelBounds.maxY +
|
|
5712
|
+
return labelBounds.maxY + LABEL_CLEARANCE;
|
|
5669
5713
|
}
|
|
5670
|
-
return labelBounds.minY -
|
|
5714
|
+
return labelBounds.minY - LABEL_CLEARANCE;
|
|
5671
5715
|
};
|
|
5672
5716
|
var markSelectedCandidate = (candidateResults, bestPath) => {
|
|
5673
5717
|
if (!bestPath) return;
|
|
@@ -5800,6 +5844,9 @@ ${label.netId ?? label.globalConnNetId}`
|
|
|
5800
5844
|
};
|
|
5801
5845
|
|
|
5802
5846
|
// lib/solvers/Example28Solver/Example28Solver.ts
|
|
5847
|
+
var LABEL_OUTWARD_STEP = 0.1;
|
|
5848
|
+
var LABEL_MAX_OUTWARD_STEPS = 10;
|
|
5849
|
+
var LABEL_TRACE_CLEARANCE = 0.1;
|
|
5803
5850
|
var Example28Solver = class extends BaseSolver {
|
|
5804
5851
|
inputProblem;
|
|
5805
5852
|
traces;
|
|
@@ -5810,6 +5857,7 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5810
5857
|
currentOverlap = null;
|
|
5811
5858
|
currentCandidateResults = [];
|
|
5812
5859
|
chipObstacles;
|
|
5860
|
+
pinMap;
|
|
5813
5861
|
constructor(params) {
|
|
5814
5862
|
super();
|
|
5815
5863
|
this.inputProblem = params.inputProblem;
|
|
@@ -5818,6 +5866,7 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5818
5866
|
this.outputTraces = [...params.traces];
|
|
5819
5867
|
this.outputNetLabelPlacements = [...params.netLabelPlacements];
|
|
5820
5868
|
this.chipObstacles = getObstacleRects(params.inputProblem);
|
|
5869
|
+
this.pinMap = getPinMap(params.inputProblem);
|
|
5821
5870
|
this.initializeQueuedOverlaps();
|
|
5822
5871
|
this.currentOverlap = this.queuedOverlaps[0] ?? null;
|
|
5823
5872
|
}
|
|
@@ -5900,6 +5949,7 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5900
5949
|
);
|
|
5901
5950
|
if (traceIndex === -1) return;
|
|
5902
5951
|
const currentTrace = this.outputTraces[traceIndex];
|
|
5952
|
+
if (this.tryMoveLabelOutward(overlap.label)) return;
|
|
5903
5953
|
const rerouteResult = findBestReroutePath({
|
|
5904
5954
|
trace: currentTrace,
|
|
5905
5955
|
obstacleLabel: overlap.label,
|
|
@@ -5922,6 +5972,62 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5922
5972
|
netLabelPlacements: this.outputNetLabelPlacements
|
|
5923
5973
|
});
|
|
5924
5974
|
}
|
|
5975
|
+
tryMoveLabelOutward(labelToMove) {
|
|
5976
|
+
const labelIndex = this.outputNetLabelPlacements.findIndex(
|
|
5977
|
+
(label2) => label2.globalConnNetId === labelToMove.globalConnNetId && label2.anchorPoint.x === labelToMove.anchorPoint.x && label2.anchorPoint.y === labelToMove.anchorPoint.y && label2.pinIds.join(",") === labelToMove.pinIds.join(",")
|
|
5978
|
+
);
|
|
5979
|
+
if (labelIndex === -1) return false;
|
|
5980
|
+
const label = this.outputNetLabelPlacements[labelIndex];
|
|
5981
|
+
if (label.mspConnectionPairIds.length > 0) return false;
|
|
5982
|
+
if (label.pinIds.length !== 1) return false;
|
|
5983
|
+
if (this.outputNetLabelPlacements.filter(
|
|
5984
|
+
(otherLabel) => otherLabel.globalConnNetId === label.globalConnNetId
|
|
5985
|
+
).length !== 1) {
|
|
5986
|
+
return false;
|
|
5987
|
+
}
|
|
5988
|
+
const outward = dir(label.orientation);
|
|
5989
|
+
if (outward.x === 0 && outward.y === 0) return false;
|
|
5990
|
+
for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
|
|
5991
|
+
const distance3 = step * LABEL_OUTWARD_STEP;
|
|
5992
|
+
const candidate = {
|
|
5993
|
+
...label,
|
|
5994
|
+
anchorPoint: {
|
|
5995
|
+
x: label.anchorPoint.x + outward.x * distance3,
|
|
5996
|
+
y: label.anchorPoint.y + outward.y * distance3
|
|
5997
|
+
},
|
|
5998
|
+
center: {
|
|
5999
|
+
x: label.center.x + outward.x * distance3,
|
|
6000
|
+
y: label.center.y + outward.y * distance3
|
|
6001
|
+
}
|
|
6002
|
+
};
|
|
6003
|
+
const candidateWithClearance = {
|
|
6004
|
+
...candidate,
|
|
6005
|
+
width: candidate.width + LABEL_TRACE_CLEARANCE * 2,
|
|
6006
|
+
height: candidate.height + LABEL_TRACE_CLEARANCE * 2
|
|
6007
|
+
};
|
|
6008
|
+
if (detectTraceLabelOverlap({
|
|
6009
|
+
traces: this.outputTraces,
|
|
6010
|
+
netLabels: [candidateWithClearance]
|
|
6011
|
+
}).length > 0) {
|
|
6012
|
+
continue;
|
|
6013
|
+
}
|
|
6014
|
+
const connectorTrace = createPortOnlyLabelConnectorTrace({
|
|
6015
|
+
label,
|
|
6016
|
+
movedLabel: candidate,
|
|
6017
|
+
pinMap: this.pinMap
|
|
6018
|
+
});
|
|
6019
|
+
if (detectTraceLabelOverlap({
|
|
6020
|
+
traces: [connectorTrace],
|
|
6021
|
+
netLabels: this.outputNetLabelPlacements
|
|
6022
|
+
}).length > 0) {
|
|
6023
|
+
continue;
|
|
6024
|
+
}
|
|
6025
|
+
this.outputNetLabelPlacements[labelIndex] = candidate;
|
|
6026
|
+
this.outputTraces.push(connectorTrace);
|
|
6027
|
+
return true;
|
|
6028
|
+
}
|
|
6029
|
+
return false;
|
|
6030
|
+
}
|
|
5925
6031
|
visualize() {
|
|
5926
6032
|
return visualizeExample28Solver({
|
|
5927
6033
|
inputProblem: this.inputProblem,
|
|
@@ -5933,6 +6039,24 @@ var Example28Solver = class extends BaseSolver {
|
|
|
5933
6039
|
});
|
|
5934
6040
|
}
|
|
5935
6041
|
};
|
|
6042
|
+
var getPortOnlyLabelConnectorMspPairId = (label) => `port-only-label-connector-${label.globalConnNetId}-${label.pinIds[0]}`;
|
|
6043
|
+
var createPortOnlyLabelConnectorTrace = ({
|
|
6044
|
+
label,
|
|
6045
|
+
movedLabel,
|
|
6046
|
+
pinMap
|
|
6047
|
+
}) => {
|
|
6048
|
+
const mspPairId = getPortOnlyLabelConnectorMspPairId(label);
|
|
6049
|
+
return {
|
|
6050
|
+
mspPairId,
|
|
6051
|
+
dcConnNetId: label.dcConnNetId ?? label.globalConnNetId,
|
|
6052
|
+
globalConnNetId: label.globalConnNetId,
|
|
6053
|
+
userNetId: label.netId,
|
|
6054
|
+
pins: getTracePins(label, pinMap),
|
|
6055
|
+
tracePath: [label.anchorPoint, movedLabel.anchorPoint],
|
|
6056
|
+
mspConnectionPairIds: [mspPairId],
|
|
6057
|
+
pinIds: label.pinIds
|
|
6058
|
+
};
|
|
6059
|
+
};
|
|
5936
6060
|
|
|
5937
6061
|
// lib/solvers/AvailableNetOrientationSolver/constants.ts
|
|
5938
6062
|
var LABEL_SEARCH_STEP = 0.05;
|
|
@@ -6046,39 +6170,6 @@ var getMaxSearchDistance = (inputProblem) => {
|
|
|
6046
6170
|
return maxChipWidth * 3;
|
|
6047
6171
|
};
|
|
6048
6172
|
|
|
6049
|
-
// lib/solvers/AvailableNetOrientationSolver/traces.ts
|
|
6050
|
-
var getPinMap = (inputProblem) => {
|
|
6051
|
-
const pinMap = {};
|
|
6052
|
-
for (const chip of inputProblem.chips) {
|
|
6053
|
-
for (const pin of chip.pins) {
|
|
6054
|
-
pinMap[pin.pinId] = { ...pin, chipId: chip.chipId };
|
|
6055
|
-
}
|
|
6056
|
-
}
|
|
6057
|
-
return pinMap;
|
|
6058
|
-
};
|
|
6059
|
-
var getTracePins = (label, pinMap) => {
|
|
6060
|
-
const pins = label.pinIds.flatMap((pinId) => {
|
|
6061
|
-
const pin = pinMap[pinId];
|
|
6062
|
-
return pin ? [pin] : [];
|
|
6063
|
-
});
|
|
6064
|
-
if (pins.length >= 2) return [pins[0], pins[1]];
|
|
6065
|
-
if (pins.length === 1) return [pins[0], pins[0]];
|
|
6066
|
-
const syntheticPin = {
|
|
6067
|
-
pinId: `${label.globalConnNetId}-netlabel-anchor`,
|
|
6068
|
-
x: label.anchorPoint.x,
|
|
6069
|
-
y: label.anchorPoint.y,
|
|
6070
|
-
chipId: "available-net-orientation"
|
|
6071
|
-
};
|
|
6072
|
-
return [syntheticPin, syntheticPin];
|
|
6073
|
-
};
|
|
6074
|
-
var toNetLabelPlacementPatch = (candidate) => ({
|
|
6075
|
-
orientation: candidate.orientation,
|
|
6076
|
-
anchorPoint: candidate.anchorPoint,
|
|
6077
|
-
center: candidate.center,
|
|
6078
|
-
width: candidate.width,
|
|
6079
|
-
height: candidate.height
|
|
6080
|
-
});
|
|
6081
|
-
|
|
6082
6173
|
// lib/solvers/AvailableNetOrientationSolver/visualize.ts
|
|
6083
6174
|
var visualizeAvailableNetOrientationSolver = (params) => {
|
|
6084
6175
|
const graphics = visualizeInputProblem(params.inputProblem);
|
|
@@ -6167,7 +6258,7 @@ var ensureGraphicsArrays = (graphics) => {
|
|
|
6167
6258
|
};
|
|
6168
6259
|
|
|
6169
6260
|
// lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
|
|
6170
|
-
var
|
|
6261
|
+
var LABEL_TRACE_CLEARANCE2 = 0.1;
|
|
6171
6262
|
var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
6172
6263
|
inputProblem;
|
|
6173
6264
|
traces;
|
|
@@ -6246,6 +6337,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6246
6337
|
this.addConnectorTrace(label, candidate, labelIndex);
|
|
6247
6338
|
}
|
|
6248
6339
|
addConnectorTrace(label, candidate, labelIndex) {
|
|
6340
|
+
if (candidate.phase === "trace-anchor") return;
|
|
6249
6341
|
const tracePath = this.getCandidateConnectorTrace(label, candidate);
|
|
6250
6342
|
if (tracePath.length < 2) return;
|
|
6251
6343
|
const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
|
|
@@ -6280,6 +6372,12 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6280
6372
|
orientations
|
|
6281
6373
|
);
|
|
6282
6374
|
if (rotatedCandidate) return rotatedCandidate;
|
|
6375
|
+
const traceAnchorCandidate = this.findValidTraceAnchorCandidate(
|
|
6376
|
+
label,
|
|
6377
|
+
orientations[0],
|
|
6378
|
+
labelIndex
|
|
6379
|
+
);
|
|
6380
|
+
if (traceAnchorCandidate) return traceAnchorCandidate;
|
|
6283
6381
|
const shiftedCandidate = this.findValidShiftedCandidate(
|
|
6284
6382
|
label,
|
|
6285
6383
|
orientations[0],
|
|
@@ -6313,6 +6411,46 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6313
6411
|
}
|
|
6314
6412
|
return null;
|
|
6315
6413
|
}
|
|
6414
|
+
findValidTraceAnchorCandidate(label, orientation, labelIndex) {
|
|
6415
|
+
const direction = dir(orientation);
|
|
6416
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(label).sort(
|
|
6417
|
+
(a, b) => {
|
|
6418
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y;
|
|
6419
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y;
|
|
6420
|
+
return bAlongDirection - aAlongDirection;
|
|
6421
|
+
}
|
|
6422
|
+
);
|
|
6423
|
+
for (const anchorPoint of candidatePoints) {
|
|
6424
|
+
const candidate = this.createCandidate(label, anchorPoint, orientation);
|
|
6425
|
+
const result = this.evaluateCandidate(
|
|
6426
|
+
candidate,
|
|
6427
|
+
label,
|
|
6428
|
+
labelIndex,
|
|
6429
|
+
"trace-anchor"
|
|
6430
|
+
);
|
|
6431
|
+
this.currentCandidateResults.push(result);
|
|
6432
|
+
if (result.status === "valid") {
|
|
6433
|
+
result.selected = true;
|
|
6434
|
+
return result;
|
|
6435
|
+
}
|
|
6436
|
+
}
|
|
6437
|
+
return null;
|
|
6438
|
+
}
|
|
6439
|
+
getTraceAnchorCandidatePoints(label) {
|
|
6440
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6441
|
+
const points = [];
|
|
6442
|
+
for (const traceId of label.mspConnectionPairIds ?? []) {
|
|
6443
|
+
const trace = this.traceMap[traceId];
|
|
6444
|
+
if (!trace) continue;
|
|
6445
|
+
for (const point of trace.tracePath) {
|
|
6446
|
+
const key = `${point.x.toFixed(9)},${point.y.toFixed(9)}`;
|
|
6447
|
+
if (seen.has(key)) continue;
|
|
6448
|
+
seen.add(key);
|
|
6449
|
+
points.push(point);
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
return points;
|
|
6453
|
+
}
|
|
6316
6454
|
getAvailableOrientations(label) {
|
|
6317
6455
|
const effectiveNetId = label.netId ?? label.globalConnNetId;
|
|
6318
6456
|
return this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? [];
|
|
@@ -6581,16 +6719,28 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6581
6719
|
}
|
|
6582
6720
|
getCandidateStatus(params) {
|
|
6583
6721
|
const { candidate, label, labelIndex, phase } = params;
|
|
6722
|
+
const bounds = getRectBounds(
|
|
6723
|
+
candidate.center,
|
|
6724
|
+
candidate.width,
|
|
6725
|
+
candidate.height
|
|
6726
|
+
);
|
|
6584
6727
|
const boundsStatus = this.getBoundsStatus({
|
|
6585
|
-
bounds
|
|
6586
|
-
candidate.center,
|
|
6587
|
-
candidate.width,
|
|
6588
|
-
candidate.height
|
|
6589
|
-
),
|
|
6728
|
+
bounds,
|
|
6590
6729
|
labelIndex,
|
|
6591
6730
|
label
|
|
6592
6731
|
});
|
|
6593
|
-
if (boundsStatus !== "valid")
|
|
6732
|
+
if (boundsStatus !== "valid") {
|
|
6733
|
+
if (phase !== "trace-anchor" || boundsStatus !== "chip-collision" || !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds)) {
|
|
6734
|
+
return boundsStatus;
|
|
6735
|
+
}
|
|
6736
|
+
const nonChipBoundsStatus = this.getBoundsStatus({
|
|
6737
|
+
bounds,
|
|
6738
|
+
labelIndex,
|
|
6739
|
+
label,
|
|
6740
|
+
ignoreChipCollisions: true
|
|
6741
|
+
});
|
|
6742
|
+
if (nonChipBoundsStatus !== "valid") return nonChipBoundsStatus;
|
|
6743
|
+
}
|
|
6594
6744
|
const connectorTrace = this.getCandidateConnectorTrace(label, {
|
|
6595
6745
|
anchorPoint: candidate.anchorPoint,
|
|
6596
6746
|
orientation: candidate.orientation,
|
|
@@ -6613,17 +6763,35 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6613
6763
|
}
|
|
6614
6764
|
return "valid";
|
|
6615
6765
|
}
|
|
6766
|
+
isAcceptableTraceAnchorChipCollision(candidate, label, bounds) {
|
|
6767
|
+
const collidingChips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds);
|
|
6768
|
+
if (collidingChips.length === 0) return false;
|
|
6769
|
+
const labelChipIds = new Set(
|
|
6770
|
+
label.pinIds.map((pinId) => this.pinMap[pinId]?.chipId).filter((chipId) => Boolean(chipId))
|
|
6771
|
+
);
|
|
6772
|
+
return collidingChips.every((chip) => {
|
|
6773
|
+
if (!labelChipIds.has(chip.chipId)) return false;
|
|
6774
|
+
const { anchorPoint, orientation } = candidate;
|
|
6775
|
+
const chipBounds = chip.bounds;
|
|
6776
|
+
if (isYOrientation(orientation)) {
|
|
6777
|
+
return anchorPoint.x < chipBounds.minX - EPS8 || anchorPoint.x > chipBounds.maxX + EPS8;
|
|
6778
|
+
}
|
|
6779
|
+
return anchorPoint.y < chipBounds.minY - EPS8 || anchorPoint.y > chipBounds.maxY + EPS8;
|
|
6780
|
+
});
|
|
6781
|
+
}
|
|
6616
6782
|
getBoundsStatus(candidateBoundsCheck) {
|
|
6617
|
-
const { bounds, labelIndex, label } = candidateBoundsCheck;
|
|
6618
|
-
if (
|
|
6619
|
-
|
|
6783
|
+
const { bounds, labelIndex, label, ignoreChipCollisions } = candidateBoundsCheck;
|
|
6784
|
+
if (!ignoreChipCollisions) {
|
|
6785
|
+
if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
|
|
6786
|
+
return "chip-collision";
|
|
6787
|
+
}
|
|
6788
|
+
if (this.sharesChipBoundary(bounds)) {
|
|
6789
|
+
return "chip-collision";
|
|
6790
|
+
}
|
|
6620
6791
|
}
|
|
6621
6792
|
if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
|
|
6622
6793
|
return "text-collision";
|
|
6623
6794
|
}
|
|
6624
|
-
if (this.sharesChipBoundary(bounds)) {
|
|
6625
|
-
return "chip-collision";
|
|
6626
|
-
}
|
|
6627
6795
|
if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
|
|
6628
6796
|
return "trace-collision";
|
|
6629
6797
|
}
|
|
@@ -6648,10 +6816,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
6648
6816
|
}
|
|
6649
6817
|
getLabelTraceClearanceBounds(bounds) {
|
|
6650
6818
|
return {
|
|
6651
|
-
minX: bounds.minX -
|
|
6652
|
-
minY: bounds.minY -
|
|
6653
|
-
maxX: bounds.maxX +
|
|
6654
|
-
maxY: bounds.maxY +
|
|
6819
|
+
minX: bounds.minX - LABEL_TRACE_CLEARANCE2,
|
|
6820
|
+
minY: bounds.minY - LABEL_TRACE_CLEARANCE2,
|
|
6821
|
+
maxX: bounds.maxX + LABEL_TRACE_CLEARANCE2,
|
|
6822
|
+
maxY: bounds.maxY + LABEL_TRACE_CLEARANCE2
|
|
6655
6823
|
};
|
|
6656
6824
|
}
|
|
6657
6825
|
shouldCheckTraceClearanceForLabel(label) {
|