@tscircuit/schematic-trace-solver 0.0.136 → 0.0.140

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 (25) hide show
  1. package/.github/workflows/bun-pver-release.yml +7 -3
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +333 -45
  4. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationObstacleIndex.ts +182 -0
  5. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +67 -25
  6. package/lib/solvers/AvailableNetOrientationSolver/constants.ts +2 -0
  7. package/lib/solvers/Example28Solver/labelMovement.ts +71 -0
  8. package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +5 -2
  9. package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +93 -14
  10. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +12 -4
  11. package/package.json +5 -1
  12. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +4 -4
  13. package/tests/bug-reports/bug-report-20260721T221026Z/bug-report-20260721T221026Z.test.ts +11 -0
  14. package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +3 -3
  15. package/tests/bug-reports/bug-report-20260815T073240Z/bug-report-20260815T073240Z.test.ts +39 -0
  16. package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +237 -0
  17. package/tests/bug-reports/bug-report-20260819T091818Z/bug-report-20260819T091818Z.json +1052 -0
  18. package/tests/bug-reports/bug-report-20260819T091818Z/bug-report-20260819T091818Z.test.ts +33 -0
  19. package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +4 -4
  20. package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +124 -0
  21. package/tests/repros/assets/repro-board-648-esp12f-section.input.json +537 -0
  22. package/tests/repros/repro-board-589-regulator-section.test.ts +23 -1
  23. package/tests/repros/repro-board-648-esp12f-section.test.ts +13 -0
  24. package/tests/repros/repro161-power-section-autolayout.test.ts +7 -0
  25. package/tests/solvers/AvailableNetOrientationSolver/AvailableNetOrientationObstacleIndex.test.ts +122 -0
@@ -3,8 +3,9 @@ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetL
3
3
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
4
  import { isPathCollidingWithObstacles } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
5
5
  import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
6
+ import { moveAttachedLabelsToReroutedTrace } from "lib/solvers/Example28Solver/labelMovement"
7
+ import { tracePathContainsPoint } from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
6
8
  import { simplifyPath } from "lib/solvers/TraceCleanupSolver/simplifyPath"
7
- import { preservesLabelAnchors } from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors"
8
9
  import {
9
10
  getVisibleTraceLength,
10
11
  getVisibleTraceSegmentCount,
@@ -95,6 +96,52 @@ const railIsOnFacingSide = ({
95
96
  return false
96
97
  }
97
98
 
99
+ const getAttachedLabelIndexes = (
100
+ trace: SolvedTracePath,
101
+ netLabelPlacements: NetLabelPlacement[],
102
+ ) =>
103
+ netLabelPlacements.flatMap((label, index) => {
104
+ const labelOwnsTrace =
105
+ label.mspConnectionPairIds.includes(trace.mspPairId) ||
106
+ (label.mspConnectionPairIds.length === 0 &&
107
+ label.globalConnNetId === trace.globalConnNetId)
108
+ return labelOwnsTrace &&
109
+ tracePathContainsPoint(trace.tracePath, label.anchorPoint)
110
+ ? [index]
111
+ : []
112
+ })
113
+
114
+ const moveAttachedLabels = ({
115
+ trace,
116
+ reroutedTracePath,
117
+ netLabelPlacements,
118
+ attachedLabelIndexes,
119
+ }: {
120
+ trace: SolvedTracePath
121
+ reroutedTracePath: Point[]
122
+ netLabelPlacements: NetLabelPlacement[]
123
+ attachedLabelIndexes: number[]
124
+ }) => {
125
+ const attachedLabels = attachedLabelIndexes.map(
126
+ (index) => netLabelPlacements[index]!,
127
+ )
128
+ const movedAttachedLabels = moveAttachedLabelsToReroutedTrace({
129
+ trace,
130
+ originalTracePath: trace.tracePath,
131
+ reroutedTracePath,
132
+ netLabelPlacements: attachedLabels,
133
+ })
134
+ const movedLabelByIndex = new Map(
135
+ attachedLabelIndexes.map((labelIndex, index) => [
136
+ labelIndex,
137
+ movedAttachedLabels[index]!,
138
+ ]),
139
+ )
140
+ return netLabelPlacements.map(
141
+ (label, index) => movedLabelByIndex.get(index) ?? label,
142
+ )
143
+ }
144
+
98
145
  const getAlignedBranchPath = ({
99
146
  donorTrace,
100
147
  branchTrace,
@@ -145,13 +192,15 @@ const candidateIsClear = ({
145
192
  originalTrace,
146
193
  traces,
147
194
  inputProblem,
148
- netLabelPlacements,
195
+ candidateNetLabelPlacements,
196
+ attachedLabelIndexes,
149
197
  }: {
150
198
  candidateTrace: SolvedTracePath
151
199
  originalTrace: SolvedTracePath
152
200
  traces: SolvedTracePath[]
153
201
  inputProblem: InputProblem
154
- netLabelPlacements: NetLabelPlacement[]
202
+ candidateNetLabelPlacements: NetLabelPlacement[]
203
+ attachedLabelIndexes: number[]
155
204
  }) => {
156
205
  const obstacles = getObstacleRects(inputProblem)
157
206
  if (isPathCollidingWithObstacles(candidateTrace.tracePath, obstacles)) {
@@ -165,15 +214,18 @@ const candidateIsClear = ({
165
214
  return false
166
215
  }
167
216
 
168
- const candidateTraces = traces.map((trace) => {
169
- if (trace.mspPairId === originalTrace.mspPairId) return candidateTrace
170
- return trace
171
- })
172
- if (!preservesLabelAnchors(netLabelPlacements, traces, candidateTraces)) {
217
+ if (
218
+ !attachedLabelIndexes.every((index) =>
219
+ tracePathContainsPoint(
220
+ candidateTrace.tracePath,
221
+ candidateNetLabelPlacements[index]!.anchorPoint,
222
+ ),
223
+ )
224
+ ) {
173
225
  return false
174
226
  }
175
227
 
176
- const otherNetLabelPlacements = netLabelPlacements.filter(
228
+ const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
177
229
  (label) => label.globalConnNetId !== candidateTrace.globalConnNetId,
178
230
  )
179
231
  if (
@@ -185,7 +237,7 @@ const candidateIsClear = ({
185
237
  return false
186
238
  }
187
239
 
188
- const sameNetLabelPlacements = netLabelPlacements.filter(
240
+ const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
189
241
  (label) => label.globalConnNetId === candidateTrace.globalConnNetId,
190
242
  )
191
243
  // A same-net rail may follow its label edge, but never enter the label body.
@@ -221,11 +273,18 @@ export const alignSameNetJunctions = ({
221
273
  netLabelPlacements,
222
274
  }: AlignSameNetJunctionsInput) => {
223
275
  let outputTraces = [...traces]
276
+ let outputNetLabelPlacements = [...netLabelPlacements]
224
277
  const alignedBranchTraceIds = new Set<string>()
225
278
  let alignedJunctionCount = 0
226
279
 
227
- // Reuse each aligned branch as the rail for the next load in the chain.
228
- for (const donorTraceId of traces.map((trace) => trace.mspPairId)) {
280
+ // Reuse each aligned branch as the rail for the next load in the chain. An
281
+ // aligned branch may already have had its donor turn, so queue it again when
282
+ // its geometry changes.
283
+ const donorTraceIds = traces.map((trace) => trace.mspPairId)
284
+ const pendingDonorTraceIds = new Set(donorTraceIds)
285
+ for (let donorIndex = 0; donorIndex < donorTraceIds.length; donorIndex++) {
286
+ const donorTraceId = donorTraceIds[donorIndex]!
287
+ pendingDonorTraceIds.delete(donorTraceId)
229
288
  const donorTrace = outputTraces.find(
230
289
  (trace) => trace.mspPairId === donorTraceId,
231
290
  )!
@@ -252,13 +311,24 @@ export const alignSameNetJunctions = ({
252
311
  if (!removesVisibleSegment && !shortensVisibleTrace) {
253
312
  continue
254
313
  }
314
+ const attachedLabelIndexes = getAttachedLabelIndexes(
315
+ branchTrace,
316
+ outputNetLabelPlacements,
317
+ )
318
+ const candidateNetLabelPlacements = moveAttachedLabels({
319
+ trace: branchTrace,
320
+ reroutedTracePath: candidatePath,
321
+ netLabelPlacements: outputNetLabelPlacements,
322
+ attachedLabelIndexes,
323
+ })
255
324
  if (
256
325
  !candidateIsClear({
257
326
  candidateTrace,
258
327
  originalTrace: branchTrace,
259
328
  traces: outputTraces,
260
329
  inputProblem,
261
- netLabelPlacements,
330
+ candidateNetLabelPlacements,
331
+ attachedLabelIndexes,
262
332
  })
263
333
  ) {
264
334
  continue
@@ -268,10 +338,19 @@ export const alignSameNetJunctions = ({
268
338
  if (trace.mspPairId === branchTrace.mspPairId) return candidateTrace
269
339
  return trace
270
340
  })
341
+ outputNetLabelPlacements = candidateNetLabelPlacements
271
342
  alignedBranchTraceIds.add(branchTrace.mspPairId)
272
343
  alignedJunctionCount++
344
+ if (!pendingDonorTraceIds.has(branchTrace.mspPairId)) {
345
+ donorTraceIds.push(branchTrace.mspPairId)
346
+ pendingDonorTraceIds.add(branchTrace.mspPairId)
347
+ }
273
348
  }
274
349
  }
275
350
 
276
- return { traces: outputTraces, alignedJunctionCount }
351
+ return {
352
+ traces: outputTraces,
353
+ netLabelPlacements: outputNetLabelPlacements,
354
+ alignedJunctionCount,
355
+ }
277
356
  }
@@ -22,7 +22,10 @@ import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePa
22
22
  import { MergedNetLabelObstacleSolver } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver"
23
23
  import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver"
24
24
  import { Example28Solver } from "../Example28Solver/Example28Solver"
25
- import { moveAttachedLabelsToReroutedTrace } from "../Example28Solver/labelMovement"
25
+ import {
26
+ moveAttachedLabelsToReroutedTrace,
27
+ moveNetLabelConnectorsToReroutedTraces,
28
+ } from "../Example28Solver/labelMovement"
26
29
  import { AvailableNetOrientationSolver } from "../AvailableNetOrientationSolver/AvailableNetOrientationSolver"
27
30
  import { RailNetLabelCornerPlacementSolver } from "../RailNetLabelCornerPlacementSolver/RailNetLabelCornerPlacementSolver"
28
31
  import { TraceAnchoredNetLabelOverlapSolver } from "../TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver"
@@ -426,12 +429,17 @@ export class SchematicTracePipelineSolver extends BaseSolver {
426
429
  const previousOutput =
427
430
  instance.preAlignmentNetLabelTraceCollisionSolver!.getOutput()
428
431
  const alignmentOutput = instance.traceCleanupSolver2!.getOutput()
432
+ const connectorMovement = moveNetLabelConnectorsToReroutedTraces({
433
+ originalTraces: previousOutput.traces,
434
+ reroutedTraces: alignmentOutput.traces,
435
+ netLabelPlacements: previousOutput.netLabelPlacements,
436
+ })
429
437
  const previousTraceMap = new Map(
430
438
  previousOutput.traces.map((trace) => [trace.mspPairId, trace]),
431
439
  )
432
- let netLabelPlacements = previousOutput.netLabelPlacements
440
+ let netLabelPlacements = connectorMovement.netLabelPlacements
433
441
 
434
- for (const trace of alignmentOutput.traces) {
442
+ for (const trace of connectorMovement.traces) {
435
443
  const previousTrace = previousTraceMap.get(trace.mspPairId)
436
444
  if (!previousTrace || previousTrace.tracePath === trace.tracePath) {
437
445
  continue
@@ -455,7 +463,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
455
463
  return [
456
464
  {
457
465
  inputProblem: instance.inputProblem,
458
- traces: alignmentOutput.traces,
466
+ traces: connectorMovement.traces,
459
467
  netLabelPlacements,
460
468
  },
461
469
  ]
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "https://github.com/tscircuit/schematic-trace-solver.git"
6
+ },
3
7
  "main": "dist/index.js",
4
- "version": "0.0.136",
8
+ "version": "0.0.140",
5
9
  "type": "module",
6
10
  "scripts": {
7
11
  "start": "cosmos",