@tscircuit/schematic-trace-solver 0.0.183 → 0.0.185

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 (109) hide show
  1. package/dist/index.js +242 -24
  2. package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +30 -12
  3. package/lib/solvers/SameNetJunctionAlignmentSolver/collapseRedundantSharedEndpointStubs.ts +267 -0
  4. package/lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts +1 -1
  5. package/lib/solvers/SameNetJunctionAlignmentSolver/getSharedPin.ts +13 -0
  6. package/lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts +12 -4
  7. package/package.json +1 -1
  8. package/site/bug-reports/bug-report-20260905T041712Z.page.tsx +4 -0
  9. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +2 -2
  10. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +2 -2
  11. package/tests/bug-reports/bug-report-20260707T020342Z/__snapshots__/bug-report-20260707T020342Z.snap.svg +2 -2
  12. package/tests/bug-reports/bug-report-20260707T020342Z/bug-report-20260707T020342Z.test.ts +10 -0
  13. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +524 -533
  14. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +2 -2
  15. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +2 -2
  16. package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +2 -2
  17. package/tests/bug-reports/bug-report-20260707T141421Z/__snapshots__/bug-report-20260707T141421Z.snap.svg +2 -2
  18. package/tests/bug-reports/bug-report-20260707T230831Z/__snapshots__/bug-report-20260707T230831Z.snap.svg +2 -2
  19. package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +2 -2
  20. package/tests/bug-reports/bug-report-20260716T144856Z/__snapshots__/bug-report-20260716T144856Z.snap.svg +2 -2
  21. package/tests/bug-reports/bug-report-20260717T022934Z/__snapshots__/bug-report-20260717T022934Z.snap.svg +2 -2
  22. package/tests/bug-reports/bug-report-20260717T042845Z/__snapshots__/bug-report-20260717T042845Z.snap.svg +2 -2
  23. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
  24. package/tests/bug-reports/bug-report-20260724T175257Z/__snapshots__/bug-report-20260724T175257Z.snap.svg +2 -2
  25. package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +2 -2
  26. package/tests/bug-reports/bug-report-20260728T144234Z/bug-report-20260728T144234Z.test.ts +2 -0
  27. package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +2 -2
  28. package/tests/bug-reports/bug-report-20260730T061837Z/bug-report-20260730T061837Z.test.ts +1 -1
  29. package/tests/bug-reports/bug-report-20260731T052229Z/__snapshots__/bug-report-20260731T052229Z.snap.svg +2 -2
  30. package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +2 -2
  31. package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +2 -2
  32. package/tests/bug-reports/bug-report-20260805T061316Z/__snapshots__/bug-report-20260805T061316Z.snap.svg +2 -2
  33. package/tests/bug-reports/bug-report-20260806T061548Z/__snapshots__/bug-report-20260806T061548Z.snap.svg +2 -2
  34. package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +2 -2
  35. package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +2 -2
  36. package/tests/bug-reports/bug-report-20260825T045913Z/__snapshots__/bug-report-20260825T045913Z.snap.svg +2 -2
  37. package/tests/bug-reports/bug-report-20260825T103621Z/__snapshots__/bug-report-20260825T103621Z.snap.svg +2 -2
  38. package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +2 -2
  39. package/tests/bug-reports/bug-report-20260901T055358Z/__snapshots__/bug-report-20260901T055358Z.snap.svg +2 -2
  40. package/tests/bug-reports/bug-report-20260901T064117Z/__snapshots__/bug-report-20260901T064117Z.snap.svg +2 -2
  41. package/tests/bug-reports/bug-report-20260901T134241Z/__snapshots__/bug-report-20260901T134241Z.snap.svg +2 -2
  42. package/tests/bug-reports/bug-report-20260902T092425Z/__snapshots__/bug-report-20260902T092425Z.snap.svg +2 -2
  43. package/tests/bug-reports/bug-report-20260905T041712Z/__snapshots__/bug-report-20260905T041712Z.snap.svg +546 -0
  44. package/tests/bug-reports/bug-report-20260905T041712Z/bug-report-20260905T041712Z.json +2023 -0
  45. package/tests/bug-reports/bug-report-20260905T041712Z/bug-report-20260905T041712Z.test.ts +33 -0
  46. package/tests/examples/__snapshots__/example02.snap.svg +2 -2
  47. package/tests/examples/__snapshots__/example13.snap.svg +2 -2
  48. package/tests/examples/__snapshots__/example14.snap.svg +2 -2
  49. package/tests/examples/__snapshots__/example15.snap.svg +2 -2
  50. package/tests/examples/__snapshots__/example18.snap.svg +2 -2
  51. package/tests/examples/__snapshots__/example19.snap.svg +2 -2
  52. package/tests/examples/__snapshots__/example21.snap.svg +2 -2
  53. package/tests/examples/__snapshots__/example27.snap.svg +2 -2
  54. package/tests/examples/__snapshots__/example29.snap.svg +2 -2
  55. package/tests/examples/__snapshots__/example42.snap.svg +2 -2
  56. package/tests/examples/__snapshots__/example45.snap.svg +2 -2
  57. package/tests/examples/__snapshots__/example46.snap.svg +2 -2
  58. package/tests/examples/__snapshots__/example47.snap.svg +2 -2
  59. package/tests/examples/__snapshots__/example51.snap.svg +2 -2
  60. package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +2 -2
  61. package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +2 -2
  62. package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +2 -2
  63. package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +2 -2
  64. package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +2 -2
  65. package/tests/repros/__snapshots__/repro-battery-management-bq24073.snap.svg +2 -2
  66. package/tests/repros/__snapshots__/repro-bc107a-common-emitter-routing.snap.svg +2 -2
  67. package/tests/repros/__snapshots__/repro-bluetooth-controller-ground-decoupling-groups.snap.svg +2 -2
  68. package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +2 -2
  69. package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +2 -2
  70. package/tests/repros/__snapshots__/repro-board-8090-power-section.snap.svg +2 -2
  71. package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +2 -2
  72. package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +2 -2
  73. package/tests/repros/__snapshots__/repro-bq40z60-current-sense.snap.svg +2 -2
  74. package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +2 -2
  75. package/tests/repros/__snapshots__/repro-core-ground-inline-label-fallback.snap.svg +2 -2
  76. package/tests/repros/__snapshots__/repro-esp12f-section.snap.svg +508 -0
  77. package/tests/repros/__snapshots__/repro-example35-minimize-trace-crossing.snap.svg +2 -2
  78. package/tests/repros/__snapshots__/repro-hdc2080-ground-label-alignment.snap.svg +2 -2
  79. package/tests/repros/__snapshots__/repro-hdc3020-ground-label-alignment.snap.svg +2 -2
  80. package/tests/repros/__snapshots__/repro-hub-usb-sheet.snap.svg +2 -2
  81. package/tests/repros/__snapshots__/repro-ina237-current-monitor.snap.svg +2 -2
  82. package/tests/repros/__snapshots__/repro-isolated-rs485-isow7841.snap.svg +2 -2
  83. package/tests/repros/__snapshots__/repro-mspm0l1306-capacitor-symmetry.snap.svg +2 -2
  84. package/tests/repros/__snapshots__/repro-pga300-c6-trace-alignment.snap.svg +2 -2
  85. package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +2 -2
  86. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +18 -18
  87. package/tests/repros/__snapshots__/repro-power-group-rail-labels.snap.svg +2 -2
  88. package/tests/repros/__snapshots__/repro-powerbank-3v-system-power.snap.svg +2 -2
  89. package/tests/repros/__snapshots__/repro-rectifier-trace-overlap.snap.svg +2 -2
  90. package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +2 -2
  91. package/tests/repros/__snapshots__/repro-rp2040-usb-cc2-ground-overlap.snap.svg +2 -2
  92. package/tests/repros/__snapshots__/repro-smartwatch-power-sheet.snap.svg +2 -2
  93. package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +2 -2
  94. package/tests/repros/__snapshots__/repro-tida-01389-hbridge.snap.svg +2 -2
  95. package/tests/repros/__snapshots__/repro-tida00553-j4-cell-divider.snap.svg +2 -2
  96. package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +2 -2
  97. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +2 -2
  98. package/tests/repros/__snapshots__/repro-tmp1075-ground-label-overlap.snap.svg +2 -2
  99. package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +2 -2
  100. package/tests/repros/__snapshots__/repro-trellis-core-gnd-net-label-overlap.snap.svg +2 -2
  101. package/tests/repros/__snapshots__/repro-type-c-vbus-led-ground-label.snap.svg +2 -2
  102. package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +2 -2
  103. package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +2 -2
  104. package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +2 -2
  105. package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +2 -2
  106. package/tests/repros/assets/repro-esp12f-status-section.input.json +1394 -0
  107. package/tests/repros/repro-esp12f-section.test.ts +12 -0
  108. package/tests/solvers/SameNetJunctionAlignmentSolver/collapse-redundant-shared-endpoint-stubs.test.ts +101 -0
  109. package/tests/solvers/UnroutedTraceRecoverySolver/local-elbow-obstacles.test.ts +74 -0
package/dist/index.js CHANGED
@@ -12659,6 +12659,7 @@ ${c.status}`
12659
12659
 
12660
12660
  // lib/solvers/UnroutedTraceRecoverySolver/UnroutedTraceRecoverySolver.ts
12661
12661
  import { distance as distance6, doSegmentsIntersect as doSegmentsIntersect3 } from "@tscircuit/math-utils";
12662
+ import { calculateElbow as calculateElbow3 } from "calculate-elbow";
12662
12663
  var ROUTE_CLEARANCE = 0.2;
12663
12664
  var COORDINATE_TOLERANCE = 1e-9;
12664
12665
  var GROUND_NET_ID = "GND";
@@ -12711,7 +12712,7 @@ var getOuterBounds = (obstacles) => {
12711
12712
  maxY: Math.max(...obstacles.map((obstacle) => obstacle.maxY))
12712
12713
  };
12713
12714
  };
12714
- var getPerimeterCandidates = ({
12715
+ var getPinConnectionCandidates = ({
12715
12716
  connectionPair,
12716
12717
  obstacles
12717
12718
  }) => {
@@ -12733,7 +12734,13 @@ var getPerimeterCandidates = ({
12733
12734
  outerBounds.minX - ROUTE_CLEARANCE,
12734
12735
  outerBounds.maxX + ROUTE_CLEARANCE
12735
12736
  ];
12736
- const candidates = [];
12737
+ const candidates = [
12738
+ calculateElbow3(
12739
+ { ...firstPin, facingDirection: firstPin._facingDirection },
12740
+ { ...secondPin, facingDirection: secondPin._facingDirection },
12741
+ { overshoot: ROUTE_CLEARANCE }
12742
+ )
12743
+ ];
12737
12744
  for (const channelY of horizontalChannels) {
12738
12745
  candidates.push(
12739
12746
  removeConsecutiveDuplicatePoints([
@@ -13053,11 +13060,11 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
13053
13060
  obstacles,
13054
13061
  maxConnectionDistance: this.maxConnectionDistance
13055
13062
  });
13056
- const perimeterCandidates = getPerimeterCandidates({
13063
+ const pinConnectionCandidates = getPinConnectionCandidates({
13057
13064
  connectionPair,
13058
13065
  obstacles
13059
13066
  });
13060
- const candidates = [...junctionCandidates, ...perimeterCandidates];
13067
+ const candidates = [...junctionCandidates, ...pinConnectionCandidates];
13061
13068
  const rejectComponentBoundaryTravel = hasParallelFailedConnection({
13062
13069
  connectionPair,
13063
13070
  failedConnectionPairs: this.failedConnectionPairs
@@ -13230,24 +13237,228 @@ var findNearestSharedPinExitRail = ({
13230
13237
  return nearestRail;
13231
13238
  };
13232
13239
 
13240
+ // lib/solvers/SameNetJunctionAlignmentSolver/getSharedPin.ts
13241
+ var getSharedPin = ({
13242
+ donorTrace,
13243
+ branchTrace
13244
+ }) => {
13245
+ const branchPinIds = new Set(branchTrace.pins.map((pin) => pin.pinId));
13246
+ return donorTrace.pins.find((pin) => branchPinIds.has(pin.pinId)) ?? null;
13247
+ };
13248
+
13249
+ // lib/solvers/SameNetJunctionAlignmentSolver/collapseRedundantSharedEndpointStubs.ts
13250
+ var getSegmentAxis3 = (start, end) => {
13251
+ if (nearlyEqual(start.x, end.x)) return "y";
13252
+ if (nearlyEqual(start.y, end.y)) return "x";
13253
+ return null;
13254
+ };
13255
+ var getPathFromSharedPin = ({
13256
+ trace,
13257
+ sharedPinId
13258
+ }) => {
13259
+ const tracePath = simplifyPath(trace.tracePath);
13260
+ if (trace.pins[0]?.pinId === sharedPinId) return tracePath;
13261
+ return tracePath.reverse();
13262
+ };
13263
+ var traceCanLoseSharedPrefix = ({
13264
+ trace,
13265
+ sharedPrefix,
13266
+ netLabelPlacements
13267
+ }) => {
13268
+ return !netLabelPlacements.some(
13269
+ (label) => label.mspConnectionPairIds.includes(trace.mspPairId) && tracePathContainsPoint(sharedPrefix, label.anchorPoint)
13270
+ );
13271
+ };
13272
+ var getTraceWithoutSharedPrefix = ({
13273
+ trace,
13274
+ pathFromSharedPin,
13275
+ sharedPinId,
13276
+ branchPoint
13277
+ }) => {
13278
+ const branchSegmentIndex = pathFromSharedPin.findIndex((point, index) => {
13279
+ const nextPoint = pathFromSharedPin[index + 1];
13280
+ if (!nextPoint) return false;
13281
+ return tracePathContainsPoint([point, nextPoint], branchPoint);
13282
+ });
13283
+ if (branchSegmentIndex < 0) return null;
13284
+ const pathFromJunction = simplifyPath([
13285
+ branchPoint,
13286
+ ...pathFromSharedPin.slice(branchSegmentIndex + 1)
13287
+ ]);
13288
+ let tracePath = pathFromJunction;
13289
+ if (trace.pins[0]?.pinId !== sharedPinId) {
13290
+ tracePath = pathFromJunction.reverse();
13291
+ }
13292
+ return { ...trace, tracePath };
13293
+ };
13294
+ var collapseRedundantSharedEndpointStubs = ({
13295
+ traces,
13296
+ netLabelPlacements,
13297
+ netLabelConnectorTraceIds,
13298
+ multiPinNetPinIds
13299
+ }) => {
13300
+ const outputTraces = [...traces];
13301
+ for (let firstIndex = 0; firstIndex < outputTraces.length; firstIndex++) {
13302
+ const firstTrace = outputTraces[firstIndex];
13303
+ if (netLabelConnectorTraceIds.has(firstTrace.mspPairId)) continue;
13304
+ for (let secondIndex = firstIndex + 1; secondIndex < outputTraces.length; secondIndex++) {
13305
+ const secondTrace = outputTraces[secondIndex];
13306
+ if (netLabelConnectorTraceIds.has(secondTrace.mspPairId) || firstTrace.globalConnNetId !== secondTrace.globalConnNetId) {
13307
+ continue;
13308
+ }
13309
+ const sharedPin = getSharedPin({
13310
+ donorTrace: firstTrace,
13311
+ branchTrace: secondTrace
13312
+ });
13313
+ if (!sharedPin || !multiPinNetPinIds.has(sharedPin.pinId)) continue;
13314
+ const firstPath = getPathFromSharedPin({
13315
+ trace: firstTrace,
13316
+ sharedPinId: sharedPin.pinId
13317
+ });
13318
+ const secondPath = getPathFromSharedPin({
13319
+ trace: secondTrace,
13320
+ sharedPinId: sharedPin.pinId
13321
+ });
13322
+ if (firstPath.length < 2 || secondPath.length < 2) continue;
13323
+ const departureAxis = getSegmentAxis3(firstPath[0], firstPath[1]);
13324
+ const secondDepartureAxis = getSegmentAxis3(secondPath[0], secondPath[1]);
13325
+ if (!departureAxis || departureAxis !== secondDepartureAxis) continue;
13326
+ const firstDepartureDirection = firstPath[1][departureAxis] - firstPath[0][departureAxis];
13327
+ const secondDepartureDirection = secondPath[1][departureAxis] - secondPath[0][departureAxis];
13328
+ if (!pointsEqual2(firstPath[1], secondPath[1]) && Math.sign(firstDepartureDirection) === Math.sign(secondDepartureDirection)) {
13329
+ let traceIndexToCollapse2 = secondIndex;
13330
+ let traceToCollapse2 = secondTrace;
13331
+ let pathToCollapse2 = secondPath;
13332
+ if (Math.abs(firstDepartureDirection) < Math.abs(secondDepartureDirection)) {
13333
+ traceIndexToCollapse2 = firstIndex;
13334
+ traceToCollapse2 = firstTrace;
13335
+ pathToCollapse2 = firstPath;
13336
+ }
13337
+ const branchPoint2 = pathToCollapse2[1];
13338
+ if (!traceCanLoseSharedPrefix({
13339
+ trace: traceToCollapse2,
13340
+ sharedPrefix: [pathToCollapse2[0], branchPoint2],
13341
+ netLabelPlacements
13342
+ })) {
13343
+ if (traceIndexToCollapse2 === firstIndex) {
13344
+ traceIndexToCollapse2 = secondIndex;
13345
+ traceToCollapse2 = secondTrace;
13346
+ pathToCollapse2 = secondPath;
13347
+ } else {
13348
+ traceIndexToCollapse2 = firstIndex;
13349
+ traceToCollapse2 = firstTrace;
13350
+ pathToCollapse2 = firstPath;
13351
+ }
13352
+ }
13353
+ if (!traceCanLoseSharedPrefix({
13354
+ trace: traceToCollapse2,
13355
+ sharedPrefix: [pathToCollapse2[0], branchPoint2],
13356
+ netLabelPlacements
13357
+ })) {
13358
+ continue;
13359
+ }
13360
+ const collapsedTrace2 = getTraceWithoutSharedPrefix({
13361
+ trace: traceToCollapse2,
13362
+ pathFromSharedPin: pathToCollapse2,
13363
+ sharedPinId: sharedPin.pinId,
13364
+ branchPoint: branchPoint2
13365
+ });
13366
+ if (!collapsedTrace2) continue;
13367
+ outputTraces[traceIndexToCollapse2] = collapsedTrace2;
13368
+ break;
13369
+ }
13370
+ if (firstPath.length < 3 || secondPath.length < 3) continue;
13371
+ if (!pointsEqual2(firstPath[1], secondPath[1])) continue;
13372
+ const railAxis = getSegmentAxis3(firstPath[1], firstPath[2]);
13373
+ const secondRailAxis = getSegmentAxis3(secondPath[1], secondPath[2]);
13374
+ if (!railAxis || railAxis !== secondRailAxis || railAxis === departureAxis) {
13375
+ continue;
13376
+ }
13377
+ const firstRailDirection = firstPath[2][railAxis] - firstPath[1][railAxis];
13378
+ const secondRailDirection = secondPath[2][railAxis] - secondPath[1][railAxis];
13379
+ const railsHaveSameDirection = Math.sign(firstRailDirection) === Math.sign(secondRailDirection);
13380
+ const sharedStub = firstPath.slice(0, 2);
13381
+ if (!railsHaveSameDirection && !nearlyEqual(
13382
+ Math.abs(firstRailDirection),
13383
+ Math.abs(secondRailDirection)
13384
+ ) && netLabelPlacements.some(
13385
+ (label) => label.globalConnNetId === firstTrace.globalConnNetId && tracePathContainsPoint(sharedStub, label.anchorPoint)
13386
+ )) {
13387
+ continue;
13388
+ }
13389
+ let traceIndexToCollapse = secondIndex;
13390
+ let traceToCollapse = secondTrace;
13391
+ let pathToCollapse = secondPath;
13392
+ let branchPoint = firstPath[1];
13393
+ if (railsHaveSameDirection && Math.abs(firstRailDirection) < Math.abs(secondRailDirection)) {
13394
+ traceIndexToCollapse = firstIndex;
13395
+ traceToCollapse = firstTrace;
13396
+ pathToCollapse = firstPath;
13397
+ branchPoint = firstPath[2];
13398
+ } else if (railsHaveSameDirection) {
13399
+ branchPoint = secondPath[2];
13400
+ }
13401
+ const sharedPrefix = [pathToCollapse[0], pathToCollapse[1], branchPoint];
13402
+ if (!traceCanLoseSharedPrefix({
13403
+ trace: traceToCollapse,
13404
+ sharedPrefix,
13405
+ netLabelPlacements
13406
+ })) {
13407
+ if (traceIndexToCollapse === firstIndex) {
13408
+ traceIndexToCollapse = secondIndex;
13409
+ traceToCollapse = secondTrace;
13410
+ pathToCollapse = secondPath;
13411
+ } else {
13412
+ traceIndexToCollapse = firstIndex;
13413
+ traceToCollapse = firstTrace;
13414
+ pathToCollapse = firstPath;
13415
+ }
13416
+ }
13417
+ if (!traceCanLoseSharedPrefix({
13418
+ trace: traceToCollapse,
13419
+ sharedPrefix: [pathToCollapse[0], pathToCollapse[1], branchPoint],
13420
+ netLabelPlacements
13421
+ })) {
13422
+ continue;
13423
+ }
13424
+ const collapsedTrace = getTraceWithoutSharedPrefix({
13425
+ trace: traceToCollapse,
13426
+ pathFromSharedPin: pathToCollapse,
13427
+ sharedPinId: sharedPin.pinId,
13428
+ branchPoint
13429
+ });
13430
+ if (!collapsedTrace) continue;
13431
+ outputTraces[traceIndexToCollapse] = collapsedTrace;
13432
+ break;
13433
+ }
13434
+ }
13435
+ return outputTraces;
13436
+ };
13437
+
13233
13438
  // lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
13234
13439
  var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
13235
13440
  var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
13236
13441
  var MAX_SHARED_PIN_RAIL_OFFSET = 0.05;
13237
13442
  var MAX_SHARED_ENDPOINT_RAIL_OFFSET = 0.8;
13238
13443
  var MIN_RETURN_STEM_LENGTH = 0.05;
13239
- var getSegmentAxis3 = (start, end) => {
13444
+ var getMultiPinNetPinIds = (inputProblem) => {
13445
+ const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
13446
+ const inputPinIds = new Set(
13447
+ inputProblem.chips.flatMap((chip) => chip.pins.map((pin) => pin.pinId))
13448
+ );
13449
+ return new Set(
13450
+ [...inputPinIds].filter((pinId) => {
13451
+ const globalNetId = netConnMap.getNetConnectedToId(pinId);
13452
+ if (!globalNetId) return false;
13453
+ return netConnMap.getIdsConnectedToNet(globalNetId).filter((connectedId) => inputPinIds.has(connectedId)).length > 2;
13454
+ })
13455
+ );
13456
+ };
13457
+ var getSegmentAxis4 = (start, end) => {
13240
13458
  if (isHorizontal2(start, end)) return "x";
13241
13459
  if (isVertical2(start, end)) return "y";
13242
13460
  return null;
13243
13461
  };
13244
- var getSharedPin = ({
13245
- donorTrace,
13246
- branchTrace
13247
- }) => {
13248
- const branchPinIds = new Set(branchTrace.pins.map((pin) => pin.pinId));
13249
- return donorTrace.pins.find((pin) => branchPinIds.has(pin.pinId)) ?? null;
13250
- };
13251
13462
  var getOtherPin = ({
13252
13463
  trace,
13253
13464
  sharedPin
@@ -13473,8 +13684,8 @@ var getAlignedSharedEndpointRailPath = ({
13473
13684
  const donorPath = pathFromShared(donorTrace);
13474
13685
  const branchPath = pathFromShared(branchTrace);
13475
13686
  if (donorPath.length < 3 || branchPath.length < 4) return null;
13476
- const donorDepartureAxis = getSegmentAxis3(donorPath[0], donorPath[1]);
13477
- const branchDepartureAxis = getSegmentAxis3(branchPath[0], branchPath[1]);
13687
+ const donorDepartureAxis = getSegmentAxis4(donorPath[0], donorPath[1]);
13688
+ const branchDepartureAxis = getSegmentAxis4(branchPath[0], branchPath[1]);
13478
13689
  if (!donorDepartureAxis || donorDepartureAxis !== branchDepartureAxis) {
13479
13690
  return null;
13480
13691
  }
@@ -13482,8 +13693,8 @@ var getAlignedSharedEndpointRailPath = ({
13482
13693
  const branchDeparture = branchPath[1][branchDepartureAxis] - branchPath[0][branchDepartureAxis];
13483
13694
  if (Math.sign(donorDeparture) !== Math.sign(branchDeparture)) return null;
13484
13695
  let railIndex = 1;
13485
- let donorRailAxis = getSegmentAxis3(donorPath[1], donorPath[2]);
13486
- let branchRailAxis = getSegmentAxis3(branchPath[1], branchPath[2]);
13696
+ let donorRailAxis = getSegmentAxis4(donorPath[1], donorPath[2]);
13697
+ let branchRailAxis = getSegmentAxis4(branchPath[1], branchPath[2]);
13487
13698
  if (!donorRailAxis || donorRailAxis !== branchRailAxis || donorRailAxis === donorDepartureAxis) {
13488
13699
  return null;
13489
13700
  }
@@ -13491,11 +13702,11 @@ var getAlignedSharedEndpointRailPath = ({
13491
13702
  if (firstRailsAreAligned) {
13492
13703
  railIndex++;
13493
13704
  if (!donorPath[railIndex + 2] || !branchPath[railIndex + 2]) return null;
13494
- donorRailAxis = getSegmentAxis3(
13705
+ donorRailAxis = getSegmentAxis4(
13495
13706
  donorPath[railIndex],
13496
13707
  donorPath[railIndex + 1]
13497
13708
  );
13498
- branchRailAxis = getSegmentAxis3(
13709
+ branchRailAxis = getSegmentAxis4(
13499
13710
  branchPath[railIndex],
13500
13711
  branchPath[railIndex + 1]
13501
13712
  );
@@ -13503,7 +13714,7 @@ var getAlignedSharedEndpointRailPath = ({
13503
13714
  }
13504
13715
  let branchParallelRailCount = 0;
13505
13716
  for (let index = 1; index < branchPath.length - 2; index++) {
13506
- if (getSegmentAxis3(branchPath[index], branchPath[index + 1]) === branchRailAxis) {
13717
+ if (getSegmentAxis4(branchPath[index], branchPath[index + 1]) === branchRailAxis) {
13507
13718
  branchParallelRailCount++;
13508
13719
  }
13509
13720
  }
@@ -13545,13 +13756,13 @@ var getAlignedAttachedLabelConnectorPath = ({
13545
13756
  const junction = connectorPath[0];
13546
13757
  const turn = connectorPath[1];
13547
13758
  const railEnd = connectorPath[2];
13548
- const departureAxis = getSegmentAxis3(junction, turn);
13549
- const railAxis = getSegmentAxis3(turn, railEnd);
13759
+ const departureAxis = getSegmentAxis4(junction, turn);
13760
+ const railAxis = getSegmentAxis4(turn, railEnd);
13550
13761
  if (!departureAxis || !railAxis || departureAxis === railAxis) return null;
13551
13762
  const donorSegmentIndex = donorTrace.tracePath.findIndex(
13552
13763
  (point, index, path) => {
13553
13764
  const next = path[index + 1];
13554
- return next && getSegmentAxis3(point, next) === departureAxis && tracePathContainsPoint([point, next], junction) && tracePathContainsPoint([point, next], turn);
13765
+ return next && getSegmentAxis4(point, next) === departureAxis && tracePathContainsPoint([point, next], junction) && tracePathContainsPoint([point, next], turn);
13555
13766
  }
13556
13767
  );
13557
13768
  if (donorSegmentIndex < 0) return null;
@@ -13564,7 +13775,7 @@ var getAlignedAttachedLabelConnectorPath = ({
13564
13775
  donorSegmentIndex > 0 ? [donorTrace.tracePath[donorSegmentIndex - 1], donorSegmentStart] : null,
13565
13776
  donorSegmentIndex + 2 < donorTrace.tracePath.length ? [donorSegmentEnd, donorTrace.tracePath[donorSegmentIndex + 2]] : null
13566
13777
  ].flatMap(
13567
- (segment) => segment && getSegmentAxis3(segment[0], segment[1]) === railAxis ? [segment] : []
13778
+ (segment) => segment && getSegmentAxis4(segment[0], segment[1]) === railAxis ? [segment] : []
13568
13779
  );
13569
13780
  if (adjacentDonorRails.length === 0) return null;
13570
13781
  const railCoordinateAxis = railAxis === "x" ? "y" : "x";
@@ -13647,7 +13858,7 @@ var preserveAttachedLabelConnectorJunctions = ({
13647
13858
  if (!movedJunction) return null;
13648
13859
  const neighborIndex = endpointIndex === 0 ? 1 : endpointIndex - 1;
13649
13860
  const neighbor = connector.tracePath[neighborIndex];
13650
- if (!neighbor || getSegmentAxis3(originalJunction, neighbor) !== getSegmentAxis3(movedJunction, neighbor)) {
13861
+ if (!neighbor || getSegmentAxis4(originalJunction, neighbor) !== getSegmentAxis4(movedJunction, neighbor)) {
13651
13862
  return null;
13652
13863
  }
13653
13864
  const movedConnector = {
@@ -14070,6 +14281,13 @@ var alignSameNetJunctions = ({
14070
14281
  }
14071
14282
  }
14072
14283
  }
14284
+ const collapsedSharedEndpointStubs = collapseRedundantSharedEndpointStubs({
14285
+ traces: outputTraces,
14286
+ netLabelPlacements: outputNetLabelPlacements,
14287
+ netLabelConnectorTraceIds,
14288
+ multiPinNetPinIds: getMultiPinNetPinIds(inputProblem)
14289
+ });
14290
+ outputTraces = collapsedSharedEndpointStubs;
14073
14291
  return {
14074
14292
  traces: outputTraces,
14075
14293
  netLabelPlacements: outputNetLabelPlacements,
@@ -2,6 +2,7 @@ import type { Point } from "@tscircuit/math-utils"
2
2
  import { getSegmentIntersection } from "@tscircuit/math-utils/line-intersections"
3
3
  import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
4
4
  import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
5
+ import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
5
6
  import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
6
7
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
7
8
  import { isPathCollidingWithObstacles } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
@@ -20,7 +21,7 @@ import {
20
21
  isVertical,
21
22
  nearlyEqual,
22
23
  } from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
23
- import type { InputPin, InputProblem } from "lib/types/InputProblem"
24
+ import type { InputPin, InputProblem, PinId } from "lib/types/InputProblem"
24
25
  import { doesPathCoincideWithTraces } from "lib/utils/doesPathCoincideWithTraces"
25
26
  import {
26
27
  pathEntersAnyNetLabel,
@@ -31,6 +32,8 @@ import {
31
32
  getPinFacingAxis,
32
33
  isCoordinateOnPinFacingSide,
33
34
  } from "./findNearestSharedPinExitRail"
35
+ import { collapseRedundantSharedEndpointStubs } from "./collapseRedundantSharedEndpointStubs"
36
+ import { getSharedPin } from "./getSharedPin"
34
37
 
35
38
  interface AlignSameNetJunctionsInput {
36
39
  inputProblem: InputProblem
@@ -57,23 +60,30 @@ const MAX_SHARED_PIN_RAIL_OFFSET = 0.05
57
60
  const MAX_SHARED_ENDPOINT_RAIL_OFFSET = 0.8
58
61
  const MIN_RETURN_STEM_LENGTH = 0.05
59
62
 
63
+ const getMultiPinNetPinIds = (inputProblem: InputProblem) => {
64
+ const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem)
65
+ const inputPinIds = new Set<PinId>(
66
+ inputProblem.chips.flatMap((chip) => chip.pins.map((pin) => pin.pinId)),
67
+ )
68
+ return new Set(
69
+ [...inputPinIds].filter((pinId) => {
70
+ const globalNetId = netConnMap.getNetConnectedToId(pinId)
71
+ if (!globalNetId) return false
72
+ return (
73
+ netConnMap
74
+ .getIdsConnectedToNet(globalNetId)
75
+ .filter((connectedId) => inputPinIds.has(connectedId)).length > 2
76
+ )
77
+ }),
78
+ )
79
+ }
80
+
60
81
  const getSegmentAxis = (start: Point, end: Point) => {
61
82
  if (isHorizontal(start, end)) return "x" as const
62
83
  if (isVertical(start, end)) return "y" as const
63
84
  return null
64
85
  }
65
86
 
66
- export const getSharedPin = ({
67
- donorTrace,
68
- branchTrace,
69
- }: {
70
- donorTrace: SolvedTracePath
71
- branchTrace: SolvedTracePath
72
- }): (InputPin & { chipId: string }) | null => {
73
- const branchPinIds = new Set(branchTrace.pins.map((pin) => pin.pinId))
74
- return donorTrace.pins.find((pin) => branchPinIds.has(pin.pinId)) ?? null
75
- }
76
-
77
87
  const getOtherPin = ({
78
88
  trace,
79
89
  sharedPin,
@@ -1301,6 +1311,14 @@ export const alignSameNetJunctions = ({
1301
1311
  }
1302
1312
  }
1303
1313
 
1314
+ const collapsedSharedEndpointStubs = collapseRedundantSharedEndpointStubs({
1315
+ traces: outputTraces,
1316
+ netLabelPlacements: outputNetLabelPlacements,
1317
+ netLabelConnectorTraceIds,
1318
+ multiPinNetPinIds: getMultiPinNetPinIds(inputProblem),
1319
+ })
1320
+ outputTraces = collapsedSharedEndpointStubs
1321
+
1304
1322
  return {
1305
1323
  traces: outputTraces,
1306
1324
  netLabelPlacements: outputNetLabelPlacements,