@tscircuit/schematic-trace-solver 0.0.48 → 0.0.49

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 (162) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +51 -1
  3. package/dist/index.js +730 -43
  4. package/lib/solvers/Example28Solver/Example28Solver.ts +182 -0
  5. package/lib/solvers/Example28Solver/geometry.ts +246 -0
  6. package/lib/solvers/Example28Solver/labelMovement.ts +82 -0
  7. package/lib/solvers/Example28Solver/reroute.ts +368 -0
  8. package/lib/solvers/Example28Solver/types.ts +47 -0
  9. package/lib/solvers/Example28Solver/visualize.ts +96 -0
  10. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +16 -0
  11. package/package.json +1 -1
  12. package/site/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.page.tsx +2 -0
  13. package/site/TraceCleanupSolver/TraceCleanupSolver.page.tsx +23 -0
  14. package/site/TraceLabelOverlapAvoidanceSolver/MergedNetLabelObstacles.page.tsx +18 -0
  15. package/site/TraceLabelOverlapAvoidanceSolver/OverlapAvoidanceStepSolver.page.tsx +24 -0
  16. package/site/TraceLabelOverlapAvoidanceSolver/SingleOverlapSolver.page.tsx +10 -0
  17. package/site/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.page.tsx +146 -0
  18. package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView01.page.tsx +32 -0
  19. package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView02.page.tsx +32 -0
  20. package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView03.page.tsx +35 -0
  21. package/site/examples/example01.page.tsx +6 -0
  22. package/site/examples/example02.page.tsx +4 -180
  23. package/site/examples/example03.page.tsx +3 -212
  24. package/site/examples/example04.page.tsx +6 -0
  25. package/site/examples/example05.page.tsx +3 -179
  26. package/site/examples/example06.page.tsx +3 -57
  27. package/site/examples/example07.page.tsx +3 -132
  28. package/site/examples/example08.page.tsx +3 -91
  29. package/site/examples/example09.page.tsx +3 -414
  30. package/site/examples/example10.page.tsx +3 -75
  31. package/site/examples/example11.page.tsx +3 -116
  32. package/site/examples/example12.page.tsx +3 -94
  33. package/site/examples/example13.page.tsx +3 -216
  34. package/site/examples/example14.page.tsx +3 -207
  35. package/site/examples/example15.page.tsx +6 -0
  36. package/site/examples/example16.page.tsx +6 -0
  37. package/site/examples/example17.page.tsx +6 -0
  38. package/site/examples/example18.page.tsx +3 -178
  39. package/site/examples/example19.page.tsx +3 -166
  40. package/site/examples/example20.page.tsx +3 -100
  41. package/site/examples/example21.page.tsx +3 -174
  42. package/site/examples/example22.page.tsx +3 -105
  43. package/site/examples/example23.page.tsx +3 -134
  44. package/site/examples/example24.page.tsx +3 -125
  45. package/site/examples/example25.page.tsx +2 -0
  46. package/site/examples/example26.page.tsx +2 -0
  47. package/site/examples/example27.page.tsx +2 -0
  48. package/site/examples/example28.page.tsx +3 -58
  49. package/site/examples/example29.page.tsx +6 -0
  50. package/site/examples/example30.page.tsx +6 -0
  51. package/site/examples/example31.page.tsx +4 -0
  52. package/tests/assets/example01.json +109 -0
  53. package/tests/assets/example02.json +178 -0
  54. package/tests/assets/example03.json +210 -0
  55. package/tests/assets/example04.json +41 -0
  56. package/tests/assets/example05.json +175 -0
  57. package/tests/assets/example06.json +55 -0
  58. package/tests/assets/example07.json +130 -0
  59. package/tests/assets/example08.json +89 -0
  60. package/tests/assets/example09.json +412 -0
  61. package/tests/assets/example10.json +73 -0
  62. package/tests/assets/example11.json +114 -0
  63. package/tests/assets/example12.json +92 -0
  64. package/tests/assets/example13.json +214 -0
  65. package/tests/assets/example14.json +205 -0
  66. package/tests/assets/example15.json +618 -0
  67. package/tests/assets/example16.json +102 -0
  68. package/tests/assets/example17.json +160 -0
  69. package/tests/assets/example18.json +176 -0
  70. package/tests/assets/example19.json +164 -0
  71. package/tests/assets/example20.json +98 -0
  72. package/tests/assets/example21.json +172 -0
  73. package/tests/assets/example22.json +103 -0
  74. package/tests/assets/example23.json +132 -0
  75. package/tests/assets/example24.json +123 -0
  76. package/tests/assets/example25.json +146 -33
  77. package/tests/assets/example26.json +51 -1134
  78. package/tests/assets/example27.json +151 -91
  79. package/tests/assets/example28.json +56 -0
  80. package/tests/assets/example29.json +1206 -0
  81. package/tests/assets/example30.json +168 -0
  82. package/tests/assets/example31.json +46 -0
  83. package/tests/examples/__snapshots__/example01.snap.svg +12 -6
  84. package/tests/examples/__snapshots__/example02.snap.svg +16 -8
  85. package/tests/examples/__snapshots__/example03.snap.svg +36 -18
  86. package/tests/examples/__snapshots__/example04.snap.svg +8 -4
  87. package/tests/examples/__snapshots__/example05.snap.svg +4 -2
  88. package/tests/examples/__snapshots__/example06.snap.svg +4 -2
  89. package/tests/examples/__snapshots__/example07.snap.svg +20 -10
  90. package/tests/examples/__snapshots__/example08.snap.svg +16 -8
  91. package/tests/examples/__snapshots__/example09.snap.svg +80 -40
  92. package/tests/examples/__snapshots__/example10.snap.svg +16 -8
  93. package/tests/examples/__snapshots__/example11.snap.svg +24 -12
  94. package/tests/examples/__snapshots__/example12.snap.svg +16 -8
  95. package/tests/examples/__snapshots__/example13.snap.svg +32 -16
  96. package/tests/examples/__snapshots__/example14.snap.svg +115 -48
  97. package/tests/examples/__snapshots__/example15.snap.svg +36 -18
  98. package/tests/examples/__snapshots__/example16.snap.svg +16 -8
  99. package/tests/examples/__snapshots__/example17.snap.svg +16 -8
  100. package/tests/examples/__snapshots__/example18.snap.svg +20 -10
  101. package/tests/examples/__snapshots__/example19.snap.svg +16 -8
  102. package/tests/examples/__snapshots__/example20.snap.svg +43 -135
  103. package/tests/examples/__snapshots__/example21.snap.svg +34 -18
  104. package/tests/examples/__snapshots__/example22.snap.svg +37 -23
  105. package/tests/examples/__snapshots__/example23.snap.svg +62 -26
  106. package/tests/examples/__snapshots__/example24.snap.svg +50 -28
  107. package/tests/examples/__snapshots__/example25.snap.svg +175 -49
  108. package/tests/examples/__snapshots__/example26.snap.svg +16 -8
  109. package/tests/examples/__snapshots__/example27.snap.svg +24 -12
  110. package/tests/examples/__snapshots__/example28.snap.svg +24 -18
  111. package/tests/examples/__snapshots__/example29.snap.svg +184 -92
  112. package/tests/examples/__snapshots__/example30.snap.svg +48 -24
  113. package/tests/examples/__snapshots__/example31.snap.svg +88 -0
  114. package/tests/examples/example01.test.ts +2 -2
  115. package/tests/examples/example02.test.ts +2 -2
  116. package/tests/examples/example03.test.ts +2 -2
  117. package/tests/examples/example04.test.ts +2 -2
  118. package/tests/examples/example05.test.ts +2 -2
  119. package/tests/examples/example06.test.ts +2 -2
  120. package/tests/examples/example07.test.ts +2 -2
  121. package/tests/examples/example08.test.ts +2 -2
  122. package/tests/examples/example09.test.ts +2 -2
  123. package/tests/examples/example10.test.ts +2 -2
  124. package/tests/examples/{example11.test.tsx → example11.test.ts} +2 -2
  125. package/tests/examples/{example12.test.tsx → example12.test.ts} +2 -2
  126. package/tests/examples/{example13.test.tsx → example13.test.ts} +2 -2
  127. package/tests/examples/example14.test.ts +12 -0
  128. package/tests/examples/example15.test.ts +12 -0
  129. package/tests/examples/example16.test.ts +12 -0
  130. package/tests/examples/example17.test.ts +12 -0
  131. package/tests/examples/example18.test.ts +12 -0
  132. package/tests/examples/example19.test.ts +12 -0
  133. package/tests/examples/example20.test.ts +12 -0
  134. package/tests/examples/example21.test.ts +12 -0
  135. package/tests/examples/example22.test.ts +12 -0
  136. package/tests/examples/example23.test.ts +12 -0
  137. package/tests/examples/example24.test.ts +12 -0
  138. package/tests/examples/example25.test.ts +12 -0
  139. package/tests/examples/example26.test.ts +12 -0
  140. package/tests/examples/example27.test.ts +3 -232
  141. package/tests/examples/example28.test.ts +2 -2
  142. package/tests/examples/example29.test.ts +1 -2
  143. package/tests/examples/example30.test.ts +2 -2
  144. package/tests/examples/example31.test.ts +12 -0
  145. package/site/examples/example01-basic.page.tsx +0 -105
  146. package/site/examples/example04-single-symbol.page.tsx +0 -46
  147. package/site/examples/example15-rp2040-caps.page.tsx +0 -623
  148. package/site/examples/example16-core-repro51.page.tsx +0 -107
  149. package/site/examples/example17-straight-line-trace.page.tsx +0 -165
  150. package/tests/examples/example14.test.tsx +0 -11
  151. package/tests/examples/example15.test.tsx +0 -629
  152. package/tests/examples/example16.test.tsx +0 -113
  153. package/tests/examples/example17.test.tsx +0 -171
  154. package/tests/examples/example18.test.tsx +0 -187
  155. package/tests/examples/example19.test.tsx +0 -175
  156. package/tests/examples/example20.test.tsx +0 -190
  157. package/tests/examples/example21.test.tsx +0 -183
  158. package/tests/examples/example22.test.tsx +0 -109
  159. package/tests/examples/example23.test.tsx +0 -109
  160. package/tests/examples/example24.test.tsx +0 -114
  161. package/tests/examples/example25.test.tsx +0 -143
  162. package/tests/examples/example26.test.tsx +0 -134
@@ -0,0 +1,368 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
+ import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
4
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
5
+ import { simplifyPath } from "lib/solvers/TraceCleanupSolver/simplifyPath"
6
+ import { detectTraceLabelOverlap } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap"
7
+ import { generateRerouteCandidates } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace"
8
+ import type { InputProblem } from "lib/types/InputProblem"
9
+ import { dir } from "lib/utils/dir"
10
+ import {
11
+ countPathIntersections,
12
+ getPathKey,
13
+ getPathLength,
14
+ isPathCollidingWithChipInterior,
15
+ } from "./geometry"
16
+ import type {
17
+ ChipObstacle,
18
+ RerouteCandidateResult,
19
+ TracePathScore,
20
+ } from "./types"
21
+
22
+ const LABEL_SIDE_CLEARANCE = 0.1
23
+ const LABEL_HUG_CLEARANCE = 0.001
24
+
25
+ export const findBestReroutePath = ({
26
+ trace,
27
+ obstacleLabel,
28
+ inputProblem,
29
+ outputTraces,
30
+ outputNetLabelPlacements,
31
+ chipObstacles,
32
+ }: {
33
+ trace: SolvedTracePath
34
+ obstacleLabel: NetLabelPlacement
35
+ inputProblem: InputProblem
36
+ outputTraces: SolvedTracePath[]
37
+ outputNetLabelPlacements: NetLabelPlacement[]
38
+ chipObstacles: ChipObstacle[]
39
+ }) => {
40
+ const candidateResults = generateRerouteCandidateResults({
41
+ trace,
42
+ label: obstacleLabel,
43
+ inputProblem,
44
+ outputTraces,
45
+ outputNetLabelPlacements,
46
+ chipObstacles,
47
+ })
48
+ const bestPath = selectBestReroutePath({
49
+ trace,
50
+ obstacleLabel,
51
+ outputTraces,
52
+ outputNetLabelPlacements,
53
+ candidateResults,
54
+ })
55
+
56
+ markSelectedCandidate(candidateResults, bestPath)
57
+
58
+ return { bestPath, candidateResults }
59
+ }
60
+
61
+ export const generateRerouteCandidateResults = ({
62
+ trace,
63
+ label,
64
+ inputProblem,
65
+ outputTraces,
66
+ outputNetLabelPlacements,
67
+ chipObstacles,
68
+ }: {
69
+ trace: SolvedTracePath
70
+ label: NetLabelPlacement
71
+ inputProblem: InputProblem
72
+ outputTraces: SolvedTracePath[]
73
+ outputNetLabelPlacements: NetLabelPlacement[]
74
+ chipObstacles: ChipObstacle[]
75
+ }) => {
76
+ const rawCandidates = generateCandidatePaths(trace, label, inputProblem)
77
+ const seen = new Set<string>()
78
+ const candidateResults: RerouteCandidateResult[] = []
79
+
80
+ for (const rawCandidate of rawCandidates) {
81
+ candidateResults.push(
82
+ createCandidateResult({
83
+ trace,
84
+ obstacleLabel: label,
85
+ path: simplifyPath(rawCandidate),
86
+ seen,
87
+ outputTraces,
88
+ outputNetLabelPlacements,
89
+ chipObstacles,
90
+ }),
91
+ )
92
+ }
93
+
94
+ return candidateResults
95
+ }
96
+
97
+ const generateCandidatePaths = (
98
+ trace: SolvedTracePath,
99
+ label: NetLabelPlacement,
100
+ inputProblem: InputProblem,
101
+ ) => [
102
+ ...generateLabelHugCandidates(trace, label),
103
+ ...generateRerouteCandidates({
104
+ trace,
105
+ label,
106
+ problem: inputProblem,
107
+ paddingBuffer: LABEL_SIDE_CLEARANCE,
108
+ detourCount: 0,
109
+ }),
110
+ ...generateEndpointDetourCandidates(trace, label),
111
+ ]
112
+
113
+ const createCandidateResult = ({
114
+ trace,
115
+ obstacleLabel,
116
+ path,
117
+ seen,
118
+ outputTraces,
119
+ outputNetLabelPlacements,
120
+ chipObstacles,
121
+ }: {
122
+ trace: SolvedTracePath
123
+ obstacleLabel: NetLabelPlacement
124
+ path: Point[]
125
+ seen: Set<string>
126
+ outputTraces: SolvedTracePath[]
127
+ outputNetLabelPlacements: NetLabelPlacement[]
128
+ chipObstacles: ChipObstacle[]
129
+ }): RerouteCandidateResult => {
130
+ const key = getPathKey(path)
131
+
132
+ if (seen.has(key)) {
133
+ return {
134
+ path,
135
+ status: "duplicate",
136
+ selected: false,
137
+ }
138
+ }
139
+
140
+ seen.add(key)
141
+
142
+ if (isPathCollidingWithChipInterior(path, chipObstacles)) {
143
+ return {
144
+ path,
145
+ status: "chip-collision",
146
+ selected: false,
147
+ }
148
+ }
149
+
150
+ return {
151
+ path,
152
+ score: scoreTracePath({
153
+ trace,
154
+ tracePath: path,
155
+ obstacleLabel,
156
+ outputTraces,
157
+ outputNetLabelPlacements,
158
+ }),
159
+ status: "valid",
160
+ selected: false,
161
+ }
162
+ }
163
+
164
+ const generateEndpointDetourCandidates = (
165
+ trace: SolvedTracePath,
166
+ label: NetLabelPlacement,
167
+ ): Point[][] => {
168
+ const start = trace.tracePath[0]
169
+ const end = trace.tracePath[trace.tracePath.length - 1]
170
+ if (!start || !end) return []
171
+
172
+ const bounds = getRectBounds(label.center, label.width, label.height)
173
+ const padding = LABEL_SIDE_CLEARANCE
174
+ const labelDirection = dir(label.orientation)
175
+ const labelSideX =
176
+ labelDirection.x < 0 ? bounds.minX - padding : bounds.maxX + padding
177
+ const oppositeSideX =
178
+ labelDirection.x < 0 ? bounds.maxX + padding : bounds.minX - padding
179
+ const topY = bounds.maxY + padding
180
+ const bottomY = bounds.minY - padding
181
+ const candidates: Point[][] = []
182
+
183
+ for (const sideX of [labelSideX, oppositeSideX]) {
184
+ candidates.push(
185
+ [
186
+ start,
187
+ { x: start.x, y: topY },
188
+ { x: sideX, y: topY },
189
+ { x: sideX, y: bottomY },
190
+ { x: end.x, y: bottomY },
191
+ end,
192
+ ],
193
+ [
194
+ start,
195
+ { x: start.x, y: bottomY },
196
+ { x: sideX, y: bottomY },
197
+ { x: sideX, y: topY },
198
+ { x: end.x, y: topY },
199
+ end,
200
+ ],
201
+ )
202
+ }
203
+
204
+ return candidates
205
+ }
206
+
207
+ const generateLabelHugCandidates = (
208
+ trace: SolvedTracePath,
209
+ label: NetLabelPlacement,
210
+ ): Point[][] => {
211
+ const start = trace.tracePath[0]
212
+ const end = trace.tracePath[trace.tracePath.length - 1]
213
+ if (!start || !end) return []
214
+
215
+ const labelDirection = dir(label.orientation)
216
+ if (labelDirection.x === 0) return []
217
+
218
+ const bounds = getRectBounds(label.center, label.width, label.height)
219
+ const sideX =
220
+ labelDirection.x < 0
221
+ ? bounds.minX - LABEL_SIDE_CLEARANCE
222
+ : bounds.maxX + LABEL_SIDE_CLEARANCE
223
+ const topY = bounds.maxY + LABEL_HUG_CLEARANCE
224
+ const bottomY = bounds.minY - LABEL_HUG_CLEARANCE
225
+ const startsAboveEnd = start.y >= end.y
226
+ const firstY = startsAboveEnd ? topY : bottomY
227
+ const secondY = startsAboveEnd ? bottomY : topY
228
+
229
+ return [
230
+ [
231
+ start,
232
+ { x: start.x, y: firstY },
233
+ { x: sideX, y: firstY },
234
+ { x: sideX, y: secondY },
235
+ { x: end.x, y: secondY },
236
+ end,
237
+ ],
238
+ [
239
+ start,
240
+ { x: start.x, y: secondY },
241
+ { x: sideX, y: secondY },
242
+ { x: sideX, y: firstY },
243
+ { x: end.x, y: firstY },
244
+ end,
245
+ ],
246
+ ]
247
+ }
248
+
249
+ const selectBestReroutePath = ({
250
+ trace,
251
+ obstacleLabel,
252
+ outputTraces,
253
+ outputNetLabelPlacements,
254
+ candidateResults,
255
+ }: {
256
+ trace: SolvedTracePath
257
+ obstacleLabel: NetLabelPlacement
258
+ outputTraces: SolvedTracePath[]
259
+ outputNetLabelPlacements: NetLabelPlacement[]
260
+ candidateResults: RerouteCandidateResult[]
261
+ }) => {
262
+ let bestPath: Point[] | null = null
263
+ let bestScore = scoreTracePath({
264
+ trace,
265
+ tracePath: trace.tracePath,
266
+ obstacleLabel,
267
+ outputTraces,
268
+ outputNetLabelPlacements,
269
+ })
270
+
271
+ for (const candidate of candidateResults) {
272
+ if (candidate.status !== "valid" || !candidate.score) continue
273
+ if (!isBetterScore(candidate.score, bestScore)) continue
274
+
275
+ bestScore = candidate.score
276
+ bestPath = candidate.path
277
+ }
278
+
279
+ return bestPath
280
+ }
281
+
282
+ const markSelectedCandidate = (
283
+ candidateResults: RerouteCandidateResult[],
284
+ bestPath: Point[] | null,
285
+ ) => {
286
+ if (!bestPath) return
287
+
288
+ for (const candidate of candidateResults) {
289
+ candidate.selected = candidate.path === bestPath
290
+ }
291
+ }
292
+
293
+ const scoreTracePath = ({
294
+ trace,
295
+ tracePath,
296
+ obstacleLabel,
297
+ outputTraces,
298
+ outputNetLabelPlacements,
299
+ }: {
300
+ trace: SolvedTracePath
301
+ tracePath: Point[]
302
+ obstacleLabel: NetLabelPlacement
303
+ outputTraces: SolvedTracePath[]
304
+ outputNetLabelPlacements: NetLabelPlacement[]
305
+ }): TracePathScore => {
306
+ const candidateTrace = { ...trace, tracePath }
307
+ return {
308
+ labelIntersections: detectTraceLabelOverlap({
309
+ traces: [candidateTrace],
310
+ netLabels: outputNetLabelPlacements,
311
+ }).length,
312
+ labelHugDistance: getLabelHugDistance(tracePath, obstacleLabel),
313
+ traceIntersections: countTraceIntersections(candidateTrace, outputTraces),
314
+ pathLength: getPathLength(tracePath),
315
+ }
316
+ }
317
+
318
+ const countTraceIntersections = (
319
+ trace: SolvedTracePath,
320
+ outputTraces: SolvedTracePath[],
321
+ ) => {
322
+ let count = 0
323
+ for (const otherTrace of outputTraces) {
324
+ if (otherTrace.mspPairId === trace.mspPairId) continue
325
+ count += countPathIntersections(trace.tracePath, otherTrace.tracePath)
326
+ }
327
+ return count
328
+ }
329
+
330
+ const isBetterScore = (score: TracePathScore, bestScore: TracePathScore) => {
331
+ if (score.labelIntersections !== bestScore.labelIntersections) {
332
+ return score.labelIntersections < bestScore.labelIntersections
333
+ }
334
+ if (score.labelHugDistance !== bestScore.labelHugDistance) {
335
+ return score.labelHugDistance < bestScore.labelHugDistance
336
+ }
337
+ if (score.traceIntersections !== bestScore.traceIntersections) {
338
+ return score.traceIntersections < bestScore.traceIntersections
339
+ }
340
+ return score.pathLength < bestScore.pathLength
341
+ }
342
+
343
+ const getLabelHugDistance = (
344
+ tracePath: Point[],
345
+ obstacleLabel: NetLabelPlacement,
346
+ ) => {
347
+ const bounds = getRectBounds(
348
+ obstacleLabel.center,
349
+ obstacleLabel.width,
350
+ obstacleLabel.height,
351
+ )
352
+ let distance = 0
353
+
354
+ for (const point of tracePath) {
355
+ distance += getPointDistanceFromRect(point, bounds)
356
+ }
357
+
358
+ return distance
359
+ }
360
+
361
+ const getPointDistanceFromRect = (
362
+ point: Point,
363
+ rect: { minX: number; minY: number; maxX: number; maxY: number },
364
+ ) => {
365
+ const dx = Math.max(rect.minX - point.x, 0, point.x - rect.maxX)
366
+ const dy = Math.max(rect.minY - point.y, 0, point.y - rect.maxY)
367
+ return dx + dy
368
+ }
@@ -0,0 +1,47 @@
1
+ import type { Point } from "@tscircuit/math-utils"
2
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
+ import type { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
4
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
5
+ import type { TraceLabelOverlap } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap"
6
+ import type { InputProblem } from "lib/types/InputProblem"
7
+
8
+ export interface Example28SolverParams {
9
+ inputProblem: InputProblem
10
+ traces: SolvedTracePath[]
11
+ netLabelPlacements: NetLabelPlacement[]
12
+ }
13
+
14
+ export type TracePathScore = {
15
+ labelIntersections: number
16
+ labelHugDistance: number
17
+ traceIntersections: number
18
+ pathLength: number
19
+ }
20
+
21
+ export type SegmentOrientation = "horizontal" | "vertical"
22
+
23
+ export type PathSegment = {
24
+ start: Point
25
+ end: Point
26
+ orientation: SegmentOrientation
27
+ }
28
+
29
+ export type ChipObstacle = ReturnType<typeof getObstacleRects>[number]
30
+
31
+ export type RerouteCandidateResult = {
32
+ path: Point[]
33
+ score?: TracePathScore
34
+ status: "valid" | "duplicate" | "chip-collision"
35
+ selected: boolean
36
+ }
37
+
38
+ export type Example28VisualizationState = {
39
+ inputProblem: InputProblem
40
+ outputTraces: SolvedTracePath[]
41
+ outputNetLabelPlacements: NetLabelPlacement[]
42
+ solved: boolean
43
+ currentOverlap: TraceLabelOverlap | null
44
+ currentCandidateResults: RerouteCandidateResult[]
45
+ }
46
+
47
+ export const EPS = 1e-9
@@ -0,0 +1,96 @@
1
+ import type { GraphicsObject } from "graphics-debug"
2
+ import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
3
+ import { getColorFromString } from "lib/utils/getColorFromString"
4
+ import type {
5
+ Example28VisualizationState,
6
+ RerouteCandidateResult,
7
+ } from "./types"
8
+
9
+ const CANDIDATE_SELECTED_COLOR = "blue"
10
+ const CANDIDATE_REJECTED_COLOR = "red"
11
+
12
+ export const visualizeExample28Solver = (
13
+ state: Example28VisualizationState,
14
+ ): GraphicsObject => {
15
+ const graphics = visualizeInputProblem(state.inputProblem)
16
+
17
+ drawTraces(graphics, state)
18
+ drawNetLabels(graphics, state)
19
+
20
+ if (!state.solved) {
21
+ drawCurrentCandidates(graphics, state.currentCandidateResults)
22
+ drawCurrentOverlap(graphics, state)
23
+ }
24
+
25
+ return graphics
26
+ }
27
+
28
+ const drawTraces = (
29
+ graphics: GraphicsObject,
30
+ state: Example28VisualizationState,
31
+ ) => {
32
+ for (const trace of state.outputTraces) {
33
+ graphics.lines!.push({
34
+ points: trace.tracePath,
35
+ strokeColor: "purple",
36
+ } as any)
37
+ }
38
+ }
39
+
40
+ const drawNetLabels = (
41
+ graphics: GraphicsObject,
42
+ state: Example28VisualizationState,
43
+ ) => {
44
+ for (const label of state.outputNetLabelPlacements) {
45
+ const color = getColorFromString(label.globalConnNetId, 0.35)
46
+ graphics.rects!.push({
47
+ center: label.center,
48
+ width: label.width,
49
+ height: label.height,
50
+ fill: color,
51
+ strokeColor: getColorFromString(label.globalConnNetId, 0.9),
52
+ label: `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}`,
53
+ } as any)
54
+ graphics.points!.push({
55
+ ...label.anchorPoint,
56
+ color: getColorFromString(label.globalConnNetId, 0.9),
57
+ label: `anchorPoint\norientation: ${label.orientation}`,
58
+ } as any)
59
+ }
60
+ }
61
+
62
+ const drawCurrentCandidates = (
63
+ graphics: GraphicsObject,
64
+ candidateResults: RerouteCandidateResult[],
65
+ ) => {
66
+ for (const candidate of candidateResults) {
67
+ const scoreLabel = candidate.score
68
+ ? `\nlabel intersections: ${candidate.score.labelIntersections}\ntrace intersections: ${candidate.score.traceIntersections}`
69
+ : ""
70
+ graphics.lines!.push({
71
+ points: candidate.path,
72
+ strokeColor: candidate.selected
73
+ ? CANDIDATE_SELECTED_COLOR
74
+ : CANDIDATE_REJECTED_COLOR,
75
+ strokeDash: candidate.selected ? undefined : "4 2",
76
+ label: `${candidate.selected ? "selected" : candidate.status} reroute${scoreLabel}`,
77
+ } as any)
78
+ }
79
+ }
80
+
81
+ const drawCurrentOverlap = (
82
+ graphics: GraphicsObject,
83
+ state: Example28VisualizationState,
84
+ ) => {
85
+ if (!state.currentOverlap) return
86
+
87
+ const label = state.currentOverlap.label
88
+ graphics.rects!.push({
89
+ center: label.center,
90
+ width: label.width,
91
+ height: label.height,
92
+ fill: "rgba(255, 0, 0, 0.2)",
93
+ strokeColor: CANDIDATE_REJECTED_COLOR,
94
+ label: `overlap target\n${label.netId ?? label.globalConnNetId}`,
95
+ } as any)
96
+ }
@@ -20,6 +20,7 @@ import { expandChipsToFitPins } from "./expandChipsToFitPins"
20
20
  import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePairSolver"
21
21
  import { MergedNetLabelObstacleSolver } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver"
22
22
  import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver"
23
+ import { Example28Solver } from "../Example28Solver/Example28Solver"
23
24
 
24
25
  type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
25
26
  solverName: string
@@ -69,6 +70,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
69
70
  labelMergingSolver?: MergedNetLabelObstacleSolver
70
71
  traceLabelOverlapAvoidanceSolver?: TraceLabelOverlapAvoidanceSolver
71
72
  traceCleanupSolver?: TraceCleanupSolver
73
+ example28Solver?: Example28Solver
72
74
 
73
75
  startTimeOfPhase: Record<string, number>
74
76
  endTimeOfPhase: Record<string, number>
@@ -224,6 +226,20 @@ export class SchematicTracePipelineSolver extends BaseSolver {
224
226
  ]
225
227
  },
226
228
  ),
229
+ definePipelineStep("example28Solver", Example28Solver, (instance) => {
230
+ const traces =
231
+ instance.traceCleanupSolver?.getOutput().traces ??
232
+ instance.traceLabelOverlapAvoidanceSolver!.getOutput().traces
233
+
234
+ return [
235
+ {
236
+ inputProblem: instance.inputProblem,
237
+ traces,
238
+ netLabelPlacements:
239
+ instance.netLabelPlacementSolver!.netLabelPlacements,
240
+ },
241
+ ]
242
+ }),
227
243
  ]
228
244
 
229
245
  constructor(inputProblem: InputProblem) {
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.48",
4
+ "version": "0.0.49",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -0,0 +1,2 @@
1
+ export { inputProblem } from "./SchematicTraceSingleLineSolver2_01-example17.page"
2
+ export { default } from "./SchematicTraceSingleLineSolver2_01-example17.page"
@@ -0,0 +1,23 @@
1
+ import { useMemo } from "react"
2
+ import { GenericSolverDebugger } from "site/components/GenericSolverDebugger"
3
+ import { TraceCleanupSolver } from "lib/solvers/TraceCleanupSolver/TraceCleanupSolver"
4
+ import inputData from "../../tests/assets/TraceCleanupSolver.test.input.json"
5
+
6
+ export default () => {
7
+ const solver = useMemo(
8
+ () =>
9
+ new TraceCleanupSolver({
10
+ ...inputData,
11
+ targetTraceIds: new Set(inputData.targetTraceIds),
12
+ mergedLabelNetIdMap: Object.fromEntries(
13
+ Object.entries(inputData.mergedLabelNetIdMap).map(([k, v]) => [
14
+ k,
15
+ new Set(v as any),
16
+ ]),
17
+ ),
18
+ } as any),
19
+ [],
20
+ )
21
+
22
+ return <GenericSolverDebugger solver={solver} />
23
+ }
@@ -0,0 +1,18 @@
1
+ import { useMemo } from "react"
2
+ import { GenericSolverDebugger } from "site/components/GenericSolverDebugger"
3
+ import { MergedNetLabelObstacleSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver"
4
+ import inputData from "../../tests/assets/MergedNetLabelObstacles.test.input.json"
5
+
6
+ export default () => {
7
+ const solver = useMemo(
8
+ () =>
9
+ new MergedNetLabelObstacleSolver({
10
+ netLabelPlacements: inputData.netLabelPlacements as any,
11
+ inputProblem: inputData.inputProblem as any,
12
+ traces: inputData.traces as any,
13
+ }),
14
+ [],
15
+ )
16
+
17
+ return <GenericSolverDebugger solver={solver} />
18
+ }
@@ -0,0 +1,24 @@
1
+ import { useMemo } from "react"
2
+ import { GenericSolverDebugger } from "site/components/GenericSolverDebugger"
3
+ import { OverlapAvoidanceStepSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver"
4
+ import inputData from "../../tests/assets/OverlapAvoidanceStepSolver.test.input.json"
5
+
6
+ export default () => {
7
+ const solver = useMemo(
8
+ () =>
9
+ new OverlapAvoidanceStepSolver({
10
+ inputProblem: inputData.problem as any,
11
+ traces: inputData.traces as any,
12
+ netLabelPlacements: inputData.netLabelPlacements as any,
13
+ mergedLabelNetIdMap: Object.fromEntries(
14
+ Object.entries(inputData.mergedLabelNetIdMap).map(([k, v]) => [
15
+ k,
16
+ new Set(v as any),
17
+ ]),
18
+ ),
19
+ } as any),
20
+ [],
21
+ )
22
+
23
+ return <GenericSolverDebugger solver={solver} />
24
+ }
@@ -0,0 +1,10 @@
1
+ import { useMemo } from "react"
2
+ import { GenericSolverDebugger } from "site/components/GenericSolverDebugger"
3
+ import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver"
4
+ import inputData from "../../tests/assets/SingleOverlapSolver.test.input.json"
5
+
6
+ export default () => {
7
+ const solver = useMemo(() => new SingleOverlapSolver(inputData as any), [])
8
+
9
+ return <GenericSolverDebugger solver={solver} />
10
+ }