@tscircuit/schematic-trace-solver 0.0.182 → 0.0.184

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 (112) hide show
  1. package/dist/index.d.ts +5 -0
  2. package/dist/index.js +542 -132
  3. package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +30 -12
  4. package/lib/solvers/SameNetJunctionAlignmentSolver/collapseRedundantSharedEndpointStubs.ts +267 -0
  5. package/lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts +1 -1
  6. package/lib/solvers/SameNetJunctionAlignmentSolver/getSharedPin.ts +13 -0
  7. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts +45 -2
  8. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection.ts +58 -10
  9. package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +166 -57
  10. package/lib/solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts +108 -3
  11. package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +62 -15
  12. package/lib/solvers/TraceCleanupSolver/turnMinimization.ts +5 -0
  13. package/package.json +1 -1
  14. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +2 -2
  15. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +13 -20
  16. package/tests/bug-reports/bug-report-20260707T020342Z/__snapshots__/bug-report-20260707T020342Z.snap.svg +2 -2
  17. package/tests/bug-reports/bug-report-20260707T020342Z/bug-report-20260707T020342Z.test.ts +10 -0
  18. package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +2 -2
  19. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +2 -2
  20. package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +2 -2
  21. package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +2 -2
  22. package/tests/bug-reports/bug-report-20260707T141421Z/__snapshots__/bug-report-20260707T141421Z.snap.svg +2 -2
  23. package/tests/bug-reports/bug-report-20260707T230831Z/__snapshots__/bug-report-20260707T230831Z.snap.svg +2 -2
  24. package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +2 -2
  25. package/tests/bug-reports/bug-report-20260716T144856Z/__snapshots__/bug-report-20260716T144856Z.snap.svg +2 -2
  26. package/tests/bug-reports/bug-report-20260717T022934Z/__snapshots__/bug-report-20260717T022934Z.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 +2 -2
  31. package/tests/bug-reports/bug-report-20260728T144234Z/bug-report-20260728T144234Z.test.ts +2 -0
  32. package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +2 -2
  33. package/tests/bug-reports/bug-report-20260730T061837Z/bug-report-20260730T061837Z.test.ts +1 -1
  34. package/tests/bug-reports/bug-report-20260731T052229Z/__snapshots__/bug-report-20260731T052229Z.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-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +2 -2
  40. package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +2 -2
  41. package/tests/bug-reports/bug-report-20260825T045913Z/__snapshots__/bug-report-20260825T045913Z.snap.svg +2 -2
  42. package/tests/bug-reports/bug-report-20260825T103621Z/__snapshots__/bug-report-20260825T103621Z.snap.svg +2 -2
  43. package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +2 -2
  44. package/tests/bug-reports/bug-report-20260901T055358Z/__snapshots__/bug-report-20260901T055358Z.snap.svg +2 -2
  45. package/tests/bug-reports/bug-report-20260901T064117Z/__snapshots__/bug-report-20260901T064117Z.snap.svg +2 -2
  46. package/tests/bug-reports/bug-report-20260901T134241Z/__snapshots__/bug-report-20260901T134241Z.snap.svg +2 -2
  47. package/tests/bug-reports/bug-report-20260902T092425Z/__snapshots__/bug-report-20260902T092425Z.snap.svg +2 -2
  48. package/tests/examples/__snapshots__/example02.snap.svg +2 -2
  49. package/tests/examples/__snapshots__/example13.snap.svg +2 -2
  50. package/tests/examples/__snapshots__/example14.snap.svg +2 -2
  51. package/tests/examples/__snapshots__/example15.snap.svg +2 -2
  52. package/tests/examples/__snapshots__/example18.snap.svg +2 -2
  53. package/tests/examples/__snapshots__/example19.snap.svg +2 -2
  54. package/tests/examples/__snapshots__/example21.snap.svg +2 -2
  55. package/tests/examples/__snapshots__/example27.snap.svg +2 -2
  56. package/tests/examples/__snapshots__/example29.snap.svg +2 -2
  57. package/tests/examples/__snapshots__/example42.snap.svg +2 -2
  58. package/tests/examples/__snapshots__/example45.snap.svg +2 -2
  59. package/tests/examples/__snapshots__/example46.snap.svg +2 -2
  60. package/tests/examples/__snapshots__/example47.snap.svg +2 -2
  61. package/tests/examples/__snapshots__/example51.snap.svg +2 -2
  62. package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +2 -2
  63. package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +2 -2
  64. package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +13 -13
  65. package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +2 -2
  66. package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +2 -2
  67. package/tests/repros/__snapshots__/repro-battery-management-bq24073.snap.svg +2 -2
  68. package/tests/repros/__snapshots__/repro-bc107a-common-emitter-routing.snap.svg +2 -2
  69. package/tests/repros/__snapshots__/repro-bluetooth-controller-ground-decoupling-groups.snap.svg +2 -2
  70. package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +2 -2
  71. package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +2 -2
  72. package/tests/repros/__snapshots__/repro-board-8090-power-section.snap.svg +2 -2
  73. package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +2 -2
  74. package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +2 -2
  75. package/tests/repros/__snapshots__/repro-bq40z60-current-sense.snap.svg +2 -2
  76. package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +2 -2
  77. package/tests/repros/__snapshots__/repro-core-ground-inline-label-fallback.snap.svg +2 -2
  78. package/tests/repros/__snapshots__/repro-esp12f-section.snap.svg +508 -0
  79. package/tests/repros/__snapshots__/repro-example35-minimize-trace-crossing.snap.svg +2 -2
  80. package/tests/repros/__snapshots__/repro-hdc2080-ground-label-alignment.snap.svg +2 -2
  81. package/tests/repros/__snapshots__/repro-hdc3020-ground-label-alignment.snap.svg +2 -2
  82. package/tests/repros/__snapshots__/repro-hub-usb-sheet.snap.svg +2 -2
  83. package/tests/repros/__snapshots__/repro-ina237-current-monitor.snap.svg +2 -2
  84. package/tests/repros/__snapshots__/repro-isolated-rs485-isow7841.snap.svg +2 -2
  85. package/tests/repros/__snapshots__/repro-mouse-switch-ground-unnecessary-jog.snap.svg +2 -2
  86. package/tests/repros/__snapshots__/repro-mspm0l1306-capacitor-symmetry.snap.svg +2 -2
  87. package/tests/repros/__snapshots__/repro-pga300-c6-trace-alignment.snap.svg +2 -2
  88. package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +2 -2
  89. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +26 -26
  90. package/tests/repros/__snapshots__/repro-power-group-rail-labels.snap.svg +2 -2
  91. package/tests/repros/__snapshots__/repro-powerbank-3v-system-power.snap.svg +2 -2
  92. package/tests/repros/__snapshots__/repro-rectifier-trace-overlap.snap.svg +2 -2
  93. package/tests/repros/__snapshots__/repro-rf1-unnecessary-dogleg.snap.svg +5 -5
  94. package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +2 -2
  95. package/tests/repros/__snapshots__/repro-rp2040-usb-cc2-ground-overlap.snap.svg +2 -2
  96. package/tests/repros/__snapshots__/repro-smartwatch-power-sheet.snap.svg +2 -2
  97. package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +2 -2
  98. package/tests/repros/__snapshots__/repro-tida-01389-hbridge.snap.svg +2 -2
  99. package/tests/repros/__snapshots__/repro-tida00553-j4-cell-divider.snap.svg +2 -2
  100. package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +2 -2
  101. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +2 -2
  102. package/tests/repros/__snapshots__/repro-tmp1075-ground-label-overlap.snap.svg +2 -2
  103. package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +2 -2
  104. package/tests/repros/__snapshots__/repro-trellis-core-gnd-net-label-overlap.snap.svg +2 -2
  105. package/tests/repros/__snapshots__/repro-type-c-vbus-led-ground-label.snap.svg +2 -2
  106. package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +2 -2
  107. package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +2 -2
  108. package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +2 -2
  109. package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +2 -2
  110. package/tests/repros/assets/repro-esp12f-status-section.input.json +1394 -0
  111. package/tests/repros/repro-esp12f-section.test.ts +12 -0
  112. package/tests/solvers/SameNetJunctionAlignmentSolver/collapse-redundant-shared-endpoint-stubs.test.ts +101 -0
package/dist/index.js CHANGED
@@ -111,7 +111,7 @@ var arePinsInDifferentSchematicSections = (inputProblem, p1, p2) => {
111
111
  };
112
112
 
113
113
  // lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection.ts
114
- var getPinDirection = (pin, chip) => {
114
+ var getPinDirectionCandidates = (pin, chip, connectedPin) => {
115
115
  const { x, y } = pin;
116
116
  const { center, width, height } = chip;
117
117
  const yPlusEdge = center.y + height / 2;
@@ -128,17 +128,34 @@ var getPinDirection = (pin, chip) => {
128
128
  xPlusDistance,
129
129
  xMinusDistance
130
130
  );
131
- if (minDistance === yPlusDistance) {
132
- return "y+";
133
- }
134
- if (minDistance === yMinusDistance) {
135
- return "y-";
131
+ const primaryDirection = minDistance === yPlusDistance ? "y+" : minDistance === yMinusDistance ? "y-" : minDistance === xPlusDistance ? "x+" : "x-";
132
+ const matchingDirections = [
133
+ { direction: "y+", distance: yPlusDistance },
134
+ { direction: "y-", distance: yMinusDistance },
135
+ { direction: "x+", distance: xPlusDistance },
136
+ { direction: "x-", distance: xMinusDistance }
137
+ ].filter(({ distance: distance7 }) => Math.abs(distance7 - minDistance) <= 1e-9).map(({ direction }) => direction);
138
+ const closestDirections = [
139
+ primaryDirection,
140
+ ...matchingDirections.filter((direction) => direction !== primaryDirection)
141
+ ];
142
+ if (!connectedPin || closestDirections.length <= 1) {
143
+ return closestDirections;
136
144
  }
137
- if (minDistance === xPlusDistance) {
138
- return "x+";
145
+ const xDistance = connectedPin.x - pin.x;
146
+ const yDistance = connectedPin.y - pin.y;
147
+ const directionTowardConnectedPin = Math.abs(xDistance) >= Math.abs(yDistance) ? xDistance >= 0 ? "x+" : "x-" : yDistance >= 0 ? "y+" : "y-";
148
+ if (!closestDirections.includes(directionTowardConnectedPin)) {
149
+ return closestDirections;
139
150
  }
140
- return "x-";
151
+ return [
152
+ directionTowardConnectedPin,
153
+ ...closestDirections.filter(
154
+ (direction) => direction !== directionTowardConnectedPin
155
+ )
156
+ ];
141
157
  };
158
+ var getPinDirection = (pin, chip, connectedPin) => getPinDirectionCandidates(pin, chip, connectedPin)[0];
142
159
 
143
160
  // lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts
144
161
  var visualizeInputProblem = (inputProblem, opts = {}) => {
@@ -1353,6 +1370,38 @@ var pinsFaceEachOther = ({
1353
1370
  }
1354
1371
  return yDistance > 0 ? pin1._facingDirection === "y+" && pin2._facingDirection === "y-" : pin1._facingDirection === "y-" && pin2._facingDirection === "y+";
1355
1372
  };
1373
+ var getPathLength = (points) => {
1374
+ let length = 0;
1375
+ for (let i = 0; i < points.length - 1; i++) {
1376
+ length += Math.abs(points[i + 1].x - points[i].x) + Math.abs(points[i + 1].y - points[i].y);
1377
+ }
1378
+ return length;
1379
+ };
1380
+ var getInitialPathForPins = ({
1381
+ pins,
1382
+ obstacles
1383
+ }) => {
1384
+ const [pin1, pin2] = pins;
1385
+ const directShortPath = calculateDirectShortPath(pin1, pin2);
1386
+ const defaultElbow = calculateElbowForPins({
1387
+ pin1,
1388
+ pin2,
1389
+ overshoot: 0.2
1390
+ });
1391
+ const routingDistance = Math.abs(pin1.x - pin2.x) + Math.abs(pin1.y - pin2.y);
1392
+ const adaptiveElbow = calculateElbowForPins({
1393
+ pin1,
1394
+ pin2,
1395
+ overshoot: Math.min(0.2, Math.max(0.02, routingDistance / 4))
1396
+ });
1397
+ const adaptiveElbowIsShorter = getPathLength(adaptiveElbow) < getPathLength(defaultElbow);
1398
+ const defaultElbowBacktracks = getPathLength(defaultElbow) > routingDistance + 1e-9;
1399
+ const shouldUseAdaptiveElbow = findFirstCollision(adaptiveElbow, obstacles) === null && (pinsFaceEachOther({ pin1, pin2 }) && defaultElbowBacktracks && adaptiveElbowIsShorter || findFirstCollision(defaultElbow, obstacles) !== null);
1400
+ return {
1401
+ path: directShortPath ?? (shouldUseAdaptiveElbow ? adaptiveElbow : defaultElbow),
1402
+ isDirectShortPath: directShortPath !== null
1403
+ };
1404
+ };
1356
1405
  var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1357
1406
  pins;
1358
1407
  connectionPair;
@@ -1364,9 +1413,12 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1364
1413
  aabb;
1365
1414
  baseElbow;
1366
1415
  preferExteriorDetours;
1416
+ reserveNetLabelClearance;
1367
1417
  solvedTracePath = null;
1368
1418
  queue = [];
1369
1419
  visited = /* @__PURE__ */ new Set();
1420
+ inferredPinIndexes = /* @__PURE__ */ new Set();
1421
+ hasAmbiguousPinDirections = false;
1370
1422
  constructor(params) {
1371
1423
  super();
1372
1424
  this.pins = params.pins;
@@ -1374,14 +1426,9 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1374
1426
  this.inputProblem = params.inputProblem;
1375
1427
  this.chipMap = params.chipMap;
1376
1428
  this.preferExteriorDetours = params.preferExteriorDetours ?? true;
1377
- for (const pin of this.pins) {
1378
- if (!pin._facingDirection) {
1379
- const chip = this.chipMap[pin.chipId];
1380
- pin._facingDirection = getPinDirection(pin, chip);
1381
- }
1382
- }
1429
+ this.reserveNetLabelClearance = params.reserveNetLabelClearance ?? true;
1383
1430
  this.obstacles = getObstacleRects(this.inputProblem, {
1384
- textBoxPadding: this.getTextBoxPaddingForConnectionPair()
1431
+ textBoxPadding: this.reserveNetLabelClearance ? this.getTextBoxPaddingForConnectionPair() : void 0
1385
1432
  });
1386
1433
  this.textObstacles = new Set(this.obstacles.filter(isTextBoxObstacle));
1387
1434
  const endpointChipIds = new Set(this.pins.map((pin) => pin.chipId));
@@ -1390,36 +1437,68 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1390
1437
  (obstacle) => obstacle.textBox.chipId !== void 0 && endpointChipIds.has(obstacle.textBox.chipId)
1391
1438
  )
1392
1439
  );
1393
- const [pin1, pin2] = this.pins;
1394
- const directShortPath = calculateDirectShortPath(pin1, pin2);
1395
- const defaultElbow = calculateElbowForPins({
1396
- pin1,
1397
- pin2,
1398
- overshoot: 0.2
1399
- });
1400
- const routingDistance = Math.abs(pin1.x - pin2.x) + Math.abs(pin1.y - pin2.y);
1401
- const adaptiveElbow = calculateElbowForPins({
1402
- pin1,
1403
- pin2,
1404
- overshoot: Math.min(0.2, Math.max(0.02, routingDistance / 4))
1440
+ const directionOptions = this.pins.map((pin, pinIndex) => {
1441
+ if (pin._facingDirection) return [pin._facingDirection];
1442
+ this.inferredPinIndexes.add(pinIndex);
1443
+ const connectedPin = this.pins[pinIndex === 0 ? 1 : 0];
1444
+ return getPinDirectionCandidates(
1445
+ pin,
1446
+ this.chipMap[pin.chipId],
1447
+ connectedPin
1448
+ );
1405
1449
  });
1406
- const adaptiveElbowIsShorter = this.pathLength(adaptiveElbow) < this.pathLength(defaultElbow);
1407
- const defaultElbowBacktracks = this.pathLength(defaultElbow) > routingDistance + 1e-9;
1408
- const shouldUseAdaptiveElbow = findFirstCollision(adaptiveElbow, this.obstacles) === null && (pinsFaceEachOther({ pin1, pin2 }) && defaultElbowBacktracks && adaptiveElbowIsShorter || findFirstCollision(defaultElbow, this.obstacles) !== null);
1409
- this.baseElbow = defaultElbow;
1410
- if (shouldUseAdaptiveElbow) {
1411
- this.baseElbow = adaptiveElbow;
1450
+ const directionPairs = directionOptions[0].flatMap(
1451
+ (firstDirection, firstIndex) => directionOptions[1].map((secondDirection, secondIndex) => ({
1452
+ directions: [firstDirection, secondDirection],
1453
+ preferenceIndex: firstIndex + secondIndex
1454
+ }))
1455
+ );
1456
+ this.hasAmbiguousPinDirections = directionOptions.some(
1457
+ (directions) => directions.length > 1
1458
+ );
1459
+ const rankedDirectionPairs = directionPairs.map(({ directions, preferenceIndex }) => {
1460
+ const candidatePins = this.pins.map((pin, index) => ({
1461
+ ...pin,
1462
+ _facingDirection: directions[index]
1463
+ }));
1464
+ const initialPath = getInitialPathForPins({
1465
+ pins: candidatePins,
1466
+ obstacles: this.obstacles
1467
+ });
1468
+ return {
1469
+ directions,
1470
+ baseElbow: initialPath.path,
1471
+ isDirectShortPath: initialPath.isDirectShortPath,
1472
+ preferenceIndex,
1473
+ collisionCount: findFirstCollision(initialPath.path, this.obstacles) === null ? 0 : 1,
1474
+ pathLength: getPathLength(initialPath.path)
1475
+ };
1476
+ }).sort(
1477
+ (first, second) => first.collisionCount - second.collisionCount || first.pathLength - second.pathLength || first.preferenceIndex - second.preferenceIndex
1478
+ );
1479
+ const preferredCandidate = rankedDirectionPairs[0];
1480
+ for (const [pinIndex, pin] of this.pins.entries()) {
1481
+ pin._facingDirection = preferredCandidate.directions[pinIndex];
1412
1482
  }
1413
- if (directShortPath) {
1414
- this.baseElbow = directShortPath;
1483
+ const [pin1, pin2] = this.pins;
1484
+ this.baseElbow = preferredCandidate.baseElbow;
1485
+ if (preferredCandidate.isDirectShortPath) {
1486
+ this.solvedTracePath = preferredCandidate.baseElbow;
1415
1487
  }
1416
- this.solvedTracePath = directShortPath;
1417
1488
  this.aabb = aabbFromPoints(
1418
1489
  { x: pin1.x, y: pin1.y },
1419
1490
  { x: pin2.x, y: pin2.y }
1420
1491
  );
1421
- this.queue.push({ path: this.baseElbow, collisionRects: /* @__PURE__ */ new Set() });
1422
- this.visited.add(pathKey(this.baseElbow));
1492
+ for (const candidate of rankedDirectionPairs) {
1493
+ const key = pathKey(candidate.baseElbow);
1494
+ if (this.visited.has(key)) continue;
1495
+ this.visited.add(key);
1496
+ this.queue.push({
1497
+ path: candidate.baseElbow,
1498
+ collisionRects: /* @__PURE__ */ new Set(),
1499
+ directions: candidate.directions
1500
+ });
1501
+ }
1423
1502
  }
1424
1503
  getConstructorParams() {
1425
1504
  return {
@@ -1427,7 +1506,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1427
1506
  pins: this.pins,
1428
1507
  connectionPair: this.connectionPair,
1429
1508
  inputProblem: this.inputProblem,
1430
- preferExteriorDetours: this.preferExteriorDetours
1509
+ preferExteriorDetours: this.preferExteriorDetours,
1510
+ reserveNetLabelClearance: this.reserveNetLabelClearance
1431
1511
  };
1432
1512
  }
1433
1513
  getTextBoxPaddingForConnectionPair() {
@@ -1490,11 +1570,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1490
1570
  return null;
1491
1571
  }
1492
1572
  pathLength(pts) {
1493
- let sum = 0;
1494
- for (let i = 0; i < pts.length - 1; i++) {
1495
- sum += Math.abs(pts[i + 1].x - pts[i].x) + Math.abs(pts[i + 1].y - pts[i].y);
1496
- }
1497
- return sum;
1573
+ return getPathLength(pts);
1498
1574
  }
1499
1575
  getPinBandPenalty(path) {
1500
1576
  let penalty = 0;
@@ -1529,7 +1605,7 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1529
1605
  this.error = "No collision-free path found";
1530
1606
  return;
1531
1607
  }
1532
- const { path, collisionRects } = state;
1608
+ const { path, collisionRects, directions } = state;
1533
1609
  const [PA, PB] = this.pins;
1534
1610
  const collision = findFirstCollision(path, this.obstacles, {
1535
1611
  excludeRectsForSegment: (segIndex2) => {
@@ -1583,7 +1659,15 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1583
1659
  const last = path[path.length - 1];
1584
1660
  const EPS17 = 1e-9;
1585
1661
  const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS17 && Math.abs(p.y - q.y) < EPS17;
1586
- if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
1662
+ if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y }) && (!this.hasAmbiguousPinDirections || pathMatchesPinDirections({
1663
+ path,
1664
+ pin1: { ...PA, _facingDirection: directions[0] },
1665
+ pin2: { ...PB, _facingDirection: directions[1] }
1666
+ }))) {
1667
+ for (const pinIndex of this.inferredPinIndexes) {
1668
+ const direction = pinIndex === 0 ? segmentDirection(path[0], path[1]) : segmentDirection(path.at(-1), path.at(-2));
1669
+ if (direction) this.pins[pinIndex]._facingDirection = direction;
1670
+ }
1587
1671
  this.solvedTracePath = path;
1588
1672
  this.solved = true;
1589
1673
  }
@@ -1616,7 +1700,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1616
1700
  nextCollisionRects.add(rect);
1617
1701
  this.queue.push({
1618
1702
  path: detour,
1619
- collisionRects: nextCollisionRects
1703
+ collisionRects: nextCollisionRects,
1704
+ directions
1620
1705
  });
1621
1706
  }
1622
1707
  return;
@@ -1725,7 +1810,11 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
1725
1810
  (a2, b2) => a2.length - b2.length || a2.pinBandPenalty - b2.pinBandPenalty
1726
1811
  );
1727
1812
  for (const st of newStates) {
1728
- this.queue.push({ path: st.path, collisionRects: st.collisionRects });
1813
+ this.queue.push({
1814
+ path: st.path,
1815
+ collisionRects: st.collisionRects,
1816
+ directions
1817
+ });
1729
1818
  }
1730
1819
  }
1731
1820
  visualize() {
@@ -1789,6 +1878,7 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1789
1878
  queuedConnectionPairs;
1790
1879
  chipMap;
1791
1880
  currentConnectionPair = null;
1881
+ retryingWithoutNetLabelClearance = false;
1792
1882
  solvedTracePaths = [];
1793
1883
  failedConnectionPairs = [];
1794
1884
  constructor(params) {
@@ -1813,6 +1903,7 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1813
1903
  if (this.activeSubSolver?.solved) {
1814
1904
  this.solvedTracePaths.push({
1815
1905
  ...this.currentConnectionPair,
1906
+ pins: this.activeSubSolver.pins,
1816
1907
  tracePath: this.activeSubSolver.solvedTracePath,
1817
1908
  mspConnectionPairIds: [this.currentConnectionPair.mspPairId],
1818
1909
  pinIds: [
@@ -1822,8 +1913,28 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1822
1913
  });
1823
1914
  this.activeSubSolver = null;
1824
1915
  this.currentConnectionPair = null;
1916
+ this.retryingWithoutNetLabelClearance = false;
1825
1917
  }
1826
1918
  if (this.activeSubSolver?.failed) {
1919
+ if (this.currentConnectionPair && !this.retryingWithoutNetLabelClearance && this.activeSubSolver.hasAmbiguousPinDirections) {
1920
+ const connectionPair2 = this.currentConnectionPair;
1921
+ this.retryingWithoutNetLabelClearance = true;
1922
+ this.activeSubSolver = new SchematicTraceSingleLineSolver2({
1923
+ inputProblem: this.inputProblem,
1924
+ pins: connectionPair2.pins.map((pin) => ({
1925
+ ...pin
1926
+ })),
1927
+ connectionPair: connectionPair2,
1928
+ chipMap: this.chipMap,
1929
+ preferExteriorDetours: shouldPreferExteriorDetours({
1930
+ connectionPair: connectionPair2,
1931
+ allConnectionPairs: this.mspConnectionPairs,
1932
+ inputProblem: this.inputProblem
1933
+ }),
1934
+ reserveNetLabelClearance: false
1935
+ });
1936
+ return;
1937
+ }
1827
1938
  if (this.currentConnectionPair) {
1828
1939
  this.failedConnectionPairs.push({
1829
1940
  ...this.currentConnectionPair,
@@ -1832,6 +1943,7 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1832
1943
  }
1833
1944
  this.activeSubSolver = null;
1834
1945
  this.currentConnectionPair = null;
1946
+ this.retryingWithoutNetLabelClearance = false;
1835
1947
  }
1836
1948
  if (this.activeSubSolver) {
1837
1949
  this.activeSubSolver.step();
@@ -1843,7 +1955,15 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
1843
1955
  return;
1844
1956
  }
1845
1957
  this.currentConnectionPair = connectionPair;
1846
- const { pins } = connectionPair;
1958
+ this.retryingWithoutNetLabelClearance = false;
1959
+ const hasAmbiguousCornerPin = connectionPair.pins.some((pin) => {
1960
+ if (pin._facingDirection) return false;
1961
+ const chip = this.chipMap[pin.chipId];
1962
+ return chip && getPinDirectionCandidates(pin, chip).length > 1;
1963
+ });
1964
+ const pins = hasAmbiguousCornerPin ? connectionPair.pins.map((pin) => ({
1965
+ ...pin
1966
+ })) : connectionPair.pins;
1847
1967
  this.activeSubSolver = new SchematicTraceSingleLineSolver2({
1848
1968
  inputProblem: this.inputProblem,
1849
1969
  pins,
@@ -4254,7 +4374,7 @@ var doesPathOverlapTraceStrokes = (path, traces) => {
4254
4374
  // lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
4255
4375
  var MAX_TRIES = 5;
4256
4376
  var PATH_LENGTH_EPSILON = 1e-9;
4257
- var getPathLength = (points) => {
4377
+ var getPathLength2 = (points) => {
4258
4378
  let length = 0;
4259
4379
  for (let pointIndex = 0; pointIndex < points.length - 1; pointIndex++) {
4260
4380
  const point = points[pointIndex];
@@ -4304,7 +4424,7 @@ var SingleOverlapSolver = class extends BaseSolver {
4304
4424
  );
4305
4425
  }
4306
4426
  this.queuedCandidatePaths = [...candidateByPath.values()].sort((a, b) => {
4307
- const pathLengthDifference = getPathLength(a) - getPathLength(b);
4427
+ const pathLengthDifference = getPathLength2(a) - getPathLength2(b);
4308
4428
  if (Math.abs(pathLengthDifference) >= PATH_LENGTH_EPSILON) {
4309
4429
  return pathLengthDifference;
4310
4430
  }
@@ -5238,12 +5358,62 @@ var tryConnectPoints = (start, end) => {
5238
5358
  };
5239
5359
 
5240
5360
  // lib/solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts
5241
- var hasCollisionsWithLabels = (pathSegments, labels) => {
5361
+ var EPSILON = 1e-9;
5362
+ var rangesMeetWithoutOverlap = (firstMin, firstMax, secondMin, secondMax) => firstMax >= secondMin - EPSILON && secondMax >= firstMin - EPSILON && Math.min(firstMax, secondMax) - Math.max(firstMin, secondMin) <= EPSILON;
5363
+ var pointsEqual = (first, second) => Math.abs(first.x - second.x) <= EPSILON && Math.abs(first.y - second.y) <= EPSILON;
5364
+ var continuesExistingBoundarySegment = ({
5365
+ start,
5366
+ end,
5367
+ label,
5368
+ originalPath
5369
+ }) => {
5370
+ const isVertical6 = Math.abs(start.x - end.x) <= EPSILON;
5371
+ const isHorizontal5 = Math.abs(start.y - end.y) <= EPSILON;
5372
+ const isOnBoundary = isVertical6 ? Math.abs(start.x - label.minX) <= EPSILON || Math.abs(start.x - label.maxX) <= EPSILON : isHorizontal5 ? Math.abs(start.y - label.minY) <= EPSILON || Math.abs(start.y - label.maxY) <= EPSILON : false;
5373
+ if (!isOnBoundary) return false;
5374
+ for (let i = 0; i < originalPath.length - 1; i++) {
5375
+ const isEndpointSegment = i === 0 || i === originalPath.length - 2;
5376
+ if (!isEndpointSegment) continue;
5377
+ const originalStart = originalPath[i];
5378
+ const originalEnd = originalPath[i + 1];
5379
+ const innerEndpoint = i === 0 ? originalEnd : originalStart;
5380
+ if (!pointsEqual(start, innerEndpoint) && !pointsEqual(end, innerEndpoint)) {
5381
+ continue;
5382
+ }
5383
+ if (!segmentIntersectsRect(originalStart, originalEnd, label)) continue;
5384
+ if (isVertical6 && Math.abs(originalStart.x - originalEnd.x) <= EPSILON && Math.abs(originalStart.x - start.x) <= EPSILON && rangesMeetWithoutOverlap(
5385
+ Math.min(start.y, end.y),
5386
+ Math.max(start.y, end.y),
5387
+ Math.min(originalStart.y, originalEnd.y),
5388
+ Math.max(originalStart.y, originalEnd.y)
5389
+ )) {
5390
+ return true;
5391
+ }
5392
+ if (isHorizontal5 && Math.abs(originalStart.y - originalEnd.y) <= EPSILON && Math.abs(originalStart.y - start.y) <= EPSILON && rangesMeetWithoutOverlap(
5393
+ Math.min(start.x, end.x),
5394
+ Math.max(start.x, end.x),
5395
+ Math.min(originalStart.x, originalEnd.x),
5396
+ Math.max(originalStart.x, originalEnd.x)
5397
+ )) {
5398
+ return true;
5399
+ }
5400
+ }
5401
+ return false;
5402
+ };
5403
+ var hasCollisionsWithLabels = (pathSegments, labels, options = {}) => {
5242
5404
  for (let i = 0; i < pathSegments.length - 1; i++) {
5243
5405
  const p1 = pathSegments[i];
5244
5406
  const p2 = pathSegments[i + 1];
5245
5407
  for (const label of labels) {
5246
5408
  if (segmentIntersectsRect(p1, p2, label)) {
5409
+ if (options.originalPath && continuesExistingBoundarySegment({
5410
+ start: p1,
5411
+ end: p2,
5412
+ label,
5413
+ originalPath: options.originalPath
5414
+ })) {
5415
+ continue;
5416
+ }
5247
5417
  return true;
5248
5418
  }
5249
5419
  }
@@ -5300,7 +5470,8 @@ var minimizeTurns = ({
5300
5470
  path,
5301
5471
  obstacles,
5302
5472
  labelBounds,
5303
- originalPath
5473
+ originalPath,
5474
+ allowLabelBoundaryExtension = false
5304
5475
  }) => {
5305
5476
  if (path.length <= 2) {
5306
5477
  return path;
@@ -5336,7 +5507,8 @@ var minimizeTurns = ({
5336
5507
  const collidesWithObstacles = hasCollisions(connection, obstacles);
5337
5508
  const collidesWithLabels = hasCollisionsWithLabels(
5338
5509
  connection,
5339
- labelBounds
5510
+ labelBounds,
5511
+ allowLabelBoundaryExtension ? { originalPath } : {}
5340
5512
  );
5341
5513
  if (!collidesWithObstacles && !collidesWithLabels) {
5342
5514
  const newTurns = countTurns(testPath);
@@ -5385,7 +5557,8 @@ var minimizeTurns = ({
5385
5557
  );
5386
5558
  const collidesWithLabels = hasCollisionsWithLabels(
5387
5559
  connectionSegments,
5388
- labelBounds
5560
+ labelBounds,
5561
+ allowLabelBoundaryExtension ? { originalPath } : {}
5389
5562
  );
5390
5563
  if (!collidesWithObstacles && !collidesWithLabels) {
5391
5564
  const newTurns = countTurns(testPath);
@@ -5420,7 +5593,8 @@ var minimizeTurns = ({
5420
5593
  const collidesWithObstacles = hasCollisions([p1, p3], obstacles);
5421
5594
  const collidesWithLabels = hasCollisionsWithLabels(
5422
5595
  [p1, p3],
5423
- labelBounds
5596
+ labelBounds,
5597
+ allowLabelBoundaryExtension ? { originalPath } : {}
5424
5598
  );
5425
5599
  if (!collidesWithObstacles && !collidesWithLabels) {
5426
5600
  optimizedPath = testPath;
@@ -5448,7 +5622,7 @@ var getRailOrientation = (a, b) => {
5448
5622
  return null;
5449
5623
  };
5450
5624
  var rangesTouchOrOverlap = (a, b) => Math.min(a.maxAlong, b.maxAlong) - Math.max(a.minAlong, b.minAlong) >= -RAIL_ALIGNMENT_EPSILON;
5451
- var pointsEqual = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
5625
+ var pointsEqual2 = (a, b) => nearlyEqual(a.x, b.x) && nearlyEqual(a.y, b.y);
5452
5626
  var getDistinctCoordinates = (coordinates) => {
5453
5627
  const distinct = [];
5454
5628
  for (const coordinate of coordinates) {
@@ -5570,6 +5744,19 @@ var shortenExcessiveLabelPaddingDetour = ({
5570
5744
  };
5571
5745
 
5572
5746
  // lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts
5747
+ var PATH_LENGTH_EPSILON2 = 1e-9;
5748
+ var getPathLength3 = (path) => path.slice(1).reduce((length, point, pointIndex) => {
5749
+ const previousPoint = path[pointIndex];
5750
+ return length + Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
5751
+ }, 0);
5752
+ var chooseLengthPreservingBoundaryRoute = ({
5753
+ strictPath,
5754
+ boundaryPath
5755
+ }) => {
5756
+ const doesNotAddTurns = countTurns(boundaryPath) <= countTurns(strictPath);
5757
+ const preservesLength = Math.abs(getPathLength3(boundaryPath) - getPathLength3(strictPath)) <= PATH_LENGTH_EPSILON2;
5758
+ return doesNotAddTurns && preservesLength ? boundaryPath : strictPath;
5759
+ };
5573
5760
  var minimizeTurnsWithFilteredLabels = ({
5574
5761
  targetMspConnectionPairId,
5575
5762
  traces,
@@ -5632,21 +5819,33 @@ var minimizeTurnsWithFilteredLabels = ({
5632
5819
  minY: nl.center.y - nl.height / 2 - paddingBuffer,
5633
5820
  maxY: nl.center.y + nl.height / 2 + paddingBuffer
5634
5821
  }));
5635
- const strictPath = minimizeTurns({
5636
- path: originalPath,
5637
- obstacles: [...staticObstacles, ...getTraceObstacles2(otherTraces)],
5638
- labelBounds,
5639
- originalPath
5640
- });
5641
- const relaxedPath = minimizeTurns({
5642
- path: originalPath,
5643
- obstacles: [
5644
- ...staticObstacles,
5645
- ...getTraceObstacles2(relaxedObstacleTraces)
5646
- ],
5647
- labelBounds,
5648
- originalPath
5649
- });
5822
+ const minimizeForObstacles = (obstacles) => {
5823
+ const strictPath2 = minimizeTurns({
5824
+ path: originalPath,
5825
+ obstacles,
5826
+ labelBounds,
5827
+ originalPath
5828
+ });
5829
+ const boundaryPath = minimizeTurns({
5830
+ path: originalPath,
5831
+ obstacles,
5832
+ labelBounds,
5833
+ originalPath,
5834
+ allowLabelBoundaryExtension: true
5835
+ });
5836
+ return chooseLengthPreservingBoundaryRoute({
5837
+ strictPath: strictPath2,
5838
+ boundaryPath
5839
+ });
5840
+ };
5841
+ const strictPath = minimizeForObstacles([
5842
+ ...staticObstacles,
5843
+ ...getTraceObstacles2(otherTraces)
5844
+ ]);
5845
+ const relaxedPath = minimizeForObstacles([
5846
+ ...staticObstacles,
5847
+ ...getTraceObstacles2(relaxedObstacleTraces)
5848
+ ]);
5650
5849
  const sameNetTraces = otherTraces.filter(
5651
5850
  (trace) => trace.globalConnNetId === targetTrace.globalConnNetId
5652
5851
  );
@@ -6038,7 +6237,7 @@ var segmentRunsAlongRectBoundary = (start, end, rect) => {
6038
6237
 
6039
6238
  // lib/solvers/Example28Solver/geometry.ts
6040
6239
  var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
6041
- var getPathLength2 = (path) => {
6240
+ var getPathLength4 = (path) => {
6042
6241
  let length = 0;
6043
6242
  for (let i = 0; i < path.length - 1; i++) {
6044
6243
  length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
@@ -6192,7 +6391,7 @@ var getTraceGeometryMetrics = (traces, allTraces) => ({
6192
6391
  ),
6193
6392
  visibleLength: getVisibleTraceLength(traces),
6194
6393
  pathLength: traces.reduce(
6195
- (sum, trace) => sum + getPathLength2(trace.tracePath),
6394
+ (sum, trace) => sum + getPathLength4(trace.tracePath),
6196
6395
  0
6197
6396
  ),
6198
6397
  otherNetCrossings: countOtherNetCrossings(traces, allTraces)
@@ -6214,7 +6413,7 @@ var isReadabilityImprovement = (candidate, baseline) => candidate.turnCount <= b
6214
6413
 
6215
6414
  // lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts
6216
6415
  var tracePathChanged = (original, candidate) => original.tracePath.length !== candidate.tracePath.length || candidate.tracePath.some(
6217
- (point, index) => !pointsEqual(point, original.tracePath[index])
6416
+ (point, index) => !pointsEqual2(point, original.tracePath[index])
6218
6417
  );
6219
6418
  var evaluateRailGroup = ({
6220
6419
  group,
@@ -7165,7 +7364,7 @@ var UntangleTraceSubsolver = class extends BaseSolver {
7165
7364
  candidate.traceId
7166
7365
  ).isColliding && (crossing.isInitialBundleCrossing || !candidate.collision.isColliding)
7167
7366
  ).sort(
7168
- (first, second) => Number(first.collision.isColliding) - Number(second.collision.isColliding) || getPathLength2(first.path) - getPathLength2(second.path)
7367
+ (first, second) => Number(first.collision.isColliding) - Number(second.collision.isColliding) || getPathLength4(first.path) - getPathLength4(second.path)
7169
7368
  );
7170
7369
  const bestCandidate = validCandidates[0];
7171
7370
  if (!bestCandidate) return false;
@@ -7505,7 +7704,7 @@ var rerouteGeneratedNetLabelConnectorCrossings = ({
7505
7704
  chipBounds: [],
7506
7705
  clearance
7507
7706
  }).filter(
7508
- (candidate) => getPathLength2(candidate.path) <= getPathLength2(trace.tracePath) + EPS12 && countTurns(candidate.path) <= countTurns(trace.tracePath) + 2 && !isPathCollidingWithObstacles(
7707
+ (candidate) => getPathLength4(candidate.path) <= getPathLength4(trace.tracePath) + EPS12 && countTurns(candidate.path) <= countTurns(trace.tracePath) + 2 && !isPathCollidingWithObstacles(
7509
7708
  candidate.path,
7510
7709
  componentAndTextObstacles
7511
7710
  ) && !hasCollisionsWithLabels(candidate.path, foreignLabelBounds) && !isPathColliding(candidate.path, foreignTraces, trace.mspPairId).isColliding && !doesPathCoincideWithTraces(candidate.path, foreignTraces)
@@ -7515,7 +7714,7 @@ var rerouteGeneratedNetLabelConnectorCrossings = ({
7515
7714
  }));
7516
7715
  });
7517
7716
  candidates.sort((first, second) => {
7518
- const lengthDifference = getPathLength2(first.path) - getPathLength2(second.path);
7717
+ const lengthDifference = getPathLength4(first.path) - getPathLength4(second.path);
7519
7718
  return Math.abs(lengthDifference) > EPS12 ? lengthDifference : countTurns(first.path) - countTurns(second.path);
7520
7719
  });
7521
7720
  const bestCandidate = candidates[0];
@@ -7715,7 +7914,7 @@ var extendVerticalTracePathAtInteriorPoint = ({
7715
7914
  extensionEndPoint
7716
7915
  }) => {
7717
7916
  const sourceIndex = tracePath.findIndex(
7718
- (point) => pointsEqual(point, sourcePoint)
7917
+ (point) => pointsEqual2(point, sourcePoint)
7719
7918
  );
7720
7919
  if (sourceIndex <= 0 || sourceIndex >= tracePath.length - 1) return null;
7721
7920
  const extensionDirectionY = extensionEndPoint.y - sourcePoint.y;
@@ -8294,7 +8493,7 @@ var scoreTracePath = ({
8294
8493
  labelHugDistance: getLabelHugDistance(tracePath, obstacleLabel),
8295
8494
  traceIntersections: countTraceIntersections(candidateTrace, outputTraces),
8296
8495
  chipBoundaryOverlap: getChipBoundaryOverlap(tracePath, chipObstacles),
8297
- pathLength: getPathLength2(tracePath)
8496
+ pathLength: getPathLength4(tracePath)
8298
8497
  };
8299
8498
  };
8300
8499
  var countTraceIntersections = (trace, outputTraces) => {
@@ -8758,7 +8957,7 @@ var getConnectorTracePath = (source, target, orientation) => simplifyOrthogonalP
8758
8957
  );
8759
8958
  var simplifyOrthogonalPath = (path) => {
8760
8959
  const deduped = path.filter(
8761
- (point, index) => index === 0 || !pointsEqual2(point, path[index - 1])
8960
+ (point, index) => index === 0 || !pointsEqual3(point, path[index - 1])
8762
8961
  );
8763
8962
  if (deduped.length < 3) return deduped;
8764
8963
  const simplified = [deduped[0]];
@@ -8774,7 +8973,7 @@ var simplifyOrthogonalPath = (path) => {
8774
8973
  simplified.push(deduped[deduped.length - 1]);
8775
8974
  return simplified;
8776
8975
  };
8777
- var pointsEqual2 = (a, b) => sameX2(a, b) && sameY2(a, b);
8976
+ var pointsEqual3 = (a, b) => sameX2(a, b) && sameY2(a, b);
8778
8977
  var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS13;
8779
8978
  var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS13;
8780
8979
  var getMaxSearchDistance = (inputProblem) => {
@@ -12476,7 +12675,7 @@ var removeConsecutiveDuplicatePoints = (path) => {
12476
12675
  }
12477
12676
  return filteredPath;
12478
12677
  };
12479
- var getPathLength3 = (path) => {
12678
+ var getPathLength5 = (path) => {
12480
12679
  let pathLength = 0;
12481
12680
  for (let pointIndex = 0; pointIndex < path.length - 1; pointIndex++) {
12482
12681
  const startPoint = path[pointIndex];
@@ -12560,7 +12759,7 @@ var getPerimeterCandidates = ({
12560
12759
  );
12561
12760
  }
12562
12761
  return candidates.sort(
12563
- (firstPath, secondPath) => getPathLength3(firstPath) - getPathLength3(secondPath)
12762
+ (firstPath, secondPath) => getPathLength5(firstPath) - getPathLength5(secondPath)
12564
12763
  );
12565
12764
  };
12566
12765
  var getSegmentMidpoint = (startPoint, endPoint) => {
@@ -12665,7 +12864,7 @@ var getJunctionCandidates = ({
12665
12864
  }
12666
12865
  }
12667
12866
  return candidates.sort(
12668
- (firstPath, secondPath) => getPathLength3(firstPath) - getPathLength3(secondPath)
12867
+ (firstPath, secondPath) => getPathLength5(firstPath) - getPathLength5(secondPath)
12669
12868
  );
12670
12869
  };
12671
12870
  var pathCollidesWithObstacles = ({
@@ -13031,24 +13230,228 @@ var findNearestSharedPinExitRail = ({
13031
13230
  return nearestRail;
13032
13231
  };
13033
13232
 
13233
+ // lib/solvers/SameNetJunctionAlignmentSolver/getSharedPin.ts
13234
+ var getSharedPin = ({
13235
+ donorTrace,
13236
+ branchTrace
13237
+ }) => {
13238
+ const branchPinIds = new Set(branchTrace.pins.map((pin) => pin.pinId));
13239
+ return donorTrace.pins.find((pin) => branchPinIds.has(pin.pinId)) ?? null;
13240
+ };
13241
+
13242
+ // lib/solvers/SameNetJunctionAlignmentSolver/collapseRedundantSharedEndpointStubs.ts
13243
+ var getSegmentAxis3 = (start, end) => {
13244
+ if (nearlyEqual(start.x, end.x)) return "y";
13245
+ if (nearlyEqual(start.y, end.y)) return "x";
13246
+ return null;
13247
+ };
13248
+ var getPathFromSharedPin = ({
13249
+ trace,
13250
+ sharedPinId
13251
+ }) => {
13252
+ const tracePath = simplifyPath(trace.tracePath);
13253
+ if (trace.pins[0]?.pinId === sharedPinId) return tracePath;
13254
+ return tracePath.reverse();
13255
+ };
13256
+ var traceCanLoseSharedPrefix = ({
13257
+ trace,
13258
+ sharedPrefix,
13259
+ netLabelPlacements
13260
+ }) => {
13261
+ return !netLabelPlacements.some(
13262
+ (label) => label.mspConnectionPairIds.includes(trace.mspPairId) && tracePathContainsPoint(sharedPrefix, label.anchorPoint)
13263
+ );
13264
+ };
13265
+ var getTraceWithoutSharedPrefix = ({
13266
+ trace,
13267
+ pathFromSharedPin,
13268
+ sharedPinId,
13269
+ branchPoint
13270
+ }) => {
13271
+ const branchSegmentIndex = pathFromSharedPin.findIndex((point, index) => {
13272
+ const nextPoint = pathFromSharedPin[index + 1];
13273
+ if (!nextPoint) return false;
13274
+ return tracePathContainsPoint([point, nextPoint], branchPoint);
13275
+ });
13276
+ if (branchSegmentIndex < 0) return null;
13277
+ const pathFromJunction = simplifyPath([
13278
+ branchPoint,
13279
+ ...pathFromSharedPin.slice(branchSegmentIndex + 1)
13280
+ ]);
13281
+ let tracePath = pathFromJunction;
13282
+ if (trace.pins[0]?.pinId !== sharedPinId) {
13283
+ tracePath = pathFromJunction.reverse();
13284
+ }
13285
+ return { ...trace, tracePath };
13286
+ };
13287
+ var collapseRedundantSharedEndpointStubs = ({
13288
+ traces,
13289
+ netLabelPlacements,
13290
+ netLabelConnectorTraceIds,
13291
+ multiPinNetPinIds
13292
+ }) => {
13293
+ const outputTraces = [...traces];
13294
+ for (let firstIndex = 0; firstIndex < outputTraces.length; firstIndex++) {
13295
+ const firstTrace = outputTraces[firstIndex];
13296
+ if (netLabelConnectorTraceIds.has(firstTrace.mspPairId)) continue;
13297
+ for (let secondIndex = firstIndex + 1; secondIndex < outputTraces.length; secondIndex++) {
13298
+ const secondTrace = outputTraces[secondIndex];
13299
+ if (netLabelConnectorTraceIds.has(secondTrace.mspPairId) || firstTrace.globalConnNetId !== secondTrace.globalConnNetId) {
13300
+ continue;
13301
+ }
13302
+ const sharedPin = getSharedPin({
13303
+ donorTrace: firstTrace,
13304
+ branchTrace: secondTrace
13305
+ });
13306
+ if (!sharedPin || !multiPinNetPinIds.has(sharedPin.pinId)) continue;
13307
+ const firstPath = getPathFromSharedPin({
13308
+ trace: firstTrace,
13309
+ sharedPinId: sharedPin.pinId
13310
+ });
13311
+ const secondPath = getPathFromSharedPin({
13312
+ trace: secondTrace,
13313
+ sharedPinId: sharedPin.pinId
13314
+ });
13315
+ if (firstPath.length < 2 || secondPath.length < 2) continue;
13316
+ const departureAxis = getSegmentAxis3(firstPath[0], firstPath[1]);
13317
+ const secondDepartureAxis = getSegmentAxis3(secondPath[0], secondPath[1]);
13318
+ if (!departureAxis || departureAxis !== secondDepartureAxis) continue;
13319
+ const firstDepartureDirection = firstPath[1][departureAxis] - firstPath[0][departureAxis];
13320
+ const secondDepartureDirection = secondPath[1][departureAxis] - secondPath[0][departureAxis];
13321
+ if (!pointsEqual2(firstPath[1], secondPath[1]) && Math.sign(firstDepartureDirection) === Math.sign(secondDepartureDirection)) {
13322
+ let traceIndexToCollapse2 = secondIndex;
13323
+ let traceToCollapse2 = secondTrace;
13324
+ let pathToCollapse2 = secondPath;
13325
+ if (Math.abs(firstDepartureDirection) < Math.abs(secondDepartureDirection)) {
13326
+ traceIndexToCollapse2 = firstIndex;
13327
+ traceToCollapse2 = firstTrace;
13328
+ pathToCollapse2 = firstPath;
13329
+ }
13330
+ const branchPoint2 = pathToCollapse2[1];
13331
+ if (!traceCanLoseSharedPrefix({
13332
+ trace: traceToCollapse2,
13333
+ sharedPrefix: [pathToCollapse2[0], branchPoint2],
13334
+ netLabelPlacements
13335
+ })) {
13336
+ if (traceIndexToCollapse2 === firstIndex) {
13337
+ traceIndexToCollapse2 = secondIndex;
13338
+ traceToCollapse2 = secondTrace;
13339
+ pathToCollapse2 = secondPath;
13340
+ } else {
13341
+ traceIndexToCollapse2 = firstIndex;
13342
+ traceToCollapse2 = firstTrace;
13343
+ pathToCollapse2 = firstPath;
13344
+ }
13345
+ }
13346
+ if (!traceCanLoseSharedPrefix({
13347
+ trace: traceToCollapse2,
13348
+ sharedPrefix: [pathToCollapse2[0], branchPoint2],
13349
+ netLabelPlacements
13350
+ })) {
13351
+ continue;
13352
+ }
13353
+ const collapsedTrace2 = getTraceWithoutSharedPrefix({
13354
+ trace: traceToCollapse2,
13355
+ pathFromSharedPin: pathToCollapse2,
13356
+ sharedPinId: sharedPin.pinId,
13357
+ branchPoint: branchPoint2
13358
+ });
13359
+ if (!collapsedTrace2) continue;
13360
+ outputTraces[traceIndexToCollapse2] = collapsedTrace2;
13361
+ break;
13362
+ }
13363
+ if (firstPath.length < 3 || secondPath.length < 3) continue;
13364
+ if (!pointsEqual2(firstPath[1], secondPath[1])) continue;
13365
+ const railAxis = getSegmentAxis3(firstPath[1], firstPath[2]);
13366
+ const secondRailAxis = getSegmentAxis3(secondPath[1], secondPath[2]);
13367
+ if (!railAxis || railAxis !== secondRailAxis || railAxis === departureAxis) {
13368
+ continue;
13369
+ }
13370
+ const firstRailDirection = firstPath[2][railAxis] - firstPath[1][railAxis];
13371
+ const secondRailDirection = secondPath[2][railAxis] - secondPath[1][railAxis];
13372
+ const railsHaveSameDirection = Math.sign(firstRailDirection) === Math.sign(secondRailDirection);
13373
+ const sharedStub = firstPath.slice(0, 2);
13374
+ if (!railsHaveSameDirection && !nearlyEqual(
13375
+ Math.abs(firstRailDirection),
13376
+ Math.abs(secondRailDirection)
13377
+ ) && netLabelPlacements.some(
13378
+ (label) => label.globalConnNetId === firstTrace.globalConnNetId && tracePathContainsPoint(sharedStub, label.anchorPoint)
13379
+ )) {
13380
+ continue;
13381
+ }
13382
+ let traceIndexToCollapse = secondIndex;
13383
+ let traceToCollapse = secondTrace;
13384
+ let pathToCollapse = secondPath;
13385
+ let branchPoint = firstPath[1];
13386
+ if (railsHaveSameDirection && Math.abs(firstRailDirection) < Math.abs(secondRailDirection)) {
13387
+ traceIndexToCollapse = firstIndex;
13388
+ traceToCollapse = firstTrace;
13389
+ pathToCollapse = firstPath;
13390
+ branchPoint = firstPath[2];
13391
+ } else if (railsHaveSameDirection) {
13392
+ branchPoint = secondPath[2];
13393
+ }
13394
+ const sharedPrefix = [pathToCollapse[0], pathToCollapse[1], branchPoint];
13395
+ if (!traceCanLoseSharedPrefix({
13396
+ trace: traceToCollapse,
13397
+ sharedPrefix,
13398
+ netLabelPlacements
13399
+ })) {
13400
+ if (traceIndexToCollapse === firstIndex) {
13401
+ traceIndexToCollapse = secondIndex;
13402
+ traceToCollapse = secondTrace;
13403
+ pathToCollapse = secondPath;
13404
+ } else {
13405
+ traceIndexToCollapse = firstIndex;
13406
+ traceToCollapse = firstTrace;
13407
+ pathToCollapse = firstPath;
13408
+ }
13409
+ }
13410
+ if (!traceCanLoseSharedPrefix({
13411
+ trace: traceToCollapse,
13412
+ sharedPrefix: [pathToCollapse[0], pathToCollapse[1], branchPoint],
13413
+ netLabelPlacements
13414
+ })) {
13415
+ continue;
13416
+ }
13417
+ const collapsedTrace = getTraceWithoutSharedPrefix({
13418
+ trace: traceToCollapse,
13419
+ pathFromSharedPin: pathToCollapse,
13420
+ sharedPinId: sharedPin.pinId,
13421
+ branchPoint
13422
+ });
13423
+ if (!collapsedTrace) continue;
13424
+ outputTraces[traceIndexToCollapse] = collapsedTrace;
13425
+ break;
13426
+ }
13427
+ }
13428
+ return outputTraces;
13429
+ };
13430
+
13034
13431
  // lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
13035
13432
  var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
13036
13433
  var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
13037
13434
  var MAX_SHARED_PIN_RAIL_OFFSET = 0.05;
13038
13435
  var MAX_SHARED_ENDPOINT_RAIL_OFFSET = 0.8;
13039
13436
  var MIN_RETURN_STEM_LENGTH = 0.05;
13040
- var getSegmentAxis3 = (start, end) => {
13437
+ var getMultiPinNetPinIds = (inputProblem) => {
13438
+ const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
13439
+ const inputPinIds = new Set(
13440
+ inputProblem.chips.flatMap((chip) => chip.pins.map((pin) => pin.pinId))
13441
+ );
13442
+ return new Set(
13443
+ [...inputPinIds].filter((pinId) => {
13444
+ const globalNetId = netConnMap.getNetConnectedToId(pinId);
13445
+ if (!globalNetId) return false;
13446
+ return netConnMap.getIdsConnectedToNet(globalNetId).filter((connectedId) => inputPinIds.has(connectedId)).length > 2;
13447
+ })
13448
+ );
13449
+ };
13450
+ var getSegmentAxis4 = (start, end) => {
13041
13451
  if (isHorizontal2(start, end)) return "x";
13042
13452
  if (isVertical2(start, end)) return "y";
13043
13453
  return null;
13044
13454
  };
13045
- var getSharedPin = ({
13046
- donorTrace,
13047
- branchTrace
13048
- }) => {
13049
- const branchPinIds = new Set(branchTrace.pins.map((pin) => pin.pinId));
13050
- return donorTrace.pins.find((pin) => branchPinIds.has(pin.pinId)) ?? null;
13051
- };
13052
13455
  var getOtherPin = ({
13053
13456
  trace,
13054
13457
  sharedPin
@@ -13274,8 +13677,8 @@ var getAlignedSharedEndpointRailPath = ({
13274
13677
  const donorPath = pathFromShared(donorTrace);
13275
13678
  const branchPath = pathFromShared(branchTrace);
13276
13679
  if (donorPath.length < 3 || branchPath.length < 4) return null;
13277
- const donorDepartureAxis = getSegmentAxis3(donorPath[0], donorPath[1]);
13278
- const branchDepartureAxis = getSegmentAxis3(branchPath[0], branchPath[1]);
13680
+ const donorDepartureAxis = getSegmentAxis4(donorPath[0], donorPath[1]);
13681
+ const branchDepartureAxis = getSegmentAxis4(branchPath[0], branchPath[1]);
13279
13682
  if (!donorDepartureAxis || donorDepartureAxis !== branchDepartureAxis) {
13280
13683
  return null;
13281
13684
  }
@@ -13283,8 +13686,8 @@ var getAlignedSharedEndpointRailPath = ({
13283
13686
  const branchDeparture = branchPath[1][branchDepartureAxis] - branchPath[0][branchDepartureAxis];
13284
13687
  if (Math.sign(donorDeparture) !== Math.sign(branchDeparture)) return null;
13285
13688
  let railIndex = 1;
13286
- let donorRailAxis = getSegmentAxis3(donorPath[1], donorPath[2]);
13287
- let branchRailAxis = getSegmentAxis3(branchPath[1], branchPath[2]);
13689
+ let donorRailAxis = getSegmentAxis4(donorPath[1], donorPath[2]);
13690
+ let branchRailAxis = getSegmentAxis4(branchPath[1], branchPath[2]);
13288
13691
  if (!donorRailAxis || donorRailAxis !== branchRailAxis || donorRailAxis === donorDepartureAxis) {
13289
13692
  return null;
13290
13693
  }
@@ -13292,11 +13695,11 @@ var getAlignedSharedEndpointRailPath = ({
13292
13695
  if (firstRailsAreAligned) {
13293
13696
  railIndex++;
13294
13697
  if (!donorPath[railIndex + 2] || !branchPath[railIndex + 2]) return null;
13295
- donorRailAxis = getSegmentAxis3(
13698
+ donorRailAxis = getSegmentAxis4(
13296
13699
  donorPath[railIndex],
13297
13700
  donorPath[railIndex + 1]
13298
13701
  );
13299
- branchRailAxis = getSegmentAxis3(
13702
+ branchRailAxis = getSegmentAxis4(
13300
13703
  branchPath[railIndex],
13301
13704
  branchPath[railIndex + 1]
13302
13705
  );
@@ -13304,7 +13707,7 @@ var getAlignedSharedEndpointRailPath = ({
13304
13707
  }
13305
13708
  let branchParallelRailCount = 0;
13306
13709
  for (let index = 1; index < branchPath.length - 2; index++) {
13307
- if (getSegmentAxis3(branchPath[index], branchPath[index + 1]) === branchRailAxis) {
13710
+ if (getSegmentAxis4(branchPath[index], branchPath[index + 1]) === branchRailAxis) {
13308
13711
  branchParallelRailCount++;
13309
13712
  }
13310
13713
  }
@@ -13346,13 +13749,13 @@ var getAlignedAttachedLabelConnectorPath = ({
13346
13749
  const junction = connectorPath[0];
13347
13750
  const turn = connectorPath[1];
13348
13751
  const railEnd = connectorPath[2];
13349
- const departureAxis = getSegmentAxis3(junction, turn);
13350
- const railAxis = getSegmentAxis3(turn, railEnd);
13752
+ const departureAxis = getSegmentAxis4(junction, turn);
13753
+ const railAxis = getSegmentAxis4(turn, railEnd);
13351
13754
  if (!departureAxis || !railAxis || departureAxis === railAxis) return null;
13352
13755
  const donorSegmentIndex = donorTrace.tracePath.findIndex(
13353
13756
  (point, index, path) => {
13354
13757
  const next = path[index + 1];
13355
- return next && getSegmentAxis3(point, next) === departureAxis && tracePathContainsPoint([point, next], junction) && tracePathContainsPoint([point, next], turn);
13758
+ return next && getSegmentAxis4(point, next) === departureAxis && tracePathContainsPoint([point, next], junction) && tracePathContainsPoint([point, next], turn);
13356
13759
  }
13357
13760
  );
13358
13761
  if (donorSegmentIndex < 0) return null;
@@ -13365,7 +13768,7 @@ var getAlignedAttachedLabelConnectorPath = ({
13365
13768
  donorSegmentIndex > 0 ? [donorTrace.tracePath[donorSegmentIndex - 1], donorSegmentStart] : null,
13366
13769
  donorSegmentIndex + 2 < donorTrace.tracePath.length ? [donorSegmentEnd, donorTrace.tracePath[donorSegmentIndex + 2]] : null
13367
13770
  ].flatMap(
13368
- (segment) => segment && getSegmentAxis3(segment[0], segment[1]) === railAxis ? [segment] : []
13771
+ (segment) => segment && getSegmentAxis4(segment[0], segment[1]) === railAxis ? [segment] : []
13369
13772
  );
13370
13773
  if (adjacentDonorRails.length === 0) return null;
13371
13774
  const railCoordinateAxis = railAxis === "x" ? "y" : "x";
@@ -13448,7 +13851,7 @@ var preserveAttachedLabelConnectorJunctions = ({
13448
13851
  if (!movedJunction) return null;
13449
13852
  const neighborIndex = endpointIndex === 0 ? 1 : endpointIndex - 1;
13450
13853
  const neighbor = connector.tracePath[neighborIndex];
13451
- if (!neighbor || getSegmentAxis3(originalJunction, neighbor) !== getSegmentAxis3(movedJunction, neighbor)) {
13854
+ if (!neighbor || getSegmentAxis4(originalJunction, neighbor) !== getSegmentAxis4(movedJunction, neighbor)) {
13452
13855
  return null;
13453
13856
  }
13454
13857
  const movedConnector = {
@@ -13871,6 +14274,13 @@ var alignSameNetJunctions = ({
13871
14274
  }
13872
14275
  }
13873
14276
  }
14277
+ const collapsedSharedEndpointStubs = collapseRedundantSharedEndpointStubs({
14278
+ traces: outputTraces,
14279
+ netLabelPlacements: outputNetLabelPlacements,
14280
+ netLabelConnectorTraceIds,
14281
+ multiPinNetPinIds: getMultiPinNetPinIds(inputProblem)
14282
+ });
14283
+ outputTraces = collapsedSharedEndpointStubs;
13874
14284
  return {
13875
14285
  traces: outputTraces,
13876
14286
  netLabelPlacements: outputNetLabelPlacements,
@@ -13885,8 +14295,8 @@ var getTracePathStartingAtPin = (trace, pin) => {
13885
14295
  const pathStart = trace.tracePath[0];
13886
14296
  const pathEnd = trace.tracePath.at(-1);
13887
14297
  if (!pathStart || !pathEnd) return null;
13888
- if (pointsEqual(pathStart, pin)) return trace.tracePath;
13889
- if (pointsEqual(pathEnd, pin)) return [...trace.tracePath].reverse();
14298
+ if (pointsEqual2(pathStart, pin)) return trace.tracePath;
14299
+ if (pointsEqual2(pathEnd, pin)) return [...trace.tracePath].reverse();
13890
14300
  return null;
13891
14301
  };
13892
14302
  var getPerpendicularPathCrossings = (targetPath, donorPath) => {
@@ -13908,7 +14318,7 @@ var getPerpendicularPathCrossings = (targetPath, donorPath) => {
13908
14318
  donorStart,
13909
14319
  donorEnd
13910
14320
  );
13911
- if (!intersectionPoint || pointsEqual(intersectionPoint, sharedEndpoint)) {
14321
+ if (!intersectionPoint || pointsEqual2(intersectionPoint, sharedEndpoint)) {
13912
14322
  continue;
13913
14323
  }
13914
14324
  crossings.push({
@@ -13931,7 +14341,7 @@ var buildCollapsedCyclePath = ({
13931
14341
  crossing.intersectionPoint,
13932
14342
  ...targetPath.slice(crossing.targetSegmentIndex + 1)
13933
14343
  ]);
13934
- if (pointsEqual(targetTrace.tracePath[0], targetPath[0])) {
14344
+ if (pointsEqual2(targetTrace.tracePath[0], targetPath[0])) {
13935
14345
  return pathFromSharedPin;
13936
14346
  }
13937
14347
  return pathFromSharedPin.reverse();
@@ -14379,8 +14789,8 @@ var generateElbowTransitionSimplificationCandidates = ({
14379
14789
  };
14380
14790
 
14381
14791
  // lib/solvers/TraceElbowTransitionSimplificationSolver/TraceElbowTransitionSimplificationSolver.ts
14382
- var PATH_LENGTH_EPSILON2 = 1e-9;
14383
- var getPathLength4 = (points) => points.slice(1).reduce((length, point, pointIndex) => {
14792
+ var PATH_LENGTH_EPSILON3 = 1e-9;
14793
+ var getPathLength6 = (points) => points.slice(1).reduce((length, point, pointIndex) => {
14384
14794
  const previousPoint = points[pointIndex];
14385
14795
  return length + Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
14386
14796
  }, 0);
@@ -14452,8 +14862,8 @@ var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
14452
14862
  netLabels: this.input.netLabelPlacements
14453
14863
  }).length;
14454
14864
  const isSimplerEquivalentReroute = candidateOverlapCount < initialRerouteOverlapCount && Math.abs(
14455
- getPathLength4(simplifiedCandidate) - getPathLength4(reroutedPath)
14456
- ) < PATH_LENGTH_EPSILON2 && simplifiedCandidate.length < reroutedPath.length && preservesLabelAnchors(
14865
+ getPathLength6(simplifiedCandidate) - getPathLength6(reroutedPath)
14866
+ ) < PATH_LENGTH_EPSILON3 && simplifiedCandidate.length < reroutedPath.length && preservesLabelAnchors(
14457
14867
  this.input.netLabelPlacements,
14458
14868
  [completedReroute.initialTrace],
14459
14869
  [candidateTrace]
@@ -14471,7 +14881,7 @@ var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
14471
14881
  ({ label }) => `${label.globalConnNetId}:${label.netId}`
14472
14882
  )
14473
14883
  );
14474
- const initialPathLength = getPathLength4(tracePath);
14884
+ const initialPathLength = getPathLength6(tracePath);
14475
14885
  const validCandidates = [...candidateByPath.values()].filter(
14476
14886
  (candidatePath) => {
14477
14887
  const candidateTrace = { ...trace, tracePath: candidatePath };
@@ -14483,7 +14893,7 @@ var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
14483
14893
  ({ label }) => initialOverlapIds.has(`${label.globalConnNetId}:${label.netId}`)
14484
14894
  );
14485
14895
  const reducesCollisions = candidateOverlaps.length < initialOverlaps.length;
14486
- const simplifiesGeometry = candidateOverlaps.length === initialOverlaps.length && getPathLength4(candidatePath) <= initialPathLength + PATH_LENGTH_EPSILON2 && candidatePath.length < tracePath.length;
14896
+ const simplifiesGeometry = candidateOverlaps.length === initialOverlaps.length && getPathLength6(candidatePath) <= initialPathLength + PATH_LENGTH_EPSILON3 && candidatePath.length < tracePath.length;
14487
14897
  return candidateOnlyKeepsExistingOverlaps && (reducesCollisions || simplifiesGeometry) && preservesLabelAnchors(
14488
14898
  this.input.netLabelPlacements,
14489
14899
  [trace],
@@ -14498,7 +14908,7 @@ var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
14498
14908
  validCandidates.sort((a, b) => {
14499
14909
  const overlapDifference = getOverlapCount(a) - getOverlapCount(b);
14500
14910
  if (overlapDifference !== 0) return overlapDifference;
14501
- return getPathLength4(a) - getPathLength4(b) || a.length - b.length;
14911
+ return getPathLength6(a) - getPathLength6(b) || a.length - b.length;
14502
14912
  });
14503
14913
  const bestCandidate = validCandidates[0];
14504
14914
  if (!bestCandidate) return;
@@ -14741,7 +15151,7 @@ var getInlineBounds = (placement) => {
14741
15151
  maxY: placement.center.y + renderedHeight / 2
14742
15152
  };
14743
15153
  };
14744
- var pointsEqual3 = (a, b) => Math.abs(a.x - b.x) <= POINT_EPSILON && Math.abs(a.y - b.y) <= POINT_EPSILON;
15154
+ var pointsEqual4 = (a, b) => Math.abs(a.x - b.x) <= POINT_EPSILON && Math.abs(a.y - b.y) <= POINT_EPSILON;
14745
15155
  var pathIntersectsBounds = (path, bounds) => {
14746
15156
  for (let index = 0; index < path.length - 1; index++) {
14747
15157
  const start = path[index];
@@ -14837,13 +15247,13 @@ var findConnectorTraceIndex = (label, traces) => traces.findIndex((trace) => {
14837
15247
  const first = trace.tracePath[0];
14838
15248
  const last = trace.tracePath.at(-1);
14839
15249
  return Boolean(
14840
- first && pointsEqual3(first, label.anchorPoint) || last && pointsEqual3(last, label.anchorPoint)
15250
+ first && pointsEqual4(first, label.anchorPoint) || last && pointsEqual4(last, label.anchorPoint)
14841
15251
  );
14842
15252
  });
14843
15253
  var canAddConnectorAtAnchor = (label, traces, pinMap) => {
14844
15254
  if (label.pinIds.some((pinId) => {
14845
15255
  const pin = pinMap[pinId];
14846
- return pin && pointsEqual3(pin, label.anchorPoint);
15256
+ return pin && pointsEqual4(pin, label.anchorPoint);
14847
15257
  })) {
14848
15258
  return true;
14849
15259
  }
@@ -14853,8 +15263,8 @@ var canAddConnectorAtAnchor = (label, traces, pinMap) => {
14853
15263
  };
14854
15264
  var moveConnectorEndpoint = (trace, oldAnchor, newAnchor) => {
14855
15265
  const tracePath = trace.tracePath.map((point) => ({ ...point }));
14856
- if (pointsEqual3(tracePath[0], oldAnchor)) tracePath[0] = newAnchor;
14857
- if (pointsEqual3(tracePath.at(-1), oldAnchor)) {
15266
+ if (pointsEqual4(tracePath[0], oldAnchor)) tracePath[0] = newAnchor;
15267
+ if (pointsEqual4(tracePath.at(-1), oldAnchor)) {
14858
15268
  tracePath[tracePath.length - 1] = newAnchor;
14859
15269
  }
14860
15270
  return { ...trace, tracePath };
@@ -15321,14 +15731,14 @@ var pathIntersectsRenderedLabel = (path, label) => {
15321
15731
 
15322
15732
  // lib/solvers/InlineNetLabelSolver/restoreReroutesAroundSupersededLabels.ts
15323
15733
  var EPS15 = 1e-6;
15324
- var getPathLength5 = (path) => path.slice(1).reduce((length, point, pointIndex) => {
15734
+ var getPathLength7 = (path) => path.slice(1).reduce((length, point, pointIndex) => {
15325
15735
  const previousPoint = path[pointIndex];
15326
15736
  return length + Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
15327
15737
  }, 0);
15328
15738
  var pathsEqual = (first, second) => first.length === second.length && first.every(
15329
15739
  (point, index) => Math.abs(point.x - second[index].x) <= EPS15 && Math.abs(point.y - second[index].y) <= EPS15
15330
15740
  );
15331
- var isStrictlySimpler = (candidate, current) => candidate.length < current.length && getPathLength5(candidate) <= getPathLength5(current) + EPS15;
15741
+ var isStrictlySimpler = (candidate, current) => candidate.length < current.length && getPathLength7(candidate) <= getPathLength7(current) + EPS15;
15332
15742
  var getIntersectionKeys = (path, otherPath) => {
15333
15743
  const intersections = /* @__PURE__ */ new Set();
15334
15744
  for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
@@ -16291,22 +16701,22 @@ var getCollisionLimitedTerminalStubEnd = ({
16291
16701
 
16292
16702
  // lib/solvers/NetLabelTraceRecovery/doesTraceRecoveryPathConflict.ts
16293
16703
  import { doSegmentsIntersect as doSegmentsIntersect4 } from "@tscircuit/math-utils";
16294
- var EPSILON = 1e-6;
16704
+ var EPSILON2 = 1e-6;
16295
16705
  var isStrictlyBetween2 = ({
16296
16706
  coordinate,
16297
16707
  segmentStartCoordinate,
16298
16708
  segmentEndCoordinate
16299
- }) => coordinate > Math.min(segmentStartCoordinate, segmentEndCoordinate) + EPSILON && coordinate < Math.max(segmentStartCoordinate, segmentEndCoordinate) - EPSILON;
16709
+ }) => coordinate > Math.min(segmentStartCoordinate, segmentEndCoordinate) + EPSILON2 && coordinate < Math.max(segmentStartCoordinate, segmentEndCoordinate) - EPSILON2;
16300
16710
  var isStrictInteriorPerpendicularCrossing = ({
16301
16711
  firstSegmentStart,
16302
16712
  firstSegmentEnd,
16303
16713
  secondSegmentStart,
16304
16714
  secondSegmentEnd
16305
16715
  }) => {
16306
- const firstIsHorizontal = Math.abs(firstSegmentStart.y - firstSegmentEnd.y) <= EPSILON;
16307
- const firstIsVertical = Math.abs(firstSegmentStart.x - firstSegmentEnd.x) <= EPSILON;
16308
- const secondIsHorizontal = Math.abs(secondSegmentStart.y - secondSegmentEnd.y) <= EPSILON;
16309
- const secondIsVertical = Math.abs(secondSegmentStart.x - secondSegmentEnd.x) <= EPSILON;
16716
+ const firstIsHorizontal = Math.abs(firstSegmentStart.y - firstSegmentEnd.y) <= EPSILON2;
16717
+ const firstIsVertical = Math.abs(firstSegmentStart.x - firstSegmentEnd.x) <= EPSILON2;
16718
+ const secondIsHorizontal = Math.abs(secondSegmentStart.y - secondSegmentEnd.y) <= EPSILON2;
16719
+ const secondIsVertical = Math.abs(secondSegmentStart.x - secondSegmentEnd.x) <= EPSILON2;
16310
16720
  let horizontalStart;
16311
16721
  let horizontalEnd;
16312
16722
  if (firstIsHorizontal) {
@@ -16465,7 +16875,7 @@ var reduceTraceCrossings = ({
16465
16875
  globalConnNetId,
16466
16876
  otherTraces
16467
16877
  });
16468
- let bestPathLength = getPathLength2(tracePath);
16878
+ let bestPathLength = getPathLength4(tracePath);
16469
16879
  for (const candidate of getEndpointAlignedSegmentCandidates(tracePath)) {
16470
16880
  if (!isCandidateValid(candidate)) continue;
16471
16881
  const crossingCount = countOtherNetCrossings2({
@@ -16473,7 +16883,7 @@ var reduceTraceCrossings = ({
16473
16883
  globalConnNetId,
16474
16884
  otherTraces
16475
16885
  });
16476
- const pathLength = getPathLength2(candidate);
16886
+ const pathLength = getPathLength4(candidate);
16477
16887
  if (crossingCount < bestCrossingCount || crossingCount === bestCrossingCount && pathLength < bestPathLength) {
16478
16888
  bestPath = candidate;
16479
16889
  bestCrossingCount = crossingCount;