@tscircuit/schematic-trace-solver 0.0.175 → 0.0.177

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 (46) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.js +570 -17
  3. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +5 -1
  4. package/lib/solvers/MspConnectionPairSolver/getGroundConnectionPolicy.ts +13 -0
  5. package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +2 -0
  6. package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +748 -15
  7. package/lib/solvers/SameNetJunctionAlignmentSolver/findNearestSharedPinExitRail.ts +140 -0
  8. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +3 -5
  9. package/package.json +1 -1
  10. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +2 -2
  11. package/tests/bug-reports/bug-report-20260707T134549Z/bug-report-20260707T134549Z.test.ts +9 -0
  12. package/tests/bug-reports/bug-report-20260707T230831Z/__snapshots__/bug-report-20260707T230831Z.snap.svg +2 -2
  13. package/tests/bug-reports/bug-report-20260717T042845Z/__snapshots__/bug-report-20260717T042845Z.snap.svg +2 -2
  14. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
  15. package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +2 -2
  16. package/tests/bug-reports/bug-report-20260730T061837Z/bug-report-20260730T061837Z.test.ts +8 -4
  17. package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +2 -2
  18. package/tests/bug-reports/bug-report-20260805T061316Z/__snapshots__/bug-report-20260805T061316Z.snap.svg +2 -2
  19. package/tests/bug-reports/bug-report-20260806T061548Z/__snapshots__/bug-report-20260806T061548Z.snap.svg +2 -2
  20. package/tests/bug-reports/bug-report-20260825T045913Z/__snapshots__/bug-report-20260825T045913Z.snap.svg +2 -2
  21. package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +6 -6
  22. package/tests/bug-reports/bug-report-20260901T134241Z/__snapshots__/bug-report-20260901T134241Z.snap.svg +7 -7
  23. package/tests/bug-reports/bug-report-20260901T134241Z/bug-report-20260901T134241Z.test.ts +48 -0
  24. package/tests/examples/__snapshots__/example51.snap.svg +2 -2
  25. package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +10 -10
  26. package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +2 -2
  27. package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +2 -2
  28. package/tests/repros/__snapshots__/repro-core-ground-inline-label-fallback.snap.svg +10 -10
  29. package/tests/repros/__snapshots__/repro-example35-minimize-trace-crossing.snap.svg +2 -2
  30. package/tests/repros/__snapshots__/repro-isolated-rs485-isow7841.snap.svg +18 -18
  31. package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +18 -18
  32. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +10 -10
  33. package/tests/repros/__snapshots__/repro-powerbank-3v-system-power.snap.svg +2 -2
  34. package/tests/repros/__snapshots__/repro-smartwatch-power-sheet.snap.svg +451 -0
  35. package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +2 -2
  36. package/tests/repros/__snapshots__/repro-tida-01389-hbridge.snap.svg +2 -2
  37. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +2 -2
  38. package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +2 -2
  39. package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +2 -2
  40. package/tests/repros/assets/repro-smartwatch-power-sheet.input.json +1112 -0
  41. package/tests/repros/repro-isolated-rs485-isow7841.test.ts +4 -1
  42. package/tests/repros/repro-powerbank-3v-system-power.test.ts +24 -0
  43. package/tests/repros/repro-smartwatch-power-sheet.test.ts +27 -0
  44. package/tests/repros/repro-tida-01389-hbridge.test.ts +24 -0
  45. package/tests/solvers/MspConnectionPairSolver/ground-direct-connection-groups.test.ts +11 -0
  46. package/tests/solvers/SameNetJunctionAlignmentSolver/parallel-load-rails.test.ts +1 -0
package/dist/index.js CHANGED
@@ -396,6 +396,12 @@ var getGroundConnectionPolicy = (inputProblem) => {
396
396
  );
397
397
  });
398
398
  const areSeparateExplicitGroundIslands = (firstPinId, secondPinId) => {
399
+ const firstChipId = pins.get(firstPinId)?.chip.chipId;
400
+ const secondChipId = pins.get(secondPinId)?.chip.chipId;
401
+ const pinsShareChip = firstChipId !== void 0 && firstChipId === secondChipId;
402
+ if (pinsShareChip && !physicalConnMap.getNetConnectedToId(firstPinId) && !physicalConnMap.getNetConnectedToId(secondPinId)) {
403
+ return false;
404
+ }
399
405
  const firstGlobalNetId = netConnMap.getNetConnectedToId(firstPinId);
400
406
  const secondGlobalNetId = netConnMap.getNetConnectedToId(secondPinId);
401
407
  if (!firstGlobalNetId || firstGlobalNetId !== secondGlobalNetId || !explicitlyWiredGroundNetIds.has(firstGlobalNetId)) {
@@ -8866,6 +8872,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8866
8872
  inputProblem;
8867
8873
  traces;
8868
8874
  netLabelPlacements;
8875
+ /** Exact provenance for connector traces created by this solver. */
8876
+ netLabelConnectorTraceIds = /* @__PURE__ */ new Set();
8869
8877
  outputNetLabelPlacements;
8870
8878
  queuedLabelIndices = [];
8871
8879
  currentLabelIndex = null;
@@ -9116,6 +9124,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9116
9124
  };
9117
9125
  this.traces.push(connectorTrace);
9118
9126
  this.traceMap[mspPairId] = connectorTrace;
9127
+ this.netLabelConnectorTraceIds.add(mspPairId);
9119
9128
  }
9120
9129
  finish() {
9121
9130
  this.currentLabelIndex = null;
@@ -9438,7 +9447,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
9438
9447
  foundConnectedTrace = false;
9439
9448
  for (const trace of Object.values(this.traceMap)) {
9440
9449
  if (connectedTraceIds.has(trace.mspPairId)) continue;
9441
- if (trace.mspPairId.startsWith("available-net-orientation-")) continue;
9450
+ if (this.netLabelConnectorTraceIds.has(trace.mspPairId)) continue;
9442
9451
  if (trace.globalConnNetId !== label.globalConnNetId) continue;
9443
9452
  if (!trace.pinIds.some((pinId) => connectedPinIds.has(pinId)))
9444
9453
  continue;
@@ -12673,11 +12682,99 @@ var pathEntersAnyNetLabel = ({
12673
12682
  return false;
12674
12683
  };
12675
12684
 
12685
+ // lib/solvers/SameNetJunctionAlignmentSolver/findNearestSharedPinExitRail.ts
12686
+ var getPinFacingAxis = (pin) => {
12687
+ if (!pin._facingDirection) return null;
12688
+ return dir(pin._facingDirection).x === 0 ? "y" : "x";
12689
+ };
12690
+ var isCoordinateOnPinFacingSide = ({
12691
+ coordinate,
12692
+ axis,
12693
+ pin
12694
+ }) => {
12695
+ if (!pin._facingDirection) return false;
12696
+ const facingVector = dir(pin._facingDirection);
12697
+ return (coordinate - pin[axis]) * facingVector[axis] > 0;
12698
+ };
12699
+ var getSegmentAxis2 = (start, end) => {
12700
+ if (isHorizontal2(start, end)) return "x";
12701
+ if (isVertical2(start, end)) return "y";
12702
+ return null;
12703
+ };
12704
+ var getAxisAlignedSegments = (path) => {
12705
+ const segments = [];
12706
+ for (let index = 0; index < path.length - 1; index++) {
12707
+ const start = path[index];
12708
+ const end = path[index + 1];
12709
+ const axis = getSegmentAxis2(start, end);
12710
+ if (!axis) continue;
12711
+ const coordinateAxis = axis === "x" ? "y" : "x";
12712
+ segments.push({ start, end, axis, coordinate: start[coordinateAxis] });
12713
+ }
12714
+ return segments;
12715
+ };
12716
+ var getPinExitPoint = ({
12717
+ pin,
12718
+ departureAxis,
12719
+ railCoordinate
12720
+ }) => departureAxis === "x" ? { x: railCoordinate, y: pin.y } : { x: pin.x, y: railCoordinate };
12721
+ var findNearestSharedPinExitRail = ({
12722
+ donorPath,
12723
+ branchDepartureSegment,
12724
+ sharedPin,
12725
+ adjacentPin
12726
+ }) => {
12727
+ const departureAxis = getPinFacingAxis(sharedPin);
12728
+ if (!departureAxis) return null;
12729
+ const railAxis = departureAxis === "x" ? "y" : "x";
12730
+ const branchDeparture = getAxisAlignedSegments(branchDepartureSegment)[0];
12731
+ if (!branchDeparture || branchDeparture.axis !== departureAxis) return null;
12732
+ let nearestRail = null;
12733
+ let nearestDistance = Number.POSITIVE_INFINITY;
12734
+ for (const segment of getAxisAlignedSegments(donorPath)) {
12735
+ if (segment.axis !== railAxis) continue;
12736
+ if (!isCoordinateOnPinFacingSide({
12737
+ coordinate: segment.coordinate,
12738
+ axis: departureAxis,
12739
+ pin: sharedPin
12740
+ })) {
12741
+ continue;
12742
+ }
12743
+ const sharedPinExit = getPinExitPoint({
12744
+ pin: sharedPin,
12745
+ departureAxis,
12746
+ railCoordinate: segment.coordinate
12747
+ });
12748
+ const adjacentPinExit = getPinExitPoint({
12749
+ pin: adjacentPin,
12750
+ departureAxis,
12751
+ railCoordinate: segment.coordinate
12752
+ });
12753
+ if (!tracePathContainsPoint([segment.start, segment.end], sharedPinExit) || !tracePathContainsPoint([segment.start, segment.end], adjacentPinExit) || !tracePathContainsPoint(
12754
+ [branchDeparture.start, branchDeparture.end],
12755
+ sharedPinExit
12756
+ )) {
12757
+ continue;
12758
+ }
12759
+ const distance7 = Math.abs(segment.coordinate - sharedPin[departureAxis]);
12760
+ if (distance7 >= nearestDistance) continue;
12761
+ nearestRail = { segment, sharedPinExit };
12762
+ nearestDistance = distance7;
12763
+ }
12764
+ return nearestRail;
12765
+ };
12766
+
12676
12767
  // lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
12677
12768
  var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
12678
12769
  var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
12679
12770
  var MAX_SHARED_PIN_RAIL_OFFSET = 0.05;
12771
+ var MAX_SHARED_ENDPOINT_RAIL_OFFSET = 0.8;
12680
12772
  var MIN_RETURN_STEM_LENGTH = 0.05;
12773
+ var getSegmentAxis3 = (start, end) => {
12774
+ if (isHorizontal2(start, end)) return "x";
12775
+ if (isVertical2(start, end)) return "y";
12776
+ return null;
12777
+ };
12681
12778
  var getSharedPin = ({
12682
12779
  donorTrace,
12683
12780
  branchTrace
@@ -12830,11 +12927,366 @@ var getAlignedBranchPath = ({
12830
12927
  if (branchTrace.pins[0].pinId === sharedPin.pinId) return sharedToOther;
12831
12928
  return [...sharedToOther].reverse();
12832
12929
  };
12930
+ var getAlignedParallelPinExitPath = ({
12931
+ donorTrace,
12932
+ branchTrace
12933
+ }) => {
12934
+ const sharedPin = getSharedPin({ donorTrace, branchTrace });
12935
+ if (!sharedPin) return null;
12936
+ const adjacentPin = getOtherPin({ trace: donorTrace, sharedPin });
12937
+ const targetPin = getOtherPin({ trace: branchTrace, sharedPin });
12938
+ if (!adjacentPin || !targetPin) return null;
12939
+ const facing = sharedPin._facingDirection;
12940
+ const departureAxis = getPinFacingAxis(sharedPin);
12941
+ if (!facing || !departureAxis) return null;
12942
+ const stemAxis = departureAxis === "x" ? "y" : "x";
12943
+ if (adjacentPin.chipId !== sharedPin.chipId || adjacentPin._facingDirection !== facing || !nearlyEqual(adjacentPin[departureAxis], sharedPin[departureAxis]) || getPinFacingAxis(targetPin) !== stemAxis) {
12944
+ return null;
12945
+ }
12946
+ const adjacentPinOffset = Math.abs(
12947
+ adjacentPin[stemAxis] - sharedPin[stemAxis]
12948
+ );
12949
+ if (adjacentPinOffset > MAX_ALIGNED_LOAD_PIN_OFFSET && !nearlyEqual(adjacentPinOffset, MAX_ALIGNED_LOAD_PIN_OFFSET)) {
12950
+ return null;
12951
+ }
12952
+ const branchStartsAtSharedPin = branchTrace.pins[0].pinId === sharedPin.pinId;
12953
+ let sharedToTargetPath = simplifyPath(branchTrace.tracePath);
12954
+ if (!branchStartsAtSharedPin) sharedToTargetPath.reverse();
12955
+ if (sharedToTargetPath.length < 2) return null;
12956
+ const branchDepartureSegment = [
12957
+ sharedToTargetPath[0],
12958
+ sharedToTargetPath[1]
12959
+ ];
12960
+ const sharedExitRail = findNearestSharedPinExitRail({
12961
+ donorPath: donorTrace.tracePath,
12962
+ branchDepartureSegment,
12963
+ sharedPin,
12964
+ adjacentPin
12965
+ });
12966
+ if (!sharedExitRail) return null;
12967
+ const alignedRailCoordinate = adjacentPin[stemAxis];
12968
+ if (!isCoordinateOnPinFacingSide({
12969
+ coordinate: alignedRailCoordinate,
12970
+ axis: stemAxis,
12971
+ pin: targetPin
12972
+ })) {
12973
+ return null;
12974
+ }
12975
+ const alignedStemExit = {
12976
+ ...sharedExitRail.sharedPinExit,
12977
+ [stemAxis]: alignedRailCoordinate
12978
+ };
12979
+ const targetApproach = {
12980
+ x: targetPin.x,
12981
+ y: targetPin.y,
12982
+ [stemAxis]: alignedRailCoordinate
12983
+ };
12984
+ const candidate = simplifyPath([
12985
+ { x: sharedPin.x, y: sharedPin.y },
12986
+ sharedExitRail.sharedPinExit,
12987
+ alignedStemExit,
12988
+ targetApproach,
12989
+ { x: targetPin.x, y: targetPin.y }
12990
+ ]);
12991
+ return branchStartsAtSharedPin ? candidate : candidate.reverse();
12992
+ };
12993
+ var getAlignedSharedEndpointRailPath = ({
12994
+ donorTrace,
12995
+ branchTrace,
12996
+ netLabelConnectorTraceIds
12997
+ }) => {
12998
+ if (netLabelConnectorTraceIds.has(donorTrace.mspPairId) || netLabelConnectorTraceIds.has(branchTrace.mspPairId)) {
12999
+ return null;
13000
+ }
13001
+ const sharedPin = getSharedPin({ donorTrace, branchTrace });
13002
+ if (!sharedPin) return null;
13003
+ const pathFromShared = (trace) => {
13004
+ const path = simplifyPath(trace.tracePath);
13005
+ return trace.pins[0].pinId === sharedPin.pinId ? path : [...path].reverse();
13006
+ };
13007
+ const donorPath = pathFromShared(donorTrace);
13008
+ const branchPath = pathFromShared(branchTrace);
13009
+ if (donorPath.length < 3 || branchPath.length < 4) return null;
13010
+ const donorDepartureAxis = getSegmentAxis3(donorPath[0], donorPath[1]);
13011
+ const branchDepartureAxis = getSegmentAxis3(branchPath[0], branchPath[1]);
13012
+ if (!donorDepartureAxis || donorDepartureAxis !== branchDepartureAxis) {
13013
+ return null;
13014
+ }
13015
+ const donorDeparture = donorPath[1][donorDepartureAxis] - donorPath[0][donorDepartureAxis];
13016
+ const branchDeparture = branchPath[1][branchDepartureAxis] - branchPath[0][branchDepartureAxis];
13017
+ if (Math.sign(donorDeparture) !== Math.sign(branchDeparture)) return null;
13018
+ const donorRailAxis = getSegmentAxis3(donorPath[1], donorPath[2]);
13019
+ const branchRailAxis = getSegmentAxis3(branchPath[1], branchPath[2]);
13020
+ if (!donorRailAxis || donorRailAxis !== branchRailAxis || donorRailAxis === donorDepartureAxis) {
13021
+ return null;
13022
+ }
13023
+ let branchParallelRailCount = 0;
13024
+ for (let index = 1; index < branchPath.length - 2; index++) {
13025
+ if (getSegmentAxis3(branchPath[index], branchPath[index + 1]) === branchRailAxis) {
13026
+ branchParallelRailCount++;
13027
+ }
13028
+ }
13029
+ if (branchParallelRailCount !== 1) return null;
13030
+ const railCoordinateAxis = donorRailAxis === "x" ? "y" : "x";
13031
+ const donorCoordinate = donorPath[1][railCoordinateAxis];
13032
+ const branchCoordinate = branchPath[1][railCoordinateAxis];
13033
+ const railOffset = Math.abs(donorCoordinate - branchCoordinate);
13034
+ if (nearlyEqual(donorCoordinate, branchCoordinate) || railOffset > MAX_SHARED_ENDPOINT_RAIL_OFFSET) {
13035
+ return null;
13036
+ }
13037
+ const donorRange = [
13038
+ donorPath[1][donorRailAxis],
13039
+ donorPath[2][donorRailAxis]
13040
+ ].sort((a, b) => a - b);
13041
+ const branchRange = [
13042
+ branchPath[1][branchRailAxis],
13043
+ branchPath[2][branchRailAxis]
13044
+ ].sort((a, b) => a - b);
13045
+ const railsTouchAfterAlignment = Math.min(donorRange[1], branchRange[1]) >= Math.max(donorRange[0], branchRange[0]) - 1e-6;
13046
+ if (!railsTouchAfterAlignment) return null;
13047
+ const candidateFromShared = simplifyPath(
13048
+ branchPath.map(
13049
+ (point, index) => index === 1 || index === 2 ? { ...point, [railCoordinateAxis]: donorCoordinate } : point
13050
+ )
13051
+ );
13052
+ return branchTrace.pins[0].pinId === sharedPin.pinId ? candidateFromShared : candidateFromShared.reverse();
13053
+ };
13054
+ var getAlignedAttachedLabelConnectorPath = ({
13055
+ donorTrace,
13056
+ connectorTrace,
13057
+ netLabelConnectorTraceIds
13058
+ }) => {
13059
+ if (netLabelConnectorTraceIds.has(donorTrace.mspPairId) || !netLabelConnectorTraceIds.has(connectorTrace.mspPairId)) {
13060
+ return null;
13061
+ }
13062
+ const findCandidate = (connectorPath) => {
13063
+ if (connectorPath.length < 3) return null;
13064
+ const junction = connectorPath[0];
13065
+ const turn = connectorPath[1];
13066
+ const railEnd = connectorPath[2];
13067
+ const departureAxis = getSegmentAxis3(junction, turn);
13068
+ const railAxis = getSegmentAxis3(turn, railEnd);
13069
+ if (!departureAxis || !railAxis || departureAxis === railAxis) return null;
13070
+ const donorSegmentIndex = donorTrace.tracePath.findIndex(
13071
+ (point, index, path) => {
13072
+ const next = path[index + 1];
13073
+ return next && getSegmentAxis3(point, next) === departureAxis && tracePathContainsPoint([point, next], junction) && tracePathContainsPoint([point, next], turn);
13074
+ }
13075
+ );
13076
+ if (donorSegmentIndex < 0) return null;
13077
+ const donorSegmentStart = donorTrace.tracePath[donorSegmentIndex];
13078
+ const donorSegmentEnd = donorTrace.tracePath[donorSegmentIndex + 1];
13079
+ if (nearlyEqual(junction.x, donorSegmentStart.x) && nearlyEqual(junction.y, donorSegmentStart.y) || nearlyEqual(junction.x, donorSegmentEnd.x) && nearlyEqual(junction.y, donorSegmentEnd.y)) {
13080
+ return null;
13081
+ }
13082
+ const adjacentDonorRails = [
13083
+ donorSegmentIndex > 0 ? [donorTrace.tracePath[donorSegmentIndex - 1], donorSegmentStart] : null,
13084
+ donorSegmentIndex + 2 < donorTrace.tracePath.length ? [donorSegmentEnd, donorTrace.tracePath[donorSegmentIndex + 2]] : null
13085
+ ].flatMap(
13086
+ (segment) => segment && getSegmentAxis3(segment[0], segment[1]) === railAxis ? [segment] : []
13087
+ );
13088
+ if (adjacentDonorRails.length === 0) return null;
13089
+ const railCoordinateAxis = railAxis === "x" ? "y" : "x";
13090
+ const connectorRailCoordinate = turn[railCoordinateAxis];
13091
+ const connectorRange = [turn[railAxis], railEnd[railAxis]].sort(
13092
+ (a, b) => a - b
13093
+ );
13094
+ const donorRail = adjacentDonorRails.map((segment) => ({
13095
+ segment,
13096
+ coordinate: segment[0][railCoordinateAxis],
13097
+ offset: Math.abs(
13098
+ segment[0][railCoordinateAxis] - connectorRailCoordinate
13099
+ )
13100
+ })).filter(({ segment, offset }) => {
13101
+ if (offset > MAX_SHARED_ENDPOINT_RAIL_OFFSET) return false;
13102
+ const donorRange = [segment[0][railAxis], segment[1][railAxis]].sort(
13103
+ (a, b) => a - b
13104
+ );
13105
+ const overlapStart = Math.max(donorRange[0], connectorRange[0]);
13106
+ const overlapEnd = Math.min(donorRange[1], connectorRange[1]);
13107
+ return overlapEnd >= overlapStart - 1e-6 && Math.abs(overlapEnd - overlapStart) <= 1e-6;
13108
+ }).sort((a, b) => a.offset - b.offset)[0];
13109
+ if (!donorRail || nearlyEqual(donorRail.offset, 0)) return null;
13110
+ return simplifyPath(
13111
+ connectorPath.map(
13112
+ (point, index) => index === 1 || index === 2 ? { ...point, [railCoordinateAxis]: donorRail.coordinate } : point
13113
+ )
13114
+ );
13115
+ };
13116
+ const forwardPath = simplifyPath(connectorTrace.tracePath);
13117
+ const forwardCandidate = findCandidate(forwardPath);
13118
+ if (forwardCandidate) return forwardCandidate;
13119
+ const reverseCandidate = findCandidate([...forwardPath].reverse());
13120
+ return reverseCandidate ? reverseCandidate.reverse() : null;
13121
+ };
13122
+ var pathsIntersect = (firstPath, secondPath) => {
13123
+ if (firstPath.some((point) => tracePathContainsPoint(secondPath, point)) || secondPath.some((point) => tracePathContainsPoint(firstPath, point))) {
13124
+ return true;
13125
+ }
13126
+ for (let firstIndex = 1; firstIndex < firstPath.length; firstIndex++) {
13127
+ for (let secondIndex = 1; secondIndex < secondPath.length; secondIndex++) {
13128
+ if (getSegmentIntersection3(
13129
+ firstPath[firstIndex - 1],
13130
+ firstPath[firstIndex],
13131
+ secondPath[secondIndex - 1],
13132
+ secondPath[secondIndex]
13133
+ )) {
13134
+ return true;
13135
+ }
13136
+ }
13137
+ }
13138
+ return false;
13139
+ };
13140
+ var preserveAttachedLabelConnectorJunctions = ({
13141
+ originalTrace,
13142
+ candidateTrace,
13143
+ traces,
13144
+ netLabelConnectorTraceIds
13145
+ }) => {
13146
+ let outputTraces = traces.map(
13147
+ (trace) => trace.mspPairId === originalTrace.mspPairId ? candidateTrace : trace
13148
+ );
13149
+ for (const connector of traces) {
13150
+ if (connector.mspPairId === originalTrace.mspPairId || !netLabelConnectorTraceIds.has(connector.mspPairId) || connector.globalConnNetId !== originalTrace.globalConnNetId || !pathsIntersect(originalTrace.tracePath, connector.tracePath) || pathsIntersect(candidateTrace.tracePath, connector.tracePath)) {
13151
+ continue;
13152
+ }
13153
+ const endpointIndex = [0, connector.tracePath.length - 1].find(
13154
+ (index) => tracePathContainsPoint(
13155
+ originalTrace.tracePath,
13156
+ connector.tracePath[index]
13157
+ )
13158
+ );
13159
+ if (endpointIndex === void 0) return null;
13160
+ const originalJunction = connector.tracePath[endpointIndex];
13161
+ const movedJunction = getMovedAnchorPointForReroute(
13162
+ originalJunction,
13163
+ originalTrace.tracePath,
13164
+ candidateTrace.tracePath
13165
+ );
13166
+ if (!movedJunction) return null;
13167
+ const neighborIndex = endpointIndex === 0 ? 1 : endpointIndex - 1;
13168
+ const neighbor = connector.tracePath[neighborIndex];
13169
+ if (!neighbor || getSegmentAxis3(originalJunction, neighbor) !== getSegmentAxis3(movedJunction, neighbor)) {
13170
+ return null;
13171
+ }
13172
+ const movedConnector = {
13173
+ ...connector,
13174
+ tracePath: connector.tracePath.map(
13175
+ (point, index) => index === endpointIndex ? movedJunction : point
13176
+ )
13177
+ };
13178
+ if (!pathsIntersect(candidateTrace.tracePath, movedConnector.tracePath)) {
13179
+ return null;
13180
+ }
13181
+ outputTraces = outputTraces.map(
13182
+ (trace) => trace.mspPairId === connector.mspPairId ? movedConnector : trace
13183
+ );
13184
+ }
13185
+ return outputTraces;
13186
+ };
13187
+ var getPerpendicularPinRail = ({
13188
+ trace,
13189
+ endpointPin
13190
+ }) => {
13191
+ const otherPin = getOtherPin({ trace, sharedPin: endpointPin });
13192
+ if (!otherPin) return null;
13193
+ const startsAtEndpoint = trace.pins[0].pinId === endpointPin.pinId;
13194
+ let endpointToOtherPath = simplifyPath(trace.tracePath);
13195
+ if (!startsAtEndpoint) endpointToOtherPath.reverse();
13196
+ if (endpointToOtherPath.length !== 3) return null;
13197
+ const [endpoint, railPoint, other] = endpointToOtherPath;
13198
+ const endpointFacing = endpointPin._facingDirection;
13199
+ if (endpointFacing === "y+" || endpointFacing === "y-") {
13200
+ if (otherPin._facingDirection !== "x+" && otherPin._facingDirection !== "x-") {
13201
+ return null;
13202
+ }
13203
+ if (!isVertical2(endpoint, railPoint) || !isHorizontal2(railPoint, other) || !railIsOnFacingSide({ railY: railPoint.y, pin: endpointPin })) {
13204
+ return null;
13205
+ }
13206
+ const approachesOtherFromFacingSide2 = otherPin._facingDirection === "x-" ? railPoint.x < otherPin.x : railPoint.x > otherPin.x;
13207
+ return approachesOtherFromFacingSide2 ? { railCoordinate: railPoint.y } : null;
13208
+ }
13209
+ if (endpointFacing !== "x+" && endpointFacing !== "x-") return null;
13210
+ if (otherPin._facingDirection !== "y+" && otherPin._facingDirection !== "y-") {
13211
+ return null;
13212
+ }
13213
+ if (!isHorizontal2(endpoint, railPoint) || !isVertical2(railPoint, other)) {
13214
+ return null;
13215
+ }
13216
+ const railIsOnEndpointFacingSide = endpointFacing === "x+" ? railPoint.x > endpointPin.x : railPoint.x < endpointPin.x;
13217
+ const approachesOtherFromFacingSide = otherPin._facingDirection === "y+" ? railPoint.y > otherPin.y : railPoint.y < otherPin.y;
13218
+ return railIsOnEndpointFacingSide && approachesOtherFromFacingSide ? { railCoordinate: railPoint.x } : null;
13219
+ };
13220
+ var getAlignedPerpendicularEndpointBusPath = ({
13221
+ donorTrace,
13222
+ branchTrace,
13223
+ traces
13224
+ }) => {
13225
+ const sharedPin = getSharedPin({ donorTrace, branchTrace });
13226
+ if (!sharedPin) return null;
13227
+ const returnPin = getOtherPin({ trace: branchTrace, sharedPin });
13228
+ if (!returnPin) return null;
13229
+ const getOtherSameNetTracesAtPin = (pin) => traces.filter(
13230
+ (trace) => trace.mspPairId !== branchTrace.mspPairId && trace.globalConnNetId === branchTrace.globalConnNetId && trace.pinIds.includes(pin.pinId)
13231
+ );
13232
+ const sharedDonorTraces = getOtherSameNetTracesAtPin(sharedPin);
13233
+ const returnDonorTraces = getOtherSameNetTracesAtPin(returnPin);
13234
+ if (sharedDonorTraces.length !== 1 || sharedDonorTraces[0].mspPairId !== donorTrace.mspPairId || returnDonorTraces.length !== 1) {
13235
+ return null;
13236
+ }
13237
+ const sharedRail = getPerpendicularPinRail({
13238
+ trace: donorTrace,
13239
+ endpointPin: sharedPin
13240
+ });
13241
+ if (!sharedRail) return null;
13242
+ const facing = sharedPin._facingDirection;
13243
+ const verticalEndpoints = facing === "y+" || facing === "y-";
13244
+ const horizontalEndpoints = facing === "x+" || facing === "x-";
13245
+ if (!verticalEndpoints && !horizontalEndpoints) return null;
13246
+ if (verticalEndpoints !== (returnPin._facingDirection === "y+" || returnPin._facingDirection === "y-")) {
13247
+ return null;
13248
+ }
13249
+ const branchStartsAtSharedPin = branchTrace.pins[0].pinId === sharedPin.pinId;
13250
+ let sharedToReturnPath = simplifyPath(branchTrace.tracePath);
13251
+ if (!branchStartsAtSharedPin) sharedToReturnPath.reverse();
13252
+ if (sharedToReturnPath.length !== 6) return null;
13253
+ const expectedSegmentOrientations = verticalEndpoints ? ["V", "H", "V", "H", "V"] : ["H", "V", "H", "V", "H"];
13254
+ const actualSegmentOrientations = sharedToReturnPath.slice(1).map(
13255
+ (point, index) => isHorizontal2(sharedToReturnPath[index], point) ? "H" : isVertical2(sharedToReturnPath[index], point) ? "V" : "D"
13256
+ );
13257
+ if (actualSegmentOrientations.some(
13258
+ (orientation, index) => orientation !== expectedSegmentOrientations[index]
13259
+ )) {
13260
+ return null;
13261
+ }
13262
+ const returnRail = getPerpendicularPinRail({
13263
+ trace: returnDonorTraces[0],
13264
+ endpointPin: returnPin
13265
+ });
13266
+ if (!returnRail) return null;
13267
+ const candidate = verticalEndpoints ? simplifyPath([
13268
+ { x: sharedPin.x, y: sharedPin.y },
13269
+ { x: sharedPin.x, y: sharedRail.railCoordinate },
13270
+ { x: sharedToReturnPath[2].x, y: sharedRail.railCoordinate },
13271
+ { x: sharedToReturnPath[3].x, y: returnRail.railCoordinate },
13272
+ { x: returnPin.x, y: returnRail.railCoordinate },
13273
+ { x: returnPin.x, y: returnPin.y }
13274
+ ]) : simplifyPath([
13275
+ { x: sharedPin.x, y: sharedPin.y },
13276
+ { x: sharedRail.railCoordinate, y: sharedPin.y },
13277
+ { x: sharedRail.railCoordinate, y: sharedToReturnPath[2].y },
13278
+ { x: returnRail.railCoordinate, y: sharedToReturnPath[3].y },
13279
+ { x: returnRail.railCoordinate, y: returnPin.y },
13280
+ { x: returnPin.x, y: returnPin.y }
13281
+ ]);
13282
+ return branchStartsAtSharedPin ? candidate : candidate.reverse();
13283
+ };
12833
13284
  var candidateIsClear = ({
12834
13285
  candidateTrace,
12835
13286
  originalTrace,
12836
13287
  traces,
12837
13288
  inputProblem,
13289
+ originalNetLabelPlacements,
12838
13290
  candidateNetLabelPlacements,
12839
13291
  attachedLabelIndexes
12840
13292
  }) => {
@@ -12877,6 +13329,53 @@ var candidateIsClear = ({
12877
13329
  )) {
12878
13330
  return false;
12879
13331
  }
13332
+ const labelHitsTrace = (label, trace) => pathIntersectsAnyNetLabel({
13333
+ path: trace.tracePath,
13334
+ netLabelPlacements: [label]
13335
+ });
13336
+ for (const labelIndex of attachedLabelIndexes) {
13337
+ const originalLabel = originalNetLabelPlacements[labelIndex];
13338
+ const candidateLabel = candidateNetLabelPlacements[labelIndex];
13339
+ const originalBounds = getRectBounds(
13340
+ originalLabel.center,
13341
+ originalLabel.width,
13342
+ originalLabel.height
13343
+ );
13344
+ const candidateBounds = getRectBounds(
13345
+ candidateLabel.center,
13346
+ candidateLabel.width,
13347
+ candidateLabel.height
13348
+ );
13349
+ for (const trace of traces) {
13350
+ if (trace.globalConnNetId === candidateLabel.globalConnNetId) continue;
13351
+ if (labelHitsTrace(candidateLabel, trace) && !labelHitsTrace(originalLabel, trace)) {
13352
+ return false;
13353
+ }
13354
+ }
13355
+ for (const obstacle of obstacles) {
13356
+ if (rectsOverlap(candidateBounds, obstacle) && !rectsOverlap(originalBounds, obstacle)) {
13357
+ return false;
13358
+ }
13359
+ }
13360
+ for (let otherIndex = 0; otherIndex < candidateNetLabelPlacements.length; otherIndex++) {
13361
+ if (otherIndex === labelIndex) continue;
13362
+ const originalOther = originalNetLabelPlacements[otherIndex];
13363
+ const candidateOther = candidateNetLabelPlacements[otherIndex];
13364
+ const originalOtherBounds = getRectBounds(
13365
+ originalOther.center,
13366
+ originalOther.width,
13367
+ originalOther.height
13368
+ );
13369
+ const candidateOtherBounds = getRectBounds(
13370
+ candidateOther.center,
13371
+ candidateOther.width,
13372
+ candidateOther.height
13373
+ );
13374
+ if (rectsOverlap(candidateBounds, candidateOtherBounds) && !rectsOverlap(originalBounds, originalOtherBounds)) {
13375
+ return false;
13376
+ }
13377
+ }
13378
+ }
12880
13379
  const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
12881
13380
  (label) => label.globalConnNetId !== candidateTrace.globalConnNetId
12882
13381
  );
@@ -12960,13 +13459,14 @@ var getAlignedReturnBranchPath = ({
12960
13459
  var alignSameNetJunctions = ({
12961
13460
  inputProblem,
12962
13461
  traces,
12963
- netLabelPlacements
13462
+ netLabelPlacements,
13463
+ netLabelConnectorTraceIds
12964
13464
  }) => {
12965
13465
  let outputTraces = [...traces];
12966
13466
  let outputNetLabelPlacements = [...netLabelPlacements];
12967
13467
  let alignedJunctionCount = 0;
13468
+ const alignedBranchTraceIds = /* @__PURE__ */ new Set();
12968
13469
  for (const alignReturnBranches of [false, true]) {
12969
- const alignedBranchTraceIds = /* @__PURE__ */ new Set();
12970
13470
  const donorTraceIds = traces.map((trace) => trace.mspPairId);
12971
13471
  const pendingDonorTraceIds = new Set(donorTraceIds);
12972
13472
  for (let donorIndex = 0; donorIndex < donorTraceIds.length; donorIndex++) {
@@ -12979,13 +13479,40 @@ var alignSameNetJunctions = ({
12979
13479
  if (alignedBranchTraceIds.has(branchTrace.mspPairId)) continue;
12980
13480
  if (donorTrace.mspPairId === branchTrace.mspPairId) continue;
12981
13481
  if (donorTrace.globalConnNetId !== branchTrace.globalConnNetId) continue;
12982
- const candidatePaths = alignReturnBranches ? [1, 0.5, 0.25].map(
12983
- (returnStemScale) => getAlignedReturnBranchPath({
13482
+ const donorIsLabelConnector = netLabelConnectorTraceIds.has(
13483
+ donorTrace.mspPairId
13484
+ );
13485
+ const branchIsLabelConnector = netLabelConnectorTraceIds.has(
13486
+ branchTrace.mspPairId
13487
+ );
13488
+ const candidatePaths = branchIsLabelConnector ? alignReturnBranches && !donorIsLabelConnector ? [
13489
+ getAlignedAttachedLabelConnectorPath({
13490
+ donorTrace,
13491
+ connectorTrace: branchTrace,
13492
+ netLabelConnectorTraceIds
13493
+ })
13494
+ ] : [] : donorIsLabelConnector ? [] : alignReturnBranches ? [
13495
+ ...[1, 0.5, 0.25].map(
13496
+ (returnStemScale) => getAlignedReturnBranchPath({
13497
+ donorTrace,
13498
+ branchTrace,
13499
+ returnStemScale
13500
+ })
13501
+ ),
13502
+ getAlignedPerpendicularEndpointBusPath({
13503
+ donorTrace,
13504
+ branchTrace,
13505
+ traces: outputTraces
13506
+ }),
13507
+ getAlignedSharedEndpointRailPath({
12984
13508
  donorTrace,
12985
13509
  branchTrace,
12986
- returnStemScale
13510
+ netLabelConnectorTraceIds
12987
13511
  })
12988
- ) : [getAlignedBranchPath({ donorTrace, branchTrace })];
13512
+ ] : [
13513
+ getAlignedBranchPath({ donorTrace, branchTrace }),
13514
+ getAlignedParallelPinExitPath({ donorTrace, branchTrace })
13515
+ ];
12989
13516
  for (const candidatePath of candidatePaths) {
12990
13517
  if (!candidatePath) continue;
12991
13518
  const candidateTrace = { ...branchTrace, tracePath: candidatePath };
@@ -13009,20 +13536,47 @@ var alignSameNetJunctions = ({
13009
13536
  netLabelPlacements: outputNetLabelPlacements,
13010
13537
  attachedLabelIndexes
13011
13538
  });
13539
+ const candidateTraces = preserveAttachedLabelConnectorJunctions({
13540
+ originalTrace: branchTrace,
13541
+ candidateTrace,
13542
+ traces: outputTraces,
13543
+ netLabelConnectorTraceIds
13544
+ });
13545
+ if (!candidateTraces) continue;
13012
13546
  if (!candidateIsClear({
13013
13547
  candidateTrace,
13014
13548
  originalTrace: branchTrace,
13015
- traces: outputTraces,
13549
+ traces: candidateTraces,
13016
13550
  inputProblem,
13551
+ originalNetLabelPlacements: outputNetLabelPlacements,
13017
13552
  candidateNetLabelPlacements,
13018
13553
  attachedLabelIndexes
13019
13554
  })) {
13020
13555
  continue;
13021
13556
  }
13022
- outputTraces = outputTraces.map((trace) => {
13023
- if (trace.mspPairId === branchTrace.mspPairId) return candidateTrace;
13024
- return trace;
13557
+ const movedConnectorsAreClear = candidateTraces.every((trace) => {
13558
+ if (trace.mspPairId === branchTrace.mspPairId || !netLabelConnectorTraceIds.has(trace.mspPairId)) {
13559
+ return true;
13560
+ }
13561
+ const originalConnector = outputTraces.find(
13562
+ (originalTrace) => originalTrace.mspPairId === trace.mspPairId
13563
+ );
13564
+ if (originalConnector.tracePath === trace.tracePath) return true;
13565
+ return candidateIsClear({
13566
+ candidateTrace: trace,
13567
+ originalTrace: originalConnector,
13568
+ traces: candidateTraces,
13569
+ inputProblem,
13570
+ originalNetLabelPlacements: outputNetLabelPlacements,
13571
+ candidateNetLabelPlacements,
13572
+ attachedLabelIndexes: getAttachedLabelIndexes(
13573
+ originalConnector,
13574
+ outputNetLabelPlacements
13575
+ )
13576
+ });
13025
13577
  });
13578
+ if (!movedConnectorsAreClear) continue;
13579
+ outputTraces = candidateTraces;
13026
13580
  outputNetLabelPlacements = candidateNetLabelPlacements;
13027
13581
  alignedBranchTraceIds.add(branchTrace.mspPairId);
13028
13582
  alignedJunctionCount++;
@@ -13834,7 +14388,7 @@ var alignPortOnlyInlineNetLabelStubs = ({
13834
14388
  };
13835
14389
 
13836
14390
  // lib/solvers/InlineNetLabelSolver/getAxisAlignedSegments.ts
13837
- var getAxisAlignedSegments = (path, epsilon = 1e-6) => {
14391
+ var getAxisAlignedSegments2 = (path, epsilon = 1e-6) => {
13838
14392
  const segments = [];
13839
14393
  let runStart = null;
13840
14394
  let runAxis = null;
@@ -14972,7 +15526,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
14972
15526
  pinIds,
14973
15527
  ignoredTraceIds = /* @__PURE__ */ new Set()
14974
15528
  }) {
14975
- const segments = getAxisAlignedSegments(trace.tracePath);
15529
+ const segments = getAxisAlignedSegments2(trace.tracePath);
14976
15530
  if (segments.length === 0) return null;
14977
15531
  const height = connection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT;
14978
15532
  const netLabelText = connection.netLabelText?.trim() || void 0;
@@ -16289,9 +16843,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
16289
16843
  inputTracePaths: instance.availableNetOrientationSolver.traces,
16290
16844
  globalConnMap: instance.mspConnectionPairSolver.globalConnMap,
16291
16845
  traceIdsToShift: new Set(
16292
- instance.availableNetOrientationSolver.traces.filter(
16293
- (trace) => trace.mspPairId.startsWith("available-net-orientation-")
16294
- ).map((trace) => trace.mspPairId)
16846
+ instance.availableNetOrientationSolver.netLabelConnectorTraceIds
16295
16847
  )
16296
16848
  }
16297
16849
  ]
@@ -16450,7 +17002,8 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
16450
17002
  {
16451
17003
  inputProblem: instance.inputProblem,
16452
17004
  traces: instance.netLabelNetLabelCollisionSolver.traces,
16453
- netLabelPlacements: collisionOutput.netLabelPlacements
17005
+ netLabelPlacements: collisionOutput.netLabelPlacements,
17006
+ netLabelConnectorTraceIds: instance.availableNetOrientationSolver.netLabelConnectorTraceIds
16454
17007
  }
16455
17008
  ];
16456
17009
  }