@tscircuit/schematic-trace-solver 0.0.175 → 0.0.176

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