@tscircuit/schematic-trace-solver 0.0.91 → 0.0.93

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 (22) hide show
  1. package/dist/index.d.ts +7 -1
  2. package/dist/index.js +150 -21
  3. package/lib/solvers/Example28Solver/doesPathRunAlongChipBoundary.ts +18 -0
  4. package/lib/solvers/Example28Solver/reroute.ts +29 -10
  5. package/lib/solvers/Example28Solver/types.ts +7 -0
  6. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +35 -23
  7. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/calculateDirectShortPath.ts +161 -0
  8. package/package.json +1 -1
  9. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +4 -6
  10. package/tests/bug-reports/bug-report-20260707T230831Z/__snapshots__/bug-report-20260707T230831Z.snap.svg +46 -52
  11. package/tests/examples/__snapshots__/example03.snap.svg +44 -44
  12. package/tests/examples/__snapshots__/example32.snap.svg +3 -3
  13. package/tests/examples/__snapshots__/example42.snap.svg +1 -1
  14. package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +60 -0
  15. package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +60 -0
  16. package/tests/repros/__snapshots__/repro-rectifier-trace-overlap.snap.svg +58 -0
  17. package/tests/repros/assets/repro-missing-trace-netlabel.input.json +155 -0
  18. package/tests/repros/assets/repro-netlabel-overlap-trace.input.json +115 -0
  19. package/tests/repros/assets/repro-rectifier-trace-overlap.input.json +137 -0
  20. package/tests/repros/repro-missing-trace-netlabel.test.ts +14 -0
  21. package/tests/repros/repro-netlabel-overlap-trace.test.ts +14 -0
  22. package/tests/repros/repro-rectifier-trace-overlap.test.ts +14 -0
@@ -0,0 +1,161 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
3
+ import type { FacingDirection } from "lib/utils/dir"
4
+ import { calculateElbow } from "calculate-elbow"
5
+
6
+ const MAX_SHORT_TRACE_DISTANCE = 0.15
7
+ const SHORT_TRACE_OVERSHOOT = MAX_SHORT_TRACE_DISTANCE / 7.5
8
+ const FALLBACK_ELBOW_MAX_OVERSHOOT = 0.2
9
+
10
+ export function segmentDirection(
11
+ from: Point,
12
+ to: Point,
13
+ ): FacingDirection | null {
14
+ if (to.x > from.x) return "x+"
15
+ if (to.x < from.x) return "x-"
16
+ if (to.y > from.y) return "y+"
17
+ if (to.y < from.y) return "y-"
18
+ return null
19
+ }
20
+
21
+ export function pathMatchesPinDirections({
22
+ path,
23
+ pin1,
24
+ pin2,
25
+ }: {
26
+ path: Point[]
27
+ pin1: MspConnectionPair["pins"][number]
28
+ pin2: MspConnectionPair["pins"][number]
29
+ }): boolean {
30
+ const firstDirection = segmentDirection(path[0]!, path[1]!)
31
+ const lastDirection = segmentDirection(
32
+ path[path.length - 1]!,
33
+ path[path.length - 2]!,
34
+ )
35
+
36
+ return (
37
+ firstDirection === pin1._facingDirection &&
38
+ lastDirection === pin2._facingDirection
39
+ )
40
+ }
41
+
42
+ /**
43
+ * Calculates a short orthogonal (perpendicular) route when the source and destination pins
44
+ * are oriented at a 90-degree angle to one another (e.g., one faces X and the other faces Y).
45
+ * It creates a minimal step-out to avoid overlapping pin bodies.
46
+ */
47
+ export function calculateShortOrthogonalRoute(
48
+ pin1: MspConnectionPair["pins"][number],
49
+ pin2: MspConnectionPair["pins"][number],
50
+ ): Point[] | null {
51
+ // If pins share an axis, an orthogonal 3-segment route is unnecessary
52
+ if (pin1.x === pin2.x || pin1.y === pin2.y) return null
53
+
54
+ const firstDir = pin1._facingDirection
55
+ const lastDir = pin2._facingDirection
56
+
57
+ const start = { x: pin1.x, y: pin1.y }
58
+ const end = { x: pin2.x, y: pin2.y }
59
+
60
+ let path: Point[] | null = null
61
+
62
+ // Handle case where pin1 faces vertically and pin2 faces horizontally
63
+ if (firstDir?.startsWith("y") && lastDir?.startsWith("x")) {
64
+ let yOffset = -SHORT_TRACE_OVERSHOOT
65
+ if (firstDir === "y+") {
66
+ yOffset = SHORT_TRACE_OVERSHOOT
67
+ }
68
+ const routeY = start.y + yOffset
69
+ const routeX = (start.x + end.x) / 2
70
+ path = [
71
+ start,
72
+ { x: start.x, y: routeY },
73
+ { x: routeX, y: routeY },
74
+ { x: routeX, y: end.y },
75
+ end,
76
+ ]
77
+ }
78
+ // Handle case where pin1 faces horizontally and pin2 faces vertically
79
+ else if (firstDir?.startsWith("x") && lastDir?.startsWith("y")) {
80
+ let xOffset = -SHORT_TRACE_OVERSHOOT
81
+ if (firstDir === "x+") {
82
+ xOffset = SHORT_TRACE_OVERSHOOT
83
+ }
84
+ const routeX = start.x + xOffset
85
+ const routeY = (start.y + end.y) / 2
86
+ path = [
87
+ start,
88
+ { x: routeX, y: start.y },
89
+ { x: routeX, y: routeY },
90
+ { x: end.x, y: routeY },
91
+ end,
92
+ ]
93
+ }
94
+
95
+ if (!path) return null
96
+
97
+ if (pathMatchesPinDirections({ path, pin1, pin2 })) {
98
+ return path
99
+ }
100
+ return null
101
+ }
102
+
103
+ /**
104
+ * Attempts to calculate a direct route between two pins if they are very close together.
105
+ * This prevents complex routing logic from taking over for trivial connections.
106
+ */
107
+ export function calculateDirectShortPath(
108
+ pin1: MspConnectionPair["pins"][number],
109
+ pin2: MspConnectionPair["pins"][number],
110
+ ): Point[] | null {
111
+ const routingDistance = Math.abs(pin1.x - pin2.x) + Math.abs(pin1.y - pin2.y)
112
+
113
+ // If the distance is too large, fallback to the standard complex routing solver
114
+ if (routingDistance > MAX_SHORT_TRACE_DISTANCE) return null
115
+
116
+ const start = { x: pin1.x, y: pin1.y }
117
+ const end = { x: pin2.x, y: pin2.y }
118
+
119
+ // First, try a simple orthogonal route if the pins are facing perpendicular directions
120
+ const orthogonalRoute = calculateShortOrthogonalRoute(pin1, pin2)
121
+ if (orthogonalRoute) return orthogonalRoute
122
+
123
+ let candidatePaths: Point[][] = []
124
+
125
+ // If the pins are completely offset (neither perfectly aligned horizontally nor vertically),
126
+ // we evaluate standard two-segment L-shaped routes.
127
+ if (pin1.x !== pin2.x && pin1.y !== pin2.y) {
128
+ candidatePaths = [
129
+ [start, { x: pin2.x, y: pin1.y }, end],
130
+ [start, { x: pin1.x, y: pin2.y }, end],
131
+ ]
132
+ } else {
133
+ // If they are perfectly aligned, try a straight line
134
+ candidatePaths = [[start, end]]
135
+ }
136
+
137
+ // Return the first candidate that matches the facing direction requirements of the pins
138
+ for (const path of candidatePaths) {
139
+ if (pathMatchesPinDirections({ path, pin1, pin2 })) return path
140
+ }
141
+
142
+ // If simple paths fail, compute a slightly overshoot-based elbow routing as a final short-trace fallback
143
+ return calculateElbow(
144
+ {
145
+ x: pin1.x,
146
+ y: pin1.y,
147
+ facingDirection: pin1._facingDirection!,
148
+ },
149
+ {
150
+ x: pin2.x,
151
+ y: pin2.y,
152
+ facingDirection: pin2._facingDirection!,
153
+ },
154
+ {
155
+ overshoot: Math.min(
156
+ FALLBACK_ELBOW_MAX_OVERSHOOT,
157
+ Math.max(SHORT_TRACE_OVERSHOOT, routingDistance / 4),
158
+ ),
159
+ },
160
+ )
161
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.91",
4
+ "version": "0.0.93",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",