@tscircuit/schematic-trace-solver 0.0.26 → 0.0.28
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 +5 -0
- package/dist/index.js +114 -4
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts +11 -0
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts +40 -1
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts +139 -0
- package/package.json +1 -1
- package/site/SchematicTracePipelineSolver/SchematicTracePipelineSolver01.page.tsx +143 -0
- package/site/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.page.tsx +574 -0
- package/site/examples/example11.page.tsx +1 -1
- package/site/examples/example12.page.tsx +1 -1
- package/site/examples/example13.page.tsx +219 -0
- package/site/examples/example14.page.tsx +210 -0
- package/tests/examples/__snapshots__/example01.snap.svg +4 -4
- package/tests/examples/__snapshots__/example02.snap.svg +4 -4
- package/tests/examples/__snapshots__/example03.snap.svg +68 -68
- package/tests/examples/__snapshots__/example04.snap.svg +12 -12
- package/tests/examples/__snapshots__/example05.snap.svg +1 -1
- package/tests/examples/__snapshots__/example06.snap.svg +11 -11
- package/tests/examples/__snapshots__/example07.snap.svg +48 -48
- package/tests/examples/__snapshots__/example08.snap.svg +25 -25
- package/tests/examples/__snapshots__/example09.snap.svg +131 -131
- package/tests/examples/__snapshots__/example10.snap.svg +26 -26
- package/tests/examples/__snapshots__/example11.snap.svg +162 -0
- package/tests/examples/__snapshots__/example12.snap.svg +145 -0
- package/tests/examples/__snapshots__/example13.snap.svg +322 -0
- package/tests/examples/__snapshots__/example14.snap.svg +213 -0
- package/tests/examples/__snapshots__/example15.snap.svg +235 -0
- package/tests/examples/example11.test.tsx +12 -0
- package/tests/examples/example12.test.tsx +12 -0
- package/tests/examples/example13.test.tsx +12 -0
- package/tests/examples/example14.test.tsx +12 -0
- package/tests/examples/example15.test.tsx +187 -0
- package/tests/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver01.test.ts +13 -0
- package/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro03.test.ts +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -154,6 +154,9 @@ interface MovableSegment {
|
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
type ChipPin = InputPin & {
|
|
158
|
+
chipId: ChipId;
|
|
159
|
+
};
|
|
157
160
|
declare class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
158
161
|
pins: MspConnectionPair["pins"];
|
|
159
162
|
inputProblem: InputProblem;
|
|
@@ -168,6 +171,7 @@ declare class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
|
168
171
|
x: number;
|
|
169
172
|
y: number;
|
|
170
173
|
}[] | null;
|
|
174
|
+
pinIdMap: Map<PinId, ChipPin>;
|
|
171
175
|
constructor(params: {
|
|
172
176
|
pins: MspConnectionPair["pins"];
|
|
173
177
|
guidelines: Guideline[];
|
|
@@ -331,6 +335,7 @@ declare class SingleNetLabelPlacementSolver extends BaseSolver {
|
|
|
331
335
|
overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup;
|
|
332
336
|
availableOrientations: FacingDirection[];
|
|
333
337
|
});
|
|
338
|
+
getConstructorParams(): ConstructorParameters<typeof SingleNetLabelPlacementSolver>[0];
|
|
334
339
|
_step(): void;
|
|
335
340
|
visualize(): GraphicsObject;
|
|
336
341
|
}
|
package/dist/index.js
CHANGED
|
@@ -668,6 +668,86 @@ var visualizeGuidelines = ({
|
|
|
668
668
|
return graphics;
|
|
669
669
|
};
|
|
670
670
|
|
|
671
|
+
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getRestrictedCenterLines.ts
|
|
672
|
+
var getRestrictedCenterLines = (params) => {
|
|
673
|
+
const { pins, inputProblem, pinIdMap, chipMap } = params;
|
|
674
|
+
const findAllDirectlyConnectedPins = (startPinId) => {
|
|
675
|
+
const visited = /* @__PURE__ */ new Set();
|
|
676
|
+
const queue = [startPinId];
|
|
677
|
+
visited.add(startPinId);
|
|
678
|
+
const directConns = inputProblem.directConnections || [];
|
|
679
|
+
while (queue.length) {
|
|
680
|
+
const cur = queue.shift();
|
|
681
|
+
for (const dc of directConns) {
|
|
682
|
+
if (dc.pinIds.includes(cur)) {
|
|
683
|
+
for (const p of dc.pinIds) {
|
|
684
|
+
if (!visited.has(p)) {
|
|
685
|
+
visited.add(p);
|
|
686
|
+
queue.push(p);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return visited;
|
|
693
|
+
};
|
|
694
|
+
const p0 = pins[0].pinId;
|
|
695
|
+
const p1 = pins[1].pinId;
|
|
696
|
+
const relatedPinIds = /* @__PURE__ */ new Set([
|
|
697
|
+
...findAllDirectlyConnectedPins(p0),
|
|
698
|
+
...findAllDirectlyConnectedPins(p1)
|
|
699
|
+
]);
|
|
700
|
+
const restrictedCenterLines = /* @__PURE__ */ new Map();
|
|
701
|
+
const chipFacingMap = /* @__PURE__ */ new Map();
|
|
702
|
+
const chipsOfFacingPins = new Set(pins.map((p) => p.chipId));
|
|
703
|
+
for (const pinId of relatedPinIds) {
|
|
704
|
+
const pin = pinIdMap.get(pinId);
|
|
705
|
+
if (!pin) continue;
|
|
706
|
+
const chip = chipMap[pin.chipId];
|
|
707
|
+
if (!chip) continue;
|
|
708
|
+
const facing = pin._facingDirection ?? getPinDirection(pin, chip);
|
|
709
|
+
let entry = chipFacingMap.get(chip.chipId);
|
|
710
|
+
if (!entry) {
|
|
711
|
+
entry = { center: chip.center };
|
|
712
|
+
const counts = { xPos: 0, xNeg: 0, yPos: 0, yNeg: 0 };
|
|
713
|
+
for (const cp of chip.pins) {
|
|
714
|
+
const cpFacing = cp._facingDirection ?? getPinDirection(cp, chip);
|
|
715
|
+
if (cpFacing === "x+") counts.xPos++;
|
|
716
|
+
if (cpFacing === "x-") counts.xNeg++;
|
|
717
|
+
if (cpFacing === "y+") counts.yPos++;
|
|
718
|
+
if (cpFacing === "y-") counts.yNeg++;
|
|
719
|
+
}
|
|
720
|
+
entry.counts = counts;
|
|
721
|
+
chipFacingMap.set(chip.chipId, entry);
|
|
722
|
+
}
|
|
723
|
+
if (facing === "x+") entry.hasXPos = true;
|
|
724
|
+
if (facing === "x-") entry.hasXNeg = true;
|
|
725
|
+
if (facing === "y+") entry.hasYPos = true;
|
|
726
|
+
if (facing === "y-") entry.hasYNeg = true;
|
|
727
|
+
}
|
|
728
|
+
for (const [chipId, faces] of chipFacingMap) {
|
|
729
|
+
const axes = /* @__PURE__ */ new Set();
|
|
730
|
+
const rcl = { axes };
|
|
731
|
+
const counts = faces.counts;
|
|
732
|
+
const anySideHasMultiplePins = !!(counts && (counts.xPos > 1 || counts.xNeg > 1 || counts.yPos > 1 || counts.yNeg > 1));
|
|
733
|
+
const skipCenterRestriction = !anySideHasMultiplePins && chipsOfFacingPins.has(chipId);
|
|
734
|
+
if (!skipCenterRestriction) {
|
|
735
|
+
if (faces.hasXPos && faces.hasXNeg) {
|
|
736
|
+
rcl.x = faces.center.x;
|
|
737
|
+
axes.add("x");
|
|
738
|
+
}
|
|
739
|
+
if (faces.hasYPos && faces.hasYNeg) {
|
|
740
|
+
rcl.y = faces.center.y;
|
|
741
|
+
axes.add("y");
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (axes.size > 0) {
|
|
745
|
+
restrictedCenterLines.set(chipId, rcl);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return restrictedCenterLines;
|
|
749
|
+
};
|
|
750
|
+
|
|
671
751
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts
|
|
672
752
|
var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
673
753
|
pins;
|
|
@@ -680,6 +760,8 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
680
760
|
queuedCandidatePaths;
|
|
681
761
|
chipObstacleSpatialIndex;
|
|
682
762
|
solvedTracePath = null;
|
|
763
|
+
// map of pinId -> pin (with chipId attached)
|
|
764
|
+
pinIdMap = /* @__PURE__ */ new Map();
|
|
683
765
|
constructor(params) {
|
|
684
766
|
super();
|
|
685
767
|
this.pins = params.pins;
|
|
@@ -690,6 +772,11 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
690
772
|
if (!this.inputProblem._chipObstacleSpatialIndex) {
|
|
691
773
|
this.inputProblem._chipObstacleSpatialIndex = this.chipObstacleSpatialIndex;
|
|
692
774
|
}
|
|
775
|
+
for (const chip of this.inputProblem.chips) {
|
|
776
|
+
for (const pin of chip.pins) {
|
|
777
|
+
this.pinIdMap.set(pin.pinId, { ...pin, chipId: chip.chipId });
|
|
778
|
+
}
|
|
779
|
+
}
|
|
693
780
|
for (const pin of this.pins) {
|
|
694
781
|
if (!pin._facingDirection) {
|
|
695
782
|
const chip = this.chipMap[pin.chipId];
|
|
@@ -746,10 +833,25 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
746
833
|
return;
|
|
747
834
|
}
|
|
748
835
|
const nextCandidatePath = this.queuedCandidatePaths.shift();
|
|
836
|
+
const restrictedCenterLines = getRestrictedCenterLines({
|
|
837
|
+
pins: this.pins,
|
|
838
|
+
inputProblem: this.inputProblem,
|
|
839
|
+
pinIdMap: this.pinIdMap,
|
|
840
|
+
chipMap: this.chipMap
|
|
841
|
+
});
|
|
749
842
|
for (let i = 0; i < nextCandidatePath.length - 1; i++) {
|
|
750
843
|
const start = nextCandidatePath[i];
|
|
751
844
|
const end = nextCandidatePath[i + 1];
|
|
752
845
|
let excludeChipIds = [];
|
|
846
|
+
const EPS2 = 1e-9;
|
|
847
|
+
for (const [, rcl] of restrictedCenterLines) {
|
|
848
|
+
if (rcl.axes.has("x") && typeof rcl.x === "number") {
|
|
849
|
+
if ((start.x - rcl.x) * (end.x - rcl.x) < -EPS2) return;
|
|
850
|
+
}
|
|
851
|
+
if (rcl.axes.has("y") && typeof rcl.y === "number") {
|
|
852
|
+
if ((start.y - rcl.y) * (end.y - rcl.y) < -EPS2) return;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
753
855
|
const isStartPin = this.pins.some(
|
|
754
856
|
(pin) => Math.abs(pin.x - start.x) < 1e-10 && Math.abs(pin.y - start.y) < 1e-10
|
|
755
857
|
);
|
|
@@ -764,12 +866,12 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
764
866
|
const bounds = getInputChipBounds(this.chipMap[startPin.chipId]);
|
|
765
867
|
const dx = end.x - start.x;
|
|
766
868
|
const dy = end.y - start.y;
|
|
767
|
-
const
|
|
869
|
+
const EPS3 = 1e-9;
|
|
768
870
|
const onLeft = Math.abs(start.x - bounds.minX) < 1e-9;
|
|
769
871
|
const onRight = Math.abs(start.x - bounds.maxX) < 1e-9;
|
|
770
872
|
const onBottom = Math.abs(start.y - bounds.minY) < 1e-9;
|
|
771
873
|
const onTop = Math.abs(start.y - bounds.maxY) < 1e-9;
|
|
772
|
-
const entersInterior = onLeft && dx >
|
|
874
|
+
const entersInterior = onLeft && dx > EPS3 || onRight && dx < -EPS3 || onBottom && dy > EPS3 || onTop && dy < -EPS3;
|
|
773
875
|
if (entersInterior) return;
|
|
774
876
|
if (!excludeChipIds.includes(startPin.chipId)) {
|
|
775
877
|
excludeChipIds.push(startPin.chipId);
|
|
@@ -784,12 +886,12 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
784
886
|
const bounds = getInputChipBounds(this.chipMap[endPin.chipId]);
|
|
785
887
|
const dx = start.x - end.x;
|
|
786
888
|
const dy = start.y - end.y;
|
|
787
|
-
const
|
|
889
|
+
const EPS3 = 1e-9;
|
|
788
890
|
const onLeft = Math.abs(end.x - bounds.minX) < 1e-9;
|
|
789
891
|
const onRight = Math.abs(end.x - bounds.maxX) < 1e-9;
|
|
790
892
|
const onBottom = Math.abs(end.y - bounds.minY) < 1e-9;
|
|
791
893
|
const onTop = Math.abs(end.y - bounds.maxY) < 1e-9;
|
|
792
|
-
const entersInterior = onLeft && dx >
|
|
894
|
+
const entersInterior = onLeft && dx > EPS3 || onRight && dx < -EPS3 || onBottom && dy > EPS3 || onTop && dy < -EPS3;
|
|
793
895
|
if (entersInterior) return;
|
|
794
896
|
if (!excludeChipIds.includes(endPin.chipId)) {
|
|
795
897
|
excludeChipIds.push(endPin.chipId);
|
|
@@ -1579,6 +1681,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
1579
1681
|
this.availableOrientations = params.availableOrientations;
|
|
1580
1682
|
this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
|
|
1581
1683
|
}
|
|
1684
|
+
getConstructorParams() {
|
|
1685
|
+
return {
|
|
1686
|
+
inputProblem: this.inputProblem,
|
|
1687
|
+
inputTraceMap: this.inputTraceMap,
|
|
1688
|
+
overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
|
|
1689
|
+
availableOrientations: this.availableOrientations
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1582
1692
|
_step() {
|
|
1583
1693
|
if (this.netLabelPlacement) {
|
|
1584
1694
|
this.solved = true;
|
|
@@ -87,6 +87,17 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
|
|
|
87
87
|
new ChipObstacleSpatialIndex(params.inputProblem.chips)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
override getConstructorParams(): ConstructorParameters<
|
|
91
|
+
typeof SingleNetLabelPlacementSolver
|
|
92
|
+
>[0] {
|
|
93
|
+
return {
|
|
94
|
+
inputProblem: this.inputProblem,
|
|
95
|
+
inputTraceMap: this.inputTraceMap,
|
|
96
|
+
overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
|
|
97
|
+
availableOrientations: this.availableOrientations,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
override _step() {
|
|
91
102
|
if (this.netLabelPlacement) {
|
|
92
103
|
this.solved = true
|
|
@@ -4,7 +4,13 @@ import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
|
4
4
|
import type { Guideline } from "lib/solvers/GuidelinesSolver/GuidelinesSolver"
|
|
5
5
|
import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
|
|
6
6
|
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
ChipId,
|
|
9
|
+
InputChip,
|
|
10
|
+
InputPin,
|
|
11
|
+
InputProblem,
|
|
12
|
+
PinId,
|
|
13
|
+
} from "lib/types/InputProblem"
|
|
8
14
|
import { calculateElbow } from "calculate-elbow"
|
|
9
15
|
import { getPinDirection } from "./getPinDirection"
|
|
10
16
|
import {
|
|
@@ -15,6 +21,9 @@ import type { Point } from "@tscircuit/math-utils"
|
|
|
15
21
|
import { visualizeGuidelines } from "lib/solvers/GuidelinesSolver/visualizeGuidelines"
|
|
16
22
|
import { getInputChipBounds } from "lib/solvers/GuidelinesSolver/getInputChipBounds"
|
|
17
23
|
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
24
|
+
import { getRestrictedCenterLines } from "./getRestrictedCenterLines"
|
|
25
|
+
|
|
26
|
+
type ChipPin = InputPin & { chipId: ChipId }
|
|
18
27
|
|
|
19
28
|
export class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
20
29
|
pins: MspConnectionPair["pins"]
|
|
@@ -31,6 +40,9 @@ export class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
|
31
40
|
|
|
32
41
|
solvedTracePath: { x: number; y: number }[] | null = null
|
|
33
42
|
|
|
43
|
+
// map of pinId -> pin (with chipId attached)
|
|
44
|
+
pinIdMap: Map<PinId, ChipPin> = new Map()
|
|
45
|
+
|
|
34
46
|
constructor(params: {
|
|
35
47
|
pins: MspConnectionPair["pins"]
|
|
36
48
|
guidelines: Guideline[]
|
|
@@ -51,6 +63,13 @@ export class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
|
51
63
|
this.chipObstacleSpatialIndex
|
|
52
64
|
}
|
|
53
65
|
|
|
66
|
+
// Build a lookup of all pins by id and attach chipId to each pin entry
|
|
67
|
+
for (const chip of this.inputProblem.chips) {
|
|
68
|
+
for (const pin of chip.pins) {
|
|
69
|
+
this.pinIdMap.set(pin.pinId, { ...pin, chipId: chip.chipId })
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
54
73
|
for (const pin of this.pins) {
|
|
55
74
|
if (!pin._facingDirection) {
|
|
56
75
|
const chip = this.chipMap[pin.chipId]
|
|
@@ -118,6 +137,13 @@ export class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
|
118
137
|
|
|
119
138
|
const nextCandidatePath = this.queuedCandidatePaths.shift()!
|
|
120
139
|
|
|
140
|
+
const restrictedCenterLines = getRestrictedCenterLines({
|
|
141
|
+
pins: this.pins,
|
|
142
|
+
inputProblem: this.inputProblem,
|
|
143
|
+
pinIdMap: this.pinIdMap,
|
|
144
|
+
chipMap: this.chipMap,
|
|
145
|
+
})
|
|
146
|
+
|
|
121
147
|
for (let i = 0; i < nextCandidatePath.length - 1; i++) {
|
|
122
148
|
const start = nextCandidatePath[i]
|
|
123
149
|
const end = nextCandidatePath[i + 1]
|
|
@@ -125,6 +151,19 @@ export class SchematicTraceSingleLineSolver extends BaseSolver {
|
|
|
125
151
|
// Determine which chips to exclude for this specific segment
|
|
126
152
|
let excludeChipIds: string[] = []
|
|
127
153
|
|
|
154
|
+
// If this segment would cross any restricted center line, reject the candidate path.
|
|
155
|
+
const EPS = 1e-9
|
|
156
|
+
for (const [, rcl] of restrictedCenterLines) {
|
|
157
|
+
if (rcl.axes.has("x") && typeof rcl.x === "number") {
|
|
158
|
+
// segment strictly crosses vertical center line
|
|
159
|
+
if ((start.x - rcl.x) * (end.x - rcl.x) < -EPS) return
|
|
160
|
+
}
|
|
161
|
+
if (rcl.axes.has("y") && typeof rcl.y === "number") {
|
|
162
|
+
// segment strictly crosses horizontal center line
|
|
163
|
+
if ((start.y - rcl.y) * (end.y - rcl.y) < -EPS) return
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
128
167
|
// Always exclude chips that contain the start or end points of this segment
|
|
129
168
|
// if those points are actually pin locations
|
|
130
169
|
const isStartPin = this.pins.some(
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChipId,
|
|
3
|
+
InputChip,
|
|
4
|
+
InputPin,
|
|
5
|
+
InputProblem,
|
|
6
|
+
PinId,
|
|
7
|
+
} from "lib/types/InputProblem"
|
|
8
|
+
import { getPinDirection } from "./getPinDirection"
|
|
9
|
+
|
|
10
|
+
type ChipPin = InputPin & { chipId: ChipId }
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A line that should not be crossed by a trace segment.
|
|
14
|
+
* This is used to prevent traces from crossing through the center of a chip when
|
|
15
|
+
* connecting pins on that same chip. Right now this is only enabled when a chip
|
|
16
|
+
* has multiple pins on at least one side.
|
|
17
|
+
*/
|
|
18
|
+
export type RestrictedCenterLine = {
|
|
19
|
+
x?: number
|
|
20
|
+
y?: number
|
|
21
|
+
axes: Set<"x" | "y">
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const getRestrictedCenterLines = (params: {
|
|
25
|
+
pins: Array<InputPin & { chipId: string }>
|
|
26
|
+
inputProblem: InputProblem
|
|
27
|
+
pinIdMap: Map<PinId, ChipPin>
|
|
28
|
+
chipMap: Record<string, InputChip>
|
|
29
|
+
}): Map<ChipId, RestrictedCenterLine> => {
|
|
30
|
+
const { pins, inputProblem, pinIdMap, chipMap } = params
|
|
31
|
+
|
|
32
|
+
// Determine set of related pin IDs (closure over directConnections) for both endpoints
|
|
33
|
+
const findAllDirectlyConnectedPins = (startPinId: string) => {
|
|
34
|
+
const visited = new Set<string>()
|
|
35
|
+
const queue: string[] = [startPinId]
|
|
36
|
+
visited.add(startPinId)
|
|
37
|
+
const directConns = inputProblem.directConnections || []
|
|
38
|
+
while (queue.length) {
|
|
39
|
+
const cur = queue.shift()!
|
|
40
|
+
for (const dc of directConns) {
|
|
41
|
+
if (dc.pinIds.includes(cur)) {
|
|
42
|
+
for (const p of dc.pinIds) {
|
|
43
|
+
if (!visited.has(p)) {
|
|
44
|
+
visited.add(p)
|
|
45
|
+
queue.push(p)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return visited
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const p0 = pins[0].pinId
|
|
55
|
+
const p1 = pins[1].pinId
|
|
56
|
+
const relatedPinIds = new Set<string>([
|
|
57
|
+
...findAllDirectlyConnectedPins(p0),
|
|
58
|
+
...findAllDirectlyConnectedPins(p1),
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
const restrictedCenterLines = new Map<ChipId, RestrictedCenterLine>()
|
|
62
|
+
|
|
63
|
+
// Collect facing-signs per chip
|
|
64
|
+
const chipFacingMap = new Map<
|
|
65
|
+
string,
|
|
66
|
+
{
|
|
67
|
+
hasXPos?: boolean
|
|
68
|
+
hasXNeg?: boolean
|
|
69
|
+
hasYPos?: boolean
|
|
70
|
+
hasYNeg?: boolean
|
|
71
|
+
center: { x: number; y: number }
|
|
72
|
+
counts?: { xPos: number; xNeg: number; yPos: number; yNeg: number }
|
|
73
|
+
}
|
|
74
|
+
>()
|
|
75
|
+
|
|
76
|
+
const chipsOfFacingPins = new Set<string>(pins.map((p) => p.chipId))
|
|
77
|
+
|
|
78
|
+
for (const pinId of relatedPinIds) {
|
|
79
|
+
const pin = pinIdMap.get(pinId)
|
|
80
|
+
if (!pin) continue
|
|
81
|
+
const chip = chipMap[pin.chipId]
|
|
82
|
+
if (!chip) continue
|
|
83
|
+
const facing = pin._facingDirection ?? getPinDirection(pin, chip)
|
|
84
|
+
let entry = chipFacingMap.get(chip.chipId)
|
|
85
|
+
if (!entry) {
|
|
86
|
+
entry = { center: chip.center }
|
|
87
|
+
|
|
88
|
+
const counts = { xPos: 0, xNeg: 0, yPos: 0, yNeg: 0 }
|
|
89
|
+
for (const cp of chip.pins) {
|
|
90
|
+
const cpFacing = cp._facingDirection ?? getPinDirection(cp, chip)
|
|
91
|
+
if (cpFacing === "x+") counts.xPos++
|
|
92
|
+
if (cpFacing === "x-") counts.xNeg++
|
|
93
|
+
if (cpFacing === "y+") counts.yPos++
|
|
94
|
+
if (cpFacing === "y-") counts.yNeg++
|
|
95
|
+
}
|
|
96
|
+
entry.counts = counts
|
|
97
|
+
|
|
98
|
+
chipFacingMap.set(chip.chipId, entry)
|
|
99
|
+
}
|
|
100
|
+
if (facing === "x+") entry.hasXPos = true
|
|
101
|
+
if (facing === "x-") entry.hasXNeg = true
|
|
102
|
+
if (facing === "y+") entry.hasYPos = true
|
|
103
|
+
if (facing === "y-") entry.hasYNeg = true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Only mark a center as restricted on an axis if both signs for that axis
|
|
107
|
+
// are present among related pins on the chip.
|
|
108
|
+
for (const [chipId, faces] of chipFacingMap) {
|
|
109
|
+
const axes = new Set<"x" | "y">()
|
|
110
|
+
const rcl: RestrictedCenterLine = { axes }
|
|
111
|
+
|
|
112
|
+
// determine whether any side on this chip has more than one pin
|
|
113
|
+
const counts = faces.counts
|
|
114
|
+
const anySideHasMultiplePins = !!(
|
|
115
|
+
counts &&
|
|
116
|
+
(counts.xPos > 1 || counts.xNeg > 1 || counts.yPos > 1 || counts.yNeg > 1)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
const skipCenterRestriction =
|
|
120
|
+
!anySideHasMultiplePins && chipsOfFacingPins.has(chipId)
|
|
121
|
+
|
|
122
|
+
if (!skipCenterRestriction) {
|
|
123
|
+
if (faces.hasXPos && faces.hasXNeg) {
|
|
124
|
+
rcl.x = faces.center.x
|
|
125
|
+
axes.add("x")
|
|
126
|
+
}
|
|
127
|
+
if (faces.hasYPos && faces.hasYNeg) {
|
|
128
|
+
rcl.y = faces.center.y
|
|
129
|
+
axes.add("y")
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (axes.size > 0) {
|
|
134
|
+
restrictedCenterLines.set(chipId, rcl)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return restrictedCenterLines
|
|
139
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { GenericSolverDebugger } from "site/components/GenericSolverDebugger"
|
|
2
|
+
import { useMemo } from "react"
|
|
3
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
4
|
+
import { SchematicTracePipelineSolver } from "lib/index"
|
|
5
|
+
|
|
6
|
+
const inputProblem = {
|
|
7
|
+
chips: [
|
|
8
|
+
{
|
|
9
|
+
chipId: "schematic_component_0",
|
|
10
|
+
center: {
|
|
11
|
+
x: 0,
|
|
12
|
+
y: 0,
|
|
13
|
+
},
|
|
14
|
+
width: 2,
|
|
15
|
+
height: 1.4,
|
|
16
|
+
pins: [
|
|
17
|
+
{
|
|
18
|
+
pinId: "U3.8",
|
|
19
|
+
x: -1.4,
|
|
20
|
+
y: 0.42500000000000004,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
pinId: "U3.4",
|
|
24
|
+
x: -1.4,
|
|
25
|
+
y: -0.42500000000000004,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
pinId: "U3.1",
|
|
29
|
+
x: 1.4,
|
|
30
|
+
y: 0.5,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
pinId: "U3.6",
|
|
34
|
+
x: 1.4,
|
|
35
|
+
y: 0.30000000000000004,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
pinId: "U3.5",
|
|
39
|
+
x: 1.4,
|
|
40
|
+
y: 0.10000000000000009,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
pinId: "U3.2",
|
|
44
|
+
x: 1.4,
|
|
45
|
+
y: -0.09999999999999998,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
pinId: "U3.3",
|
|
49
|
+
x: 1.4,
|
|
50
|
+
y: -0.3,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
pinId: "U3.7",
|
|
54
|
+
x: 1.4,
|
|
55
|
+
y: -0.5,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
chipId: "schematic_component_1",
|
|
61
|
+
center: {
|
|
62
|
+
x: -2.3145833,
|
|
63
|
+
y: 0,
|
|
64
|
+
},
|
|
65
|
+
width: 0.5291665999999999,
|
|
66
|
+
height: 1.0583333000000001,
|
|
67
|
+
pins: [
|
|
68
|
+
{
|
|
69
|
+
pinId: "C20.1",
|
|
70
|
+
x: -2.3148566499999994,
|
|
71
|
+
y: 0.5512093000000002,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
pinId: "C20.2",
|
|
75
|
+
x: -2.31430995,
|
|
76
|
+
y: -0.5512093000000002,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
chipId: "schematic_component_2",
|
|
82
|
+
center: {
|
|
83
|
+
x: 1.7577928249999983,
|
|
84
|
+
y: 1.7512907000000002,
|
|
85
|
+
},
|
|
86
|
+
width: 0.3155856499999966,
|
|
87
|
+
height: 1.0583332999999997,
|
|
88
|
+
pins: [
|
|
89
|
+
{
|
|
90
|
+
pinId: "R11.1",
|
|
91
|
+
x: 1.7580660749999977,
|
|
92
|
+
y: 2.3025814000000002,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
pinId: "R11.2",
|
|
96
|
+
x: 1.757519574999999,
|
|
97
|
+
y: 1.2,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
directConnections: [
|
|
103
|
+
{
|
|
104
|
+
pinIds: ["C20.1", "U3.8"],
|
|
105
|
+
netId: "capacitor.C20 > port.pin1 to .U3 > .VDD",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
pinIds: ["C20.2", "U3.4"],
|
|
109
|
+
netId: "capacitor.C20 > port.pin2 to .U3 > .GND",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
pinIds: ["R11.2", "U3.1"],
|
|
113
|
+
netId: "resistor.R11 > port.pin2 to .U3 > .N_CS",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
netConnections: [
|
|
117
|
+
{
|
|
118
|
+
netId: "V3_3",
|
|
119
|
+
pinIds: ["U3.8", "U3.3", "U3.7", "C20.1", "R11.1"],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
netId: "GND",
|
|
123
|
+
pinIds: ["U3.4", "C20.2"],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
netId: "FLASH_N_CS",
|
|
127
|
+
pinIds: ["U3.1", "R11.2"],
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
availableNetLabelOrientations: {
|
|
131
|
+
V3_3: ["y+"],
|
|
132
|
+
GND: ["y-"],
|
|
133
|
+
},
|
|
134
|
+
maxMspPairDistance: 5,
|
|
135
|
+
} as InputProblem
|
|
136
|
+
|
|
137
|
+
export default () => {
|
|
138
|
+
const solver = useMemo(
|
|
139
|
+
() => new SchematicTracePipelineSolver(inputProblem),
|
|
140
|
+
[],
|
|
141
|
+
)
|
|
142
|
+
return <GenericSolverDebugger solver={solver} />
|
|
143
|
+
}
|