@tscircuit/schematic-trace-solver 0.0.157 → 0.0.159

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 (175) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +269 -58
  3. package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +15 -8
  4. package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +5 -0
  5. package/lib/solvers/MspConnectionPairSolver/getGroundConnectionPolicy.ts +83 -0
  6. package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +6 -1
  7. package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +196 -67
  8. package/lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts +122 -0
  9. package/package.json +1 -1
  10. package/site/bug-reports/bug-report-20260826T072956Z.page.tsx +4 -0
  11. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +2 -2
  12. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +2 -2
  13. package/tests/bug-reports/bug-report-20260707T020342Z/__snapshots__/bug-report-20260707T020342Z.snap.svg +2 -2
  14. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +2 -2
  15. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +2 -2
  16. package/tests/bug-reports/bug-report-20260707T134722Z/__snapshots__/bug-report-20260707T134722Z.snap.svg +2 -2
  17. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +2 -2
  18. package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +2 -2
  19. package/tests/bug-reports/bug-report-20260707T141421Z/__snapshots__/bug-report-20260707T141421Z.snap.svg +2 -2
  20. package/tests/bug-reports/bug-report-20260707T230831Z/__snapshots__/bug-report-20260707T230831Z.snap.svg +2 -2
  21. package/tests/bug-reports/bug-report-20260708T053736Z/__snapshots__/bug-report-20260708T053736Z.snap.svg +2 -2
  22. package/tests/bug-reports/bug-report-20260708T055430Z/__snapshots__/bug-report-20260708T055430Z.snap.svg +2 -2
  23. package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +2 -2
  24. package/tests/bug-reports/bug-report-20260716T144856Z/__snapshots__/bug-report-20260716T144856Z.snap.svg +2 -2
  25. package/tests/bug-reports/bug-report-20260717T022934Z/__snapshots__/bug-report-20260717T022934Z.snap.svg +2 -2
  26. package/tests/bug-reports/bug-report-20260717T031704Z/__snapshots__/bug-report-20260717T031704Z.snap.svg +2 -2
  27. package/tests/bug-reports/bug-report-20260717T042845Z/__snapshots__/bug-report-20260717T042845Z.snap.svg +2 -2
  28. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
  29. package/tests/bug-reports/bug-report-20260724T175257Z/__snapshots__/bug-report-20260724T175257Z.snap.svg +2 -2
  30. package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +4 -4
  31. package/tests/bug-reports/bug-report-20260728T225606Z/__snapshots__/bug-report-20260728T225606Z.snap.svg +2 -2
  32. package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +2 -2
  33. package/tests/bug-reports/bug-report-20260731T052229Z/__snapshots__/bug-report-20260731T052229Z.snap.svg +2 -2
  34. package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +2 -2
  35. package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +2 -2
  36. package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +2 -2
  37. package/tests/bug-reports/bug-report-20260805T061316Z/__snapshots__/bug-report-20260805T061316Z.snap.svg +2 -2
  38. package/tests/bug-reports/bug-report-20260806T061548Z/__snapshots__/bug-report-20260806T061548Z.snap.svg +2 -2
  39. package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +2 -2
  40. package/tests/bug-reports/bug-report-20260811T075142Z/__snapshots__/bug-report-20260811T075142Z.snap.svg +2 -2
  41. package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +2 -2
  42. package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +2 -2
  43. package/tests/bug-reports/bug-report-20260825T045913Z/__snapshots__/bug-report-20260825T045913Z.snap.svg +2 -2
  44. package/tests/bug-reports/bug-report-20260825T103621Z/__snapshots__/bug-report-20260825T103621Z.snap.svg +2 -2
  45. package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +851 -0
  46. package/tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.json +2452 -0
  47. package/tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.test.ts +12 -0
  48. package/tests/examples/__snapshots__/example01.snap.svg +2 -2
  49. package/tests/examples/__snapshots__/example02.snap.svg +12 -12
  50. package/tests/examples/__snapshots__/example03.snap.svg +2 -2
  51. package/tests/examples/__snapshots__/example04.snap.svg +2 -2
  52. package/tests/examples/__snapshots__/example05.snap.svg +2 -2
  53. package/tests/examples/__snapshots__/example06.snap.svg +2 -2
  54. package/tests/examples/__snapshots__/example07.snap.svg +2 -2
  55. package/tests/examples/__snapshots__/example08.snap.svg +2 -2
  56. package/tests/examples/__snapshots__/example09.snap.svg +2 -2
  57. package/tests/examples/__snapshots__/example10.snap.svg +2 -2
  58. package/tests/examples/__snapshots__/example11.snap.svg +2 -2
  59. package/tests/examples/__snapshots__/example12.snap.svg +2 -2
  60. package/tests/examples/__snapshots__/example13.snap.svg +2 -2
  61. package/tests/examples/__snapshots__/example14.snap.svg +2 -2
  62. package/tests/examples/__snapshots__/example15.snap.svg +2 -2
  63. package/tests/examples/__snapshots__/example16.snap.svg +2 -2
  64. package/tests/examples/__snapshots__/example17.snap.svg +2 -2
  65. package/tests/examples/__snapshots__/example18.snap.svg +2 -2
  66. package/tests/examples/__snapshots__/example19.snap.svg +2 -2
  67. package/tests/examples/__snapshots__/example20.snap.svg +2 -2
  68. package/tests/examples/__snapshots__/example21.snap.svg +2 -2
  69. package/tests/examples/__snapshots__/example22.snap.svg +2 -2
  70. package/tests/examples/__snapshots__/example23.snap.svg +2 -2
  71. package/tests/examples/__snapshots__/example24.snap.svg +2 -2
  72. package/tests/examples/__snapshots__/example25.snap.svg +2 -2
  73. package/tests/examples/__snapshots__/example26.snap.svg +2 -2
  74. package/tests/examples/__snapshots__/example27.snap.svg +2 -2
  75. package/tests/examples/__snapshots__/example28.snap.svg +2 -2
  76. package/tests/examples/__snapshots__/example29.snap.svg +2 -2
  77. package/tests/examples/__snapshots__/example30.snap.svg +2 -2
  78. package/tests/examples/__snapshots__/example31.snap.svg +2 -2
  79. package/tests/examples/__snapshots__/example32.snap.svg +2 -2
  80. package/tests/examples/__snapshots__/example33.snap.svg +2 -2
  81. package/tests/examples/__snapshots__/example34.snap.svg +2 -2
  82. package/tests/examples/__snapshots__/example35.snap.svg +2 -2
  83. package/tests/examples/__snapshots__/example36.snap.svg +2 -2
  84. package/tests/examples/__snapshots__/example37.snap.svg +2 -2
  85. package/tests/examples/__snapshots__/example38.snap.svg +2 -2
  86. package/tests/examples/__snapshots__/example39.snap.svg +2 -2
  87. package/tests/examples/__snapshots__/example40.snap.svg +2 -2
  88. package/tests/examples/__snapshots__/example41.snap.svg +2 -2
  89. package/tests/examples/__snapshots__/example42.snap.svg +2 -2
  90. package/tests/examples/__snapshots__/example43.snap.svg +2 -2
  91. package/tests/examples/__snapshots__/example44.snap.svg +2 -2
  92. package/tests/examples/__snapshots__/example45.snap.svg +2 -2
  93. package/tests/examples/__snapshots__/example46.snap.svg +2 -2
  94. package/tests/examples/__snapshots__/example47.snap.svg +2 -2
  95. package/tests/examples/__snapshots__/example48.snap.svg +2 -2
  96. package/tests/examples/__snapshots__/example49.snap.svg +2 -2
  97. package/tests/examples/__snapshots__/example50.snap.svg +2 -2
  98. package/tests/examples/__snapshots__/example51.snap.svg +4 -4
  99. package/tests/fixtures/matcher.ts +3 -3
  100. package/tests/fixtures/parallel-ground-rail.ts +53 -0
  101. package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +4 -4
  102. package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +12 -12
  103. package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +5 -5
  104. package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +2 -2
  105. package/tests/repros/__snapshots__/manufacturePartNumber-text-box.snap.svg +2 -2
  106. package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +2 -2
  107. package/tests/repros/__snapshots__/repro-atmega328p-fault-pullup.snap.svg +2 -2
  108. package/tests/repros/__snapshots__/repro-atmega328p-missing-gnd-netlabel.snap.svg +2 -2
  109. package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +2 -2
  110. package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +2 -2
  111. package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +2 -2
  112. package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +2 -2
  113. package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +2 -2
  114. package/tests/repros/__snapshots__/repro-core-555-power-cross-net-overlap.snap.svg +2 -2
  115. package/tests/repros/__snapshots__/repro-core-subcircuit-missing-ground.snap.svg +2 -2
  116. package/tests/repros/__snapshots__/repro-core-tb67s579ftg-inline-label-routing.snap.svg +2 -2
  117. package/tests/repros/__snapshots__/repro-dc-power-isolated-routing.snap.svg +2 -2
  118. package/tests/repros/__snapshots__/repro-example35-minimize-trace-crossing.snap.svg +2 -2
  119. package/tests/repros/__snapshots__/repro-ground-label-on-lower-rail-end.snap.svg +2 -2
  120. package/tests/repros/__snapshots__/repro-ina237-current-monitor.snap.svg +2 -2
  121. package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +2 -2
  122. package/tests/repros/__snapshots__/repro-mspm0l1306-capacitor-symmetry.snap.svg +200 -0
  123. package/tests/repros/__snapshots__/repro-netlabel-collision-687.snap.svg +2 -2
  124. package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +2 -2
  125. package/tests/repros/__snapshots__/repro-nrf52810-clock-routing.snap.svg +2 -2
  126. package/tests/repros/__snapshots__/repro-pga300-c6-trace-alignment.snap.svg +2 -2
  127. package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +288 -0
  128. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +4 -4
  129. package/tests/repros/__snapshots__/repro-power-group-rail-labels.snap.svg +2 -2
  130. package/tests/repros/__snapshots__/repro-rectifier-trace-overlap.snap.svg +2 -2
  131. package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +2 -2
  132. package/tests/repros/__snapshots__/repro-rp2040-usb-cc2-ground-overlap.snap.svg +2 -2
  133. package/tests/repros/__snapshots__/repro-rp2040-v3v3-label-placement.snap.svg +2 -2
  134. package/tests/repros/__snapshots__/repro-rp2040-zero-crystal-fallback-netlabels.snap.svg +2 -2
  135. package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +4 -4
  136. package/tests/repros/__snapshots__/repro-tida00553-j4-cell-divider.snap.svg +2 -2
  137. package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +2 -2
  138. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +2 -2
  139. package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +4 -4
  140. package/tests/repros/__snapshots__/repro-type-c-16pin-self-connection.snap.svg +2 -2
  141. package/tests/repros/__snapshots__/repro-type-c-16pin-to-resistor.snap.svg +2 -2
  142. package/tests/repros/__snapshots__/repro-type-c-vbus-led-ground-label.snap.svg +2 -2
  143. package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +2 -2
  144. package/tests/repros/__snapshots__/repro-vcc-pin1-detour.snap.svg +2 -2
  145. package/tests/repros/__snapshots__/repro129-host-custom-symbol-passives.snap.svg +2 -2
  146. package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +2 -2
  147. package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +4 -4
  148. package/tests/repros/__snapshots__/repro47-endpoint-obstacle-detour.snap.svg +2 -2
  149. package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +2 -2
  150. package/tests/repros/__snapshots__/repro5-escape-padded-text-obstacles.snap.svg +2 -2
  151. package/tests/repros/__snapshots__/repro51-overlap-junction-crossing.snap.svg +2 -2
  152. package/tests/repros/__snapshots__/repro84-stacked-gnd-label-orientation.snap.svg +2 -2
  153. package/tests/repros/__snapshots__/small-variant-resistor-facing-direction.snap.svg +2 -2
  154. package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +2 -2
  155. package/tests/repros/assets/repro-mspm0l1306-capacitor-symmetry.input.json +537 -0
  156. package/tests/repros/assets/repro-pga300-redundant-ground-traces.input.json +858 -0
  157. package/tests/repros/repro-mspm0l1306-capacitor-symmetry.test.ts +87 -0
  158. package/tests/repros/repro-pga300-redundant-ground-traces.test.ts +33 -0
  159. package/tests/solvers/InlineNetLabelSolver/__snapshots__/anchored-net-label-width.snap.svg +2 -2
  160. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-anchored-label-clearance.snap.svg +2 -2
  161. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-port-stub.snap.svg +2 -2
  162. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-two-pin-net.snap.svg +2 -2
  163. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label01.snap.svg +2 -2
  164. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label02.snap.svg +2 -2
  165. package/tests/solvers/InlineNetLabelSolver/__snapshots__/terminal-stub-trace-clearance.snap.svg +2 -2
  166. package/tests/solvers/MspConnectionPairSolver/local-ground-branches.test.ts +105 -0
  167. package/tests/solvers/SameNetJunctionAlignmentSolver/ground-rail-label.test.ts +188 -0
  168. package/tests/solvers/SameNetJunctionAlignmentSolver/parallel-load-rails.test.ts +291 -0
  169. package/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.snap.svg +2 -2
  170. package/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/SchematicTraceSingleLineSolver2_repro47-short-opposing-pins.snap.svg +2 -2
  171. package/tests/solvers/TraceCleanupSolver/__snapshots__/TraceCleanupSolver.snap.svg +2 -2
  172. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +2 -2
  173. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg +2 -2
  174. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg +2 -2
  175. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg +2 -2
@@ -1,4 +1,5 @@
1
1
  import type { Point } from "@tscircuit/math-utils"
2
+ import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections"
2
3
  import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
4
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
5
  import { isPathCollidingWithObstacles } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
@@ -10,6 +11,7 @@ import {
10
11
  getVisibleTraceLength,
11
12
  getVisibleTraceSegmentCount,
12
13
  isHorizontal,
14
+ isVertical,
13
15
  nearlyEqual,
14
16
  } from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
15
17
  import type { InputPin, InputProblem } from "lib/types/InputProblem"
@@ -37,6 +39,7 @@ const MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2
37
39
  // A shared vertical pin can anchor an existing rail when the rail is only a
38
40
  // symbol-stem correction away from the pin itself.
39
41
  const MAX_SHARED_PIN_RAIL_OFFSET = 0.05
42
+ const MIN_RETURN_STEM_LENGTH = 0.05
40
43
 
41
44
  const getSharedPin = ({
42
45
  donorTrace,
@@ -96,6 +99,7 @@ const railIsOnFacingSide = ({
96
99
  pin: InputPin
97
100
  }) => {
98
101
  if (pin._facingDirection === "y+") return railY > pin.y
102
+ if (pin._facingDirection === "y-") return railY < pin.y
99
103
  return false
100
104
  }
101
105
 
@@ -287,6 +291,33 @@ const candidateIsClear = ({
287
291
  return false
288
292
  }
289
293
 
294
+ // Moving a junction must not introduce an extra crossing on another net,
295
+ // including single-segment traces and intersections at a bend.
296
+ for (const otherTrace of otherNetTraces) {
297
+ const countIntersections = (path: Point[]) => {
298
+ const intersections = new Set<string>()
299
+ for (let i = 1; i < path.length; i++) {
300
+ for (let j = 1; j < otherTrace.tracePath.length; j++) {
301
+ const point = getSegmentIntersection(
302
+ path[i - 1]!,
303
+ path[i]!,
304
+ otherTrace.tracePath[j - 1]!,
305
+ otherTrace.tracePath[j]!,
306
+ )
307
+ if (point)
308
+ intersections.add(`${point.x.toFixed(6)},${point.y.toFixed(6)}`)
309
+ }
310
+ }
311
+ return intersections.size
312
+ }
313
+ if (
314
+ countIntersections(candidateTrace.tracePath) >
315
+ countIntersections(originalTrace.tracePath)
316
+ ) {
317
+ return false
318
+ }
319
+ }
320
+
290
321
  if (
291
322
  !attachedLabelIndexes.every((index) =>
292
323
  tracePathContainsPoint(
@@ -340,6 +371,87 @@ const candidateIsClear = ({
340
371
  )
341
372
  }
342
373
 
374
+ /** Extend the outer load's stem instead of adding a return trunk between loads. */
375
+ const getAlignedReturnBranchPath = ({
376
+ donorTrace,
377
+ branchTrace,
378
+ returnStemScale = 1,
379
+ }: {
380
+ donorTrace: SolvedTracePath
381
+ branchTrace: SolvedTracePath
382
+ returnStemScale?: number
383
+ }): Point[] | null => {
384
+ const sharedPin = getSharedPin({ donorTrace, branchTrace })
385
+ if (!sharedPin) return null
386
+ const railPin = getOtherPin({ trace: donorTrace, sharedPin })
387
+ const returnPin = getOtherPin({ trace: branchTrace, sharedPin })
388
+ if (!railPin || !returnPin) return null
389
+ const facing = sharedPin._facingDirection
390
+ if (facing !== "y+" && facing !== "y-") return null
391
+ if (
392
+ railPin._facingDirection !== facing ||
393
+ returnPin._facingDirection !== facing
394
+ )
395
+ return null
396
+ if (!nearlyEqual(sharedPin.y, railPin.y)) return null
397
+
398
+ const donorPath = simplifyPath(donorTrace.tracePath)
399
+ if (donorPath.length !== 4) return null
400
+ const rail = getLongestHorizontalSegment(donorTrace)
401
+ if (!rail || !railIsOnFacingSide({ railY: rail.start.y, pin: sharedPin }))
402
+ return null
403
+ if (
404
+ !isVertical(donorPath[0]!, donorPath[1]!) ||
405
+ !isVertical(donorPath[2]!, donorPath[3]!)
406
+ )
407
+ return null
408
+
409
+ const sharedFirst = branchTrace.pins[0].pinId === sharedPin.pinId
410
+ const path = simplifyPath(
411
+ sharedFirst ? branchTrace.tracePath : [...branchTrace.tracePath].reverse(),
412
+ )
413
+ // A return branch leaves the shared stem, detours beside the loads, and
414
+ // approaches the remote parallel pin from its facing side.
415
+ if (path.length !== 6) return null
416
+ if (
417
+ !isVertical(path[0]!, path[1]!) ||
418
+ !isHorizontal(path[1]!, path[2]!) ||
419
+ !isVertical(path[2]!, path[3]!) ||
420
+ !isHorizontal(path[3]!, path[4]!) ||
421
+ !isVertical(path[4]!, path[5]!)
422
+ )
423
+ return null
424
+ if (
425
+ !railIsOnFacingSide({ railY: path[1]!.y, pin: sharedPin }) ||
426
+ !railIsOnFacingSide({ railY: path[4]!.y, pin: returnPin })
427
+ )
428
+ return null
429
+ const direction = facing === "y+" ? 1 : -1
430
+ if (direction * (returnPin.y - rail.start.y) <= 0) return null
431
+
432
+ const minX = Math.min(sharedPin.x, railPin.x)
433
+ const maxX = Math.max(sharedPin.x, railPin.x)
434
+ if (path[2]!.x < minX || path[2]!.x > maxX) return null
435
+ if (
436
+ nearlyEqual(path[2]!.x, railPin.x) &&
437
+ nearlyEqual(path[1]!.y, rail.start.y)
438
+ )
439
+ return null
440
+
441
+ const returnRailY = returnPin.y + (path[4]!.y - returnPin.y) * returnStemScale
442
+ if (Math.abs(returnRailY - returnPin.y) < MIN_RETURN_STEM_LENGTH - 1e-6)
443
+ return null
444
+ const candidate = simplifyPath([
445
+ { x: sharedPin.x, y: sharedPin.y },
446
+ { x: sharedPin.x, y: rail.start.y },
447
+ { x: railPin.x, y: rail.start.y },
448
+ { x: railPin.x, y: returnRailY },
449
+ { x: returnPin.x, y: returnRailY },
450
+ { x: returnPin.x, y: returnPin.y },
451
+ ])
452
+ return sharedFirst ? candidate : candidate.reverse()
453
+ }
454
+
343
455
  export const alignSameNetJunctions = ({
344
456
  inputProblem,
345
457
  traces,
@@ -347,76 +459,93 @@ export const alignSameNetJunctions = ({
347
459
  }: AlignSameNetJunctionsInput) => {
348
460
  let outputTraces = [...traces]
349
461
  let outputNetLabelPlacements = [...netLabelPlacements]
350
- const alignedBranchTraceIds = new Set<string>()
351
462
  let alignedJunctionCount = 0
352
463
 
353
- // Reuse each aligned branch as the rail for the next load in the chain. An
354
- // aligned branch may already have had its donor turn, so queue it again when
355
- // its geometry changes.
356
- const donorTraceIds = traces.map((trace) => trace.mspPairId)
357
- const pendingDonorTraceIds = new Set(donorTraceIds)
358
- for (let donorIndex = 0; donorIndex < donorTraceIds.length; donorIndex++) {
359
- const donorTraceId = donorTraceIds[donorIndex]!
360
- pendingDonorTraceIds.delete(donorTraceId)
361
- const donorTrace = outputTraces.find(
362
- (trace) => trace.mspPairId === donorTraceId,
363
- )!
364
- for (const branchTrace of outputTraces) {
365
- if (alignedBranchTraceIds.has(branchTrace.mspPairId)) continue
366
- if (donorTrace.mspPairId === branchTrace.mspPairId) continue
367
- if (donorTrace.globalConnNetId !== branchTrace.globalConnNetId) continue
368
-
369
- const candidatePath = getAlignedBranchPath({ donorTrace, branchTrace })
370
- if (!candidatePath) continue
371
- const candidateTrace = { ...branchTrace, tracePath: candidatePath }
372
- const originalPair = [donorTrace, branchTrace]
373
- const candidatePair = [donorTrace, candidateTrace]
374
- const removesVisibleSegment =
375
- getVisibleTraceSegmentCount(candidatePair) <
376
- getVisibleTraceSegmentCount(originalPair)
377
- const shortensVisibleTrace =
378
- getVisibleTraceLength(candidatePair) <
379
- getVisibleTraceLength(originalPair) &&
380
- !nearlyEqual(
381
- getVisibleTraceLength(candidatePair),
382
- getVisibleTraceLength(originalPair),
383
- )
384
- if (!removesVisibleSegment && !shortensVisibleTrace) {
385
- continue
386
- }
387
- const attachedLabelIndexes = getAttachedLabelIndexes(
388
- branchTrace,
389
- outputNetLabelPlacements,
390
- )
391
- const candidateNetLabelPlacements = moveAttachedLabels({
392
- trace: branchTrace,
393
- reroutedTracePath: candidatePath,
394
- netLabelPlacements: outputNetLabelPlacements,
395
- attachedLabelIndexes,
396
- })
397
- if (
398
- !candidateIsClear({
399
- candidateTrace,
400
- originalTrace: branchTrace,
401
- traces: outputTraces,
402
- inputProblem,
403
- candidateNetLabelPlacements,
404
- attachedLabelIndexes,
405
- })
406
- ) {
407
- continue
408
- }
464
+ // First level the load rails, then attach return branches to those final
465
+ // rails. Doing this in one pass could attach a branch to a rail that moves later.
466
+ for (const alignReturnBranches of [false, true]) {
467
+ const alignedBranchTraceIds = new Set<string>()
468
+ // Reuse each aligned branch as the rail for the next load in the chain. An
469
+ // aligned branch may already have had its donor turn, so queue it again when
470
+ // its geometry changes.
471
+ const donorTraceIds = traces.map((trace) => trace.mspPairId)
472
+ const pendingDonorTraceIds = new Set(donorTraceIds)
473
+ for (let donorIndex = 0; donorIndex < donorTraceIds.length; donorIndex++) {
474
+ const donorTraceId = donorTraceIds[donorIndex]!
475
+ pendingDonorTraceIds.delete(donorTraceId)
476
+ const donorTrace = outputTraces.find(
477
+ (trace) => trace.mspPairId === donorTraceId,
478
+ )!
479
+ for (const branchTrace of outputTraces) {
480
+ if (alignedBranchTraceIds.has(branchTrace.mspPairId)) continue
481
+ if (donorTrace.mspPairId === branchTrace.mspPairId) continue
482
+ if (donorTrace.globalConnNetId !== branchTrace.globalConnNetId) continue
483
+
484
+ // Prefer the existing return stem. If extending the outer column
485
+ // meets another net, try shorter escapes without moving any pins.
486
+ const candidatePaths = alignReturnBranches
487
+ ? [1, 0.5, 0.25].map((returnStemScale) =>
488
+ getAlignedReturnBranchPath({
489
+ donorTrace,
490
+ branchTrace,
491
+ returnStemScale,
492
+ }),
493
+ )
494
+ : [getAlignedBranchPath({ donorTrace, branchTrace })]
495
+ for (const candidatePath of candidatePaths) {
496
+ if (!candidatePath) continue
497
+ const candidateTrace = { ...branchTrace, tracePath: candidatePath }
498
+ const originalPair = [donorTrace, branchTrace]
499
+ const candidatePair = [donorTrace, candidateTrace]
500
+ const removesVisibleSegment =
501
+ getVisibleTraceSegmentCount(candidatePair) <
502
+ getVisibleTraceSegmentCount(originalPair)
503
+ const shortensVisibleTrace =
504
+ getVisibleTraceLength(candidatePair) <
505
+ getVisibleTraceLength(originalPair) &&
506
+ !nearlyEqual(
507
+ getVisibleTraceLength(candidatePair),
508
+ getVisibleTraceLength(originalPair),
509
+ )
510
+ if (!removesVisibleSegment && !shortensVisibleTrace) {
511
+ continue
512
+ }
513
+ const attachedLabelIndexes = getAttachedLabelIndexes(
514
+ branchTrace,
515
+ outputNetLabelPlacements,
516
+ )
517
+ const candidateNetLabelPlacements = moveAttachedLabels({
518
+ trace: branchTrace,
519
+ reroutedTracePath: candidatePath,
520
+ netLabelPlacements: outputNetLabelPlacements,
521
+ attachedLabelIndexes,
522
+ })
523
+ if (
524
+ !candidateIsClear({
525
+ candidateTrace,
526
+ originalTrace: branchTrace,
527
+ traces: outputTraces,
528
+ inputProblem,
529
+ candidateNetLabelPlacements,
530
+ attachedLabelIndexes,
531
+ })
532
+ ) {
533
+ continue
534
+ }
409
535
 
410
- outputTraces = outputTraces.map((trace) => {
411
- if (trace.mspPairId === branchTrace.mspPairId) return candidateTrace
412
- return trace
413
- })
414
- outputNetLabelPlacements = candidateNetLabelPlacements
415
- alignedBranchTraceIds.add(branchTrace.mspPairId)
416
- alignedJunctionCount++
417
- if (!pendingDonorTraceIds.has(branchTrace.mspPairId)) {
418
- donorTraceIds.push(branchTrace.mspPairId)
419
- pendingDonorTraceIds.add(branchTrace.mspPairId)
536
+ outputTraces = outputTraces.map((trace) => {
537
+ if (trace.mspPairId === branchTrace.mspPairId) return candidateTrace
538
+ return trace
539
+ })
540
+ outputNetLabelPlacements = candidateNetLabelPlacements
541
+ alignedBranchTraceIds.add(branchTrace.mspPairId)
542
+ alignedJunctionCount++
543
+ if (!pendingDonorTraceIds.has(branchTrace.mspPairId)) {
544
+ donorTraceIds.push(branchTrace.mspPairId)
545
+ pendingDonorTraceIds.add(branchTrace.mspPairId)
546
+ }
547
+ break
548
+ }
420
549
  }
421
550
  }
422
551
  }
@@ -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
+ }
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.157",
8
+ "version": "0.0.159",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",
@@ -0,0 +1,4 @@
1
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
2
+ import inputProblem from "../../tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.json"
3
+
4
+ export default () => <PipelineDebugger inputProblem={inputProblem as any} />
@@ -1,4 +1,4 @@
1
- <svg width="2416" height="1200" viewBox="0 0 2416 1200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Solver debug visualization on the left and Circuit JSON schematic on the right">
1
+ <svg width="1200" height="2016" viewBox="0 0 1200 2016" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Solver debug visualization on top and Circuit JSON schematic on the bottom">
2
2
  <g transform="translate(0, 0) scale(1.875, 1.875) translate(0, 0)">
3
3
  <rect width="100%" height="100%" fill="white"/><g><polyline data-points="17.565,0 17.765,0 17.765,2 17.565,2" data-type="line" data-label="" points="594.9036857272865,290.95950250265435 598.301228575762,290.95950250265435 598.301228575762,256.9840740178978 594.9036857272865,256.9840740178978" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="17.565,-2 17.765,-2 17.765,0 17.565,0" data-type="line" data-label="" points="594.9036857272865,324.9349309874109 598.301228575762,324.9349309874109 598.301228575762,290.95950250265435 594.9036857272865,290.95950250265435" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="17.565,-4 17.765,-4 17.765,-2 17.565,-2" data-type="line" data-label="" points="594.9036857272865,358.91035947216744 598.301228575762,358.91035947216744 598.301228575762,324.9349309874109 594.9036857272865,324.9349309874109" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-4.949999999999999,9 -4.5249999999999995,9 -4.5249999999999995,2.3 -4.1,2.3" data-type="line" data-label="" points="212.4252995601395,138.07007432124985 219.64507811315028,138.07007432124985 219.64507811315028,251.88775974518433 226.86485666616105,251.88775974518433" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-7.85,1.7 -5.975,1.7 -5.975,2.1 -4.1,2.1" data-type="line" data-label="" points="163.1609282572425,262.08038829061127 195.01289246170177,262.08038829061127 195.01289246170177,255.28530259365996 226.86485666616105,255.28530259365996" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="10.15,2.1 10.450000000000001,2.1 10.450000000000001,5.55 8.75,5.55 8.75,9 8.95,9" data-type="line" data-label="" points="468.9397846200515,255.28530259365996 474.03609889276504,255.28530259365996 474.03609889276504,196.6776884574549 445.1569846807219,196.6776884574549 445.1569846807219,138.07007432124985 448.55452752919757,138.07007432124985" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-7.9,-4.3 -6,-4.3 -6,-4.1 -4.1,-4.1" data-type="line" data-label="" points="162.3115425451236,364.0066737448809 194.5881996056423,364.0066737448809 194.5881996056423,360.6091308964053 226.86485666616105,360.6091308964053" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="15.05,9 15.7425,9 15.7425,-4 16.435,-4" data-type="line" data-label="" points="552.1795844077051,138.07007432124985 563.943576520552,138.07007432124985 563.943576520552,358.91035947216744 575.707568633399,358.91035947216744" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="1.15,-9.5 1.3499999999999999,-9.5 1.3499999999999999,-10.75 0.25,-10.75 0.25,-12 0.44999999999999996,-12" data-type="line" data-label="" points="316.050356438647,452.342787805248 319.44789928712265,452.342787805248 319.44789928712265,473.5774306082208 300.7614136205066,473.5774306082208 300.7614136205066,494.8120734111937 304.1589564689822,494.8120734111937" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="4.15,2.3 4.3500000000000005,2.3 4.3500000000000005,2.7 10.25,2.7 10.25,2.3 10.15,2.3" data-type="line" data-label="" points="367.0134991657818,251.88775974518433 370.4110420142575,251.88775974518433 370.4110420142575,245.092674048233 470.6385560442893,245.092674048233 470.6385560442893,251.88775974518433 468.9397846200515,251.88775974518433" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-7.9,-3.7 -7.7,-3.7 -7.7,-3.3 -1.7,-3.3 -1.7,-3.7 -1.9,-3.7" data-type="line" data-label="" points="162.3115425451236,353.814045199454 165.70908539359922,353.814045199454 165.70908539359922,347.01895950250264 267.6353708478689,347.01895950250264 267.6353708478689,353.814045199454 264.23782799939323,353.814045199454" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-11.55,-12 -11.75,-12 -11.75,-12.819999999999999 -8.75,-12.819999999999999 -8.75,-12 -8.55,-12" data-type="line" data-label="" points="100.30638556044286,494.8120734111937 96.90884271196722,494.8120734111937 96.90884271196722,508.7419990899439 147.87198543910205,508.7419990899439 147.87198543910205,494.8120734111937 151.26952828757769,494.8120734111937" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-5.55,-12 -5.75,-12 -5.75,-12.819999999999999 -2.7499999999999982,-12.819999999999999 -2.7499999999999982,-12 -2.549999999999999,-12" data-type="line" data-label="" points="202.23267101471254,494.8120734111937 198.83512816623687,494.8120734111937 198.83512816623687,508.7419990899439 249.79827089337175,508.7419990899439 249.79827089337175,494.8120734111937 253.1958137418474,494.8120734111937" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="3.45,-12 3.25,-12 3.25,-12.819999999999999 6.25,-12.819999999999999 6.25,-12 6.45,-12" data-type="line" data-label="" points="355.12209919611706,494.8120734111937 351.7245563476414,494.8120734111937 351.7245563476414,508.7419990899439 402.6876990747762,508.7419990899439 402.6876990747762,494.8120734111937 406.0852419232519,494.8120734111937" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="1.15,-9.3 1.75,-9.3 1.75,-12 1.55,-12" data-type="line" data-label="" points="316.050356438647,448.94524495677234 326.242984984074,448.94524495677234 326.242984984074,494.8120734111937 322.8454421355983,494.8120734111937" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="7.55,-12 7.75,-12 7.75,-12.819999999999999 10.75,-12.819999999999999 10.75,-12 10.55,-12" data-type="line" data-label="" points="424.77172758986796,494.8120734111937 428.16927043834363,494.8120734111937 428.16927043834363,508.7419990899439 479.13241316547845,508.7419990899439 479.13241316547845,494.8120734111937 475.7348703170028,494.8120734111937" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-12.9,4.3 -12.700000000000001,4.3 -12.700000000000001,6.65 -13.75,6.65 -13.75,9 -13.55,9" data-type="line" data-label="" points="77.37297133323219,217.91233126042775 80.77051418170782,217.91233126042775 80.77051418170782,177.9912027908388 62.93341422721065,177.9912027908388 62.93341422721065,138.07007432124985 66.33095707568629,138.07007432124985" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-12.45,9 -11.600999999999999,9 -11.600999999999999,2.3 -10.15,2.3" data-type="line" data-label="" points="85.01744274230242,138.07007432124985 99.4400121340816,138.07007432124985 99.4400121340816,251.88775974518433 124.08918549977244,251.88775974518433" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="1.8499999999999996,2.3 -1.319,2.3 -1.319,9 -2.45,9" data-type="line" data-label="" points="327.9417564083118,251.88775974518433 274.107689974215,251.88775974518433 274.107689974215,138.07007432124985 254.8945851660852,138.07007432124985" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.9000000000000021,1.7 -1.7000000000000028,1.7 -1.7000000000000028,-0.9 -10.650999999999998,-0.9 -10.650999999999998,-3.9 -10.1,-3.9" data-type="line" data-label="" points="264.23782799939323,262.08038829061127 267.63537084786884,262.08038829061127 267.63537084786884,306.2484453207948 115.57834066434097,306.2484453207948 115.57834066434097,357.21158804792964 124.93857121189137,357.21158804792964" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="4.15,1.9 4.701,1.9 4.701,-0.9000000000000001 1.6499999999999995,-0.9000000000000001 1.6499999999999995,-3.7 1.8499999999999996,-3.7" data-type="line" data-label="" points="367.0134991657818,258.6828454421356 376.37372971333224,258.6828454421356 376.37372971333224,306.2484453207948 324.5442135598361,306.2484453207948 324.5442135598361,353.814045199454 327.9417564083118,353.814045199454" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="10.15,1.7 15.800999999999998,1.7 15.800999999999998,4.3 15.15,4.3" data-type="line" data-label="" points="468.9397846200515,262.08038829061127 564.9373578037312,262.08038829061127 564.9373578037312,217.91233126042775 553.8783558319428,217.91233126042775" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="10.15,-4.1 10.700999999999999,-4.1000000000000005 10.700999999999999,-4.8 -1.049,-4.8 -1.049,-3.8000000000000003 -1.3489999999999998,-3.8000000000000003 -1.3489999999999998,-4.1 -1.9,-4.1" data-type="line" data-label="" points="468.9397846200515,360.6091308964053 478.30001516760194,360.6091308964053 478.30001516760194,372.5005308660701 278.6943728196572,372.5005308660701 278.6943728196572,355.51281662369183 273.59805854694366,355.51281662369183 273.59805854694366,360.6091308964053 264.23782799939323,360.6091308964053" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="7.85,-3.9 7.299,-3.9 7.299,0.6000000000000001 15.900999999999998,0.6000000000000001 15.900999999999998,4.7 15.15,4.7" data-type="line" data-label="" points="429.86804186258144,357.21158804792964 420.50781131503106,357.21158804792964 420.50781131503106,280.7668739572274 566.6361292279689,280.7668739572274 566.6361292279689,211.11724556347644 553.8783558319428,211.11724556347644" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-10.15,1.7 -10.750999999999998,1.7 -10.750999999999998,-4.3 -10.1,-4.3" data-type="line" data-label="" points="124.08918549977244,262.08038829061127 113.87956924010314,262.08038829061127 113.87956924010314,364.0066737448809 124.93857121189137,364.0066737448809" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-12.9,4.7 -12.8,4.7 -12.8,4.751 -12.499,4.751" data-type="line" data-label="" points="77.37297133323219,211.11724556347644 79.07174275747002,211.11724556347644 79.07174275747002,210.25087213711515 84.18504474442588,210.25087213711515" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="15.15,3.3 16.151,3.3 16.151,3.3509999999999995" data-type="line" data-label="" points="553.8783558319428,234.90004550280602 570.8830577885635,234.90004550280602 570.8830577885635,234.03367207644476" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-7.85,2.3 -7.749,2.3 -7.749,2.501" data-type="line" data-label="" points="163.1609282572425,251.88775974518433 164.8766873957227,251.88775974518433 164.8766873957227,248.47322918246627" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.9,2.3 -1.799,2.3 -1.799,2.501" data-type="line" data-label="" points="264.23782799939323,251.88775974518433 265.95358713787346,251.88775974518433 265.95358713787346,248.47322918246627" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="4.15,-3.7 4.251,-3.7 4.251,-3.499" data-type="line" data-label="" points="367.0134991657818,353.814045199454 368.72925830426203,353.814045199454 368.72925830426203,350.39951463673594" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="10.15,-3.7 10.251,-3.7 10.251,-3.499" data-type="line" data-label="" points="468.9397846200515,353.814045199454 470.65554375853173,353.814045199454 470.65554375853173,350.39951463673594" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-3.55,9 -3.651,9 -3.651,9.401" data-type="line" data-label="" points="236.20809949946909,138.07007432124985 234.4923403609889,138.07007432124985 234.4923403609889,131.25800091005615" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.05,9 -1.151,9 -1.151,9.401" data-type="line" data-label="" points="278.67738510541477,138.07007432124985 276.96162596693455,138.07007432124985 276.96162596693455,131.25800091005615" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="9.45,-12 9.349,-12 9.349,-11.349" data-type="line" data-label="" points="457.04838465038665,494.8120734111937 455.3326255119065,494.8120734111937 455.3326255119065,483.7530714394054" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-12.9,4.5 -12.8,4.5 -12.8,4.449 -12.349000000000002,4.449" data-type="line" data-label="" points="77.37297133323219,214.5147884119521 79.07174275747002,214.5147884119521 79.07174275747002,215.3811618383134 86.73320188078259,215.3811618383134" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="15.15,3.5 15.251,3.5 15.251,3.099" data-type="line" data-label="" points="553.8783558319428,231.50250265433039 555.5941149704231,231.50250265433039 555.5941149704231,238.31457606552405" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-4.1,1.7 -4.201,1.7 -4.201,1.499" data-type="line" data-label="" points="226.86485666616105,262.08038829061127 225.14909752768085,262.08038829061127 225.14909752768085,265.4949188533293" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="1.8499999999999996,1.7 1.7489999999999997,1.7 1.7489999999999997,1.499" data-type="line" data-label="" points="327.9417564083118,262.08038829061127 326.22599726983157,262.08038829061127 326.22599726983157,265.4949188533293" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="7.85,1.7 7.749,1.7 7.749,1.499" data-type="line" data-label="" points="429.86804186258144,262.08038829061127 428.15228272410127,262.08038829061127 428.15228272410127,265.4949188533293" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-4.1,-4.3 -4.201,-4.3 -4.201,-4.501" data-type="line" data-label="" points="226.86485666616105,364.0066737448809 225.14909752768085,364.0066737448809 225.14909752768085,367.421204307599" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="1.8499999999999996,-4.3 -1.150999999999998,-4.3 -1.150999999999998,-4.901" data-type="line" data-label="" points="327.9417564083118,364.0066737448809 276.9616259669346,364.0066737448809 276.9616259669346,374.2162900045503" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-10.45,-12 -10.299,-12 -10.299,-12.051" data-type="line" data-label="" points="118.992871227059,494.8120734111937 121.5580160776581,494.8120734111937 121.5580160776581,495.678446837555" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-7.449999999999999,-12 -7.348999999999999,-12 -7.348999999999999,-12.651" data-type="line" data-label="" points="169.95601395419382,494.8120734111937 171.67177309267402,494.8120734111937 171.67177309267402,505.8710753829819" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-4.45,-12 -4.299,-12 -4.299,-12.051" data-type="line" data-label="" points="220.91915668132862,494.8120734111937 223.48430153192777,494.8120734111937 223.48430153192777,495.678446837555" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="-1.45,-12 -1.349,-12 -1.349,-12.651" data-type="line" data-label="" points="271.8822994084635,494.8120734111937 273.59805854694366,494.8120734111937 273.59805854694366,505.8710753829819" fill="none" stroke="purple" stroke-width="1px"/></g><g><polyline data-points="4.55,-12 4.701,-12 4.701,-12.051" data-type="line" data-label="" points="373.80858486273314,494.8120734111937 376.37372971333224,494.8120734111937 376.37372971333224,495.678446837555" fill="none" stroke="purple" stroke-width="1px"/></g><g><rect data-type="rect" data-label="schematic_component_0" data-x="-14" data-y="4" x="39.999999999999986" y="207.71970271500078" width="37.37297133323219" height="30.57788563628091" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_1" data-x="14" data-y="4" x="514.8066130744728" y="207.71970271500078" width="39.07174275747002" height="30.57788563628091" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_2" data-x="0" data-y="-9" x="276.97861368117697" y="431.957530714394" width="39.07174275747002" height="23.78279993932955" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_3" data-x="-9" data-y="2" x="124.08918549977246" y="248.49021689670866" width="39.07174275747005" height="16.987714242378274" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_4" data-x="-3" data-y="2" x="226.86485666616107" y="248.49021689670866" width="37.37297133323219" height="16.987714242378274" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_5" data-x="3" data-y="2" x="327.9417564083118" y="248.49021689670866" width="39.07174275747002" height="16.987714242378274" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_6" data-x="9" data-y="2" x="429.86804186258144" y="248.49021689670866" width="39.071742757470076" height="16.987714242378274" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_7" data-x="-9" data-y="-4" x="124.93857121189137" y="350.4165023509783" width="37.372971333232215" height="16.987714242378274" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_8" data-x="-3" data-y="-4" x="226.86485666616107" y="350.4165023509783" width="37.37297133323219" height="16.987714242378274" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_9" data-x="3" data-y="-4" x="327.9417564083118" y="350.4165023509783" width="39.07174275747002" height="16.987714242378274" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_10" data-x="9" data-y="-4" x="429.86804186258144" y="350.4165023509783" width="39.071742757470076" height="16.987714242378274" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_11" data-x="-13" data-y="9" x="66.33095707568629" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_12" data-x="-10.5" data-y="9" x="108.800242681632" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_13" data-x="-8" data-y="9" x="151.26952828757769" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_14" data-x="-5.5" data-y="9" x="193.7388138935234" y="131.45411812528445" width="18.686485666616107" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_15" data-x="-3" data-y="9" x="236.20809949946909" y="131.45411812528445" width="18.686485666616107" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_16" data-x="-0.5" data-y="9" x="278.67738510541477" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_17" data-x="2" data-y="9" x="321.14667071136046" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_18" data-x="4.5" data-y="9" x="363.61595631730614" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_19" data-x="7" data-y="9" x="406.08524192325183" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_20" data-x="9.5" data-y="9" x="448.55452752919757" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_21" data-x="12" data-y="9" x="491.0238131351433" y="131.45411812528445" width="18.68648566661608" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_22" data-x="14.5" data-y="9" x="533.4930987410889" y="131.45411812528445" width="18.686485666616136" height="13.231912391930791" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_23" data-x="-11" data-y="-12" x="100.30638556044286" y="484.2796905809191" width="18.686485666616136" height="21.064765660549085" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_24" data-x="-8" data-y="-12" x="151.26952828757769" y="484.2796905809191" width="18.686485666616136" height="21.064765660549085" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_25" data-x="-5" data-y="-12" x="202.23267101471257" y="484.2796905809191" width="18.68648566661608" height="21.064765660549085" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_26" data-x="-2" data-y="-12" x="253.19581374184736" y="484.2796905809191" width="18.686485666616136" height="21.064765660549085" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_27" data-x="1" data-y="-12" x="304.1589564689822" y="484.2796905809191" width="18.686485666616136" height="21.064765660549085" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_28" data-x="4" data-y="-12" x="355.122099196117" y="484.2796905809191" width="18.68648566661608" height="21.064765660549085" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_29" data-x="7" data-y="-12" x="406.08524192325183" y="484.2796905809191" width="18.68648566661608" height="21.064765660549085" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_30" data-x="10" data-y="-12" x="457.04838465038665" y="484.2796905809191" width="18.686485666616136" height="21.064765660549085" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_31" data-x="17" data-y="2" x="575.707568633399" y="246.9613226148946" width="19.19611709388755" height="20.04550280600637" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_32" data-x="17" data-y="0" x="575.707568633399" y="280.93675109965113" width="19.19611709388755" height="20.045502806006425" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_33" data-x="17" data-y="-2" x="575.707568633399" y="314.9121795844077" width="19.19611709388755" height="20.04550280600637" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="schematic_component_34" data-x="17" data-y="-4" x="575.707568633399" y="348.88760806916423" width="19.19611709388755" height="20.04550280600637" fill="hsl(320, 100%, 50%, 0.8)" stroke="black" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="netId: VIN
4
4
  globalConnNetId: connectivity_net36" data-x="-12.499" data-y="4.9910000000000005" x="82.48627332018806" y="202.09676930077353" width="3.397542848475638" height="8.154102836341593" fill="#ef444466" stroke="#ef4444" stroke-width="0.058866071428571434"/></g><g><rect data-type="rect" data-label="netId: VIN
@@ -387,7 +387,7 @@ orientation: y+" data-x="15.39625" data-y="9" cx="558.0615804641286" cy="138.070
387
387
  });
388
388
  ]]></script>
389
389
  </g>
390
- <g transform="translate(1216, 200)">
390
+ <g transform="translate(0, 1216)">
391
391
  <style>
392
392
  .boundary { fill: rgb(245, 241, 237); }
393
393
  .schematic-boundary { fill: none; stroke: #fff; }