@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.
Files changed (27) hide show
  1. package/dist/index.d.ts +41 -9
  2. package/dist/index.js +762 -110
  3. package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +536 -94
  4. package/lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts +9 -7
  5. package/lib/solvers/InlineNetLabelSolver/pushInlineTerminalLabelsAwayFromAnchoredLabels.ts +295 -0
  6. package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +15 -8
  7. package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +5 -0
  8. package/lib/solvers/MspConnectionPairSolver/getGroundConnectionPolicy.ts +83 -0
  9. package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +7 -35
  10. package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +6 -1
  11. package/lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts +122 -0
  12. package/lib/solvers/SchematicTraceLinesSolver/getTraceConnectedPinComponents.ts +60 -0
  13. package/lib/types/InputProblem.ts +5 -4
  14. package/package.json +1 -1
  15. package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +600 -783
  16. package/tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.test.ts +66 -1
  17. package/tests/fixtures/parallel-ground-rail.ts +53 -0
  18. package/tests/repros/__snapshots__/repro-mspm0l1306-capacitor-symmetry.snap.svg +34 -16
  19. package/tests/repros/repro-mspm0l1306-capacitor-symmetry.test.ts +40 -7
  20. package/tests/repros/repro-usb-power-vbus-label-detour.test.ts +14 -0
  21. package/tests/solvers/InlineNetLabelSolver/multi-pin-connected-components.test.ts +252 -0
  22. package/tests/solvers/InlineNetLabelSolver/opposite-side-placement.test.ts +71 -0
  23. package/tests/solvers/InlineNetLabelSolver/push-anchored-net-labels-away.test.ts +8 -2
  24. package/tests/solvers/InlineNetLabelSolver/push-inline-terminal-labels-away.test.ts +91 -0
  25. package/tests/solvers/InlineNetLabelSolver/two-pin-terminal-stubs-atomic.test.ts +7 -1
  26. package/tests/solvers/MspConnectionPairSolver/local-ground-branches.test.ts +105 -0
  27. 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 one- or two-pin net may use inline labels. A single-pin
114
- * net gets an outward stub. A routed two-pin net gets one label along its
115
- * trace; when that route is intentionally skipped, both endpoints get
116
- * outward stubs. Nets with more than two pins retain anchored labels.
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
 
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://github.com/tscircuit/schematic-trace-solver.git"
6
6
  },
7
7
  "main": "dist/index.js",
8
- "version": "0.0.158",
8
+ "version": "0.0.160",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",