@tscircuit/schematic-trace-solver 0.0.158 → 0.0.160
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 +41 -9
- package/dist/index.js +762 -110
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +536 -94
- package/lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts +9 -7
- package/lib/solvers/InlineNetLabelSolver/pushInlineTerminalLabelsAwayFromAnchoredLabels.ts +295 -0
- package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +15 -8
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +5 -0
- package/lib/solvers/MspConnectionPairSolver/getGroundConnectionPolicy.ts +83 -0
- package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +7 -35
- package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +6 -1
- package/lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts +122 -0
- package/lib/solvers/SchematicTraceLinesSolver/getTraceConnectedPinComponents.ts +60 -0
- package/lib/types/InputProblem.ts +5 -4
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +600 -783
- package/tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.test.ts +66 -1
- package/tests/fixtures/parallel-ground-rail.ts +53 -0
- package/tests/repros/__snapshots__/repro-mspm0l1306-capacitor-symmetry.snap.svg +34 -16
- package/tests/repros/repro-mspm0l1306-capacitor-symmetry.test.ts +40 -7
- package/tests/repros/repro-usb-power-vbus-label-detour.test.ts +14 -0
- package/tests/solvers/InlineNetLabelSolver/multi-pin-connected-components.test.ts +252 -0
- package/tests/solvers/InlineNetLabelSolver/opposite-side-placement.test.ts +71 -0
- package/tests/solvers/InlineNetLabelSolver/push-anchored-net-labels-away.test.ts +8 -2
- package/tests/solvers/InlineNetLabelSolver/push-inline-terminal-labels-away.test.ts +91 -0
- package/tests/solvers/InlineNetLabelSolver/two-pin-terminal-stubs-atomic.test.ts +7 -1
- package/tests/solvers/MspConnectionPairSolver/local-ground-branches.test.ts +105 -0
- package/tests/solvers/SameNetJunctionAlignmentSolver/ground-rail-label.test.ts +188 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { traceCrossesBoundsInterior } from "lib/solvers/AvailableNetOrientationSolver/geometry"
|
|
2
|
+
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
3
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
4
|
+
import {
|
|
5
|
+
getCenterFromAnchor,
|
|
6
|
+
getRectBounds,
|
|
7
|
+
} from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
8
|
+
import {
|
|
9
|
+
rectsOverlap,
|
|
10
|
+
tracePathContainsPoint,
|
|
11
|
+
} from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
|
|
12
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
13
|
+
import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
14
|
+
import {
|
|
15
|
+
isHorizontal,
|
|
16
|
+
isVertical,
|
|
17
|
+
nearlyEqual,
|
|
18
|
+
} from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
|
|
19
|
+
import { simplifyPath } from "lib/solvers/TraceCleanupSolver/simplifyPath"
|
|
20
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
21
|
+
|
|
22
|
+
/** Put a shared decoupling GND at the load end of its rail, away from the IC. */
|
|
23
|
+
export const placeGroundRailLabelsAtOuterEnd = ({
|
|
24
|
+
inputProblem,
|
|
25
|
+
traces,
|
|
26
|
+
netLabelPlacements,
|
|
27
|
+
}: {
|
|
28
|
+
inputProblem: InputProblem
|
|
29
|
+
traces: SolvedTracePath[]
|
|
30
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
31
|
+
}): NetLabelPlacement[] => {
|
|
32
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem)
|
|
33
|
+
const groundNetId = netConnMap.getNetConnectedToId("GND")
|
|
34
|
+
if (!groundNetId) return netLabelPlacements
|
|
35
|
+
const chipMap = new Map(inputProblem.chips.map((chip) => [chip.chipId, chip]))
|
|
36
|
+
const traceMap = Object.fromEntries(
|
|
37
|
+
traces.map((trace) => [trace.mspPairId, trace]),
|
|
38
|
+
)
|
|
39
|
+
const obstacles = getObstacleRects(inputProblem)
|
|
40
|
+
const output = [...netLabelPlacements]
|
|
41
|
+
|
|
42
|
+
for (const rail of traces) {
|
|
43
|
+
if (rail.globalConnNetId !== groundNetId) continue
|
|
44
|
+
const [a, b] = rail.pins
|
|
45
|
+
if (
|
|
46
|
+
a.chipId === b.chipId ||
|
|
47
|
+
!nearlyEqual(a.y, b.y) ||
|
|
48
|
+
![a, b].every(
|
|
49
|
+
(pin) =>
|
|
50
|
+
pin._facingDirection === "y-" &&
|
|
51
|
+
chipMap.get(pin.chipId)?.pins.length === 2,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
continue
|
|
55
|
+
const path = simplifyPath(rail.tracePath)
|
|
56
|
+
if (
|
|
57
|
+
path.length !== 4 ||
|
|
58
|
+
!isVertical(path[0]!, path[1]!) ||
|
|
59
|
+
!isHorizontal(path[1]!, path[2]!) ||
|
|
60
|
+
!isVertical(path[2]!, path[3]!) ||
|
|
61
|
+
path[1]!.y >= a.y
|
|
62
|
+
)
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
for (const feed of traces) {
|
|
66
|
+
if (feed.globalConnNetId !== groundNetId) continue
|
|
67
|
+
const shared = feed.pins.find((pin) => rail.pinIds.includes(pin.pinId))
|
|
68
|
+
const icPin = feed.pins.find(
|
|
69
|
+
(pin) =>
|
|
70
|
+
!rail.pinIds.includes(pin.pinId) &&
|
|
71
|
+
(chipMap.get(pin.chipId)?.pins.length ?? 0) > 2,
|
|
72
|
+
)
|
|
73
|
+
if (!shared || !icPin || !nearlyEqual(icPin.y, path[1]!.y)) continue
|
|
74
|
+
const outer = shared.pinId === a.pinId ? b : a
|
|
75
|
+
if (Math.abs(outer.x - icPin.x) <= Math.abs(shared.x - icPin.x)) continue
|
|
76
|
+
const anchorPoint = { x: outer.x, y: path[1]!.y }
|
|
77
|
+
if (!tracePathContainsPoint(rail.tracePath, anchorPoint)) continue
|
|
78
|
+
|
|
79
|
+
for (let index = 0; index < output.length; index++) {
|
|
80
|
+
const label = output[index]!
|
|
81
|
+
if (
|
|
82
|
+
label.globalConnNetId !== groundNetId ||
|
|
83
|
+
label.orientation !== "y-" ||
|
|
84
|
+
!label.mspConnectionPairIds.some(
|
|
85
|
+
(id) => id === feed.mspPairId || id === rail.mspPairId,
|
|
86
|
+
) ||
|
|
87
|
+
(!tracePathContainsPoint(feed.tracePath, label.anchorPoint) &&
|
|
88
|
+
!tracePathContainsPoint(rail.tracePath, label.anchorPoint))
|
|
89
|
+
)
|
|
90
|
+
continue
|
|
91
|
+
const center = getCenterFromAnchor(
|
|
92
|
+
anchorPoint,
|
|
93
|
+
label.orientation,
|
|
94
|
+
label.width,
|
|
95
|
+
label.height,
|
|
96
|
+
)
|
|
97
|
+
const bounds = getRectBounds(center, label.width, label.height)
|
|
98
|
+
if (
|
|
99
|
+
obstacles.some((obstacle) => rectsOverlap(bounds, obstacle)) ||
|
|
100
|
+
traceCrossesBoundsInterior(bounds, traceMap) ||
|
|
101
|
+
output.some(
|
|
102
|
+
(other, otherIndex) =>
|
|
103
|
+
otherIndex !== index &&
|
|
104
|
+
rectsOverlap(
|
|
105
|
+
bounds,
|
|
106
|
+
getRectBounds(other.center, other.width, other.height),
|
|
107
|
+
),
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
continue
|
|
111
|
+
output[index] = {
|
|
112
|
+
...label,
|
|
113
|
+
anchorPoint,
|
|
114
|
+
center,
|
|
115
|
+
mspConnectionPairIds: [rail.mspPairId],
|
|
116
|
+
pinIds: [...rail.pinIds],
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return output
|
|
122
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { SolvedTracePath } from "./SchematicTraceLinesSolver"
|
|
2
|
+
import type { PinId } from "lib/types/InputProblem"
|
|
3
|
+
|
|
4
|
+
export interface TraceConnectedPinComponent {
|
|
5
|
+
pinIds: PinId[]
|
|
6
|
+
traces: SolvedTracePath[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Partitions pins into connected components using solved traces as graph edges.
|
|
11
|
+
* Pins without a solved trace are returned as singleton components.
|
|
12
|
+
*/
|
|
13
|
+
export const getTraceConnectedPinComponents = ({
|
|
14
|
+
pinIds,
|
|
15
|
+
traces,
|
|
16
|
+
}: {
|
|
17
|
+
pinIds: PinId[]
|
|
18
|
+
traces: SolvedTracePath[]
|
|
19
|
+
}): TraceConnectedPinComponent[] => {
|
|
20
|
+
const adjacency = new Map<PinId, Set<PinId>>()
|
|
21
|
+
for (const pinId of pinIds) adjacency.set(pinId, new Set())
|
|
22
|
+
|
|
23
|
+
const relevantTraces = traces.filter((trace) => {
|
|
24
|
+
if (trace.pins.length !== 2) return false
|
|
25
|
+
const [firstPin, secondPin] = trace.pins
|
|
26
|
+
return adjacency.has(firstPin.pinId) && adjacency.has(secondPin.pinId)
|
|
27
|
+
})
|
|
28
|
+
for (const trace of relevantTraces) {
|
|
29
|
+
const [firstPin, secondPin] = trace.pins
|
|
30
|
+
adjacency.get(firstPin.pinId)!.add(secondPin.pinId)
|
|
31
|
+
adjacency.get(secondPin.pinId)!.add(firstPin.pinId)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const components: TraceConnectedPinComponent[] = []
|
|
35
|
+
const visited = new Set<PinId>()
|
|
36
|
+
for (const pinId of pinIds) {
|
|
37
|
+
if (visited.has(pinId)) continue
|
|
38
|
+
const componentPinIds: PinId[] = []
|
|
39
|
+
const pending = [pinId]
|
|
40
|
+
visited.add(pinId)
|
|
41
|
+
while (pending.length > 0) {
|
|
42
|
+
const currentPinId = pending.pop()!
|
|
43
|
+
componentPinIds.push(currentPinId)
|
|
44
|
+
for (const adjacentPinId of adjacency.get(currentPinId) ?? []) {
|
|
45
|
+
if (visited.has(adjacentPinId)) continue
|
|
46
|
+
visited.add(adjacentPinId)
|
|
47
|
+
pending.push(adjacentPinId)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const componentPinIdSet = new Set(componentPinIds)
|
|
52
|
+
components.push({
|
|
53
|
+
pinIds: componentPinIds,
|
|
54
|
+
traces: relevantTraces.filter((trace) =>
|
|
55
|
+
trace.pins.every((pin) => componentPinIdSet.has(pin.pinId)),
|
|
56
|
+
),
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
return components
|
|
60
|
+
}
|
|
@@ -110,10 +110,11 @@ export interface InputNetConnection {
|
|
|
110
110
|
netLabelHeight?: number
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
|
-
* When true, a named
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
113
|
+
* When true, a named net may use inline labels. Each routed connected
|
|
114
|
+
* component gets a label along its representative trace, while each
|
|
115
|
+
* disconnected endpoint gets an outward inline-label stub. Conversion is
|
|
116
|
+
* atomic for the whole net so inline and anchored representations are not
|
|
117
|
+
* mixed when any label cannot be placed.
|
|
117
118
|
*/
|
|
118
119
|
allowInlineNetLabel?: boolean
|
|
119
120
|
|