@tscircuit/schematic-trace-solver 0.0.129 → 0.0.131

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 (33) hide show
  1. package/dist/index.d.ts +7 -0
  2. package/dist/index.js +219 -13
  3. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +290 -12
  4. package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +23 -2
  5. package/package.json +1 -1
  6. package/site/bug-reports/bug-report-20260811T075142Z.page.tsx +4 -0
  7. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +34 -22
  8. package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +5 -3
  9. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +305 -303
  10. package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +5 -3
  11. package/tests/bug-reports/bug-report-20260811T075142Z/__snapshots__/bug-report-20260811T075142Z.snap.svg +74 -0
  12. package/tests/bug-reports/bug-report-20260811T075142Z/bug-report-20260811T075142Z.json +194 -0
  13. package/tests/bug-reports/bug-report-20260811T075142Z/bug-report-20260811T075142Z.test.ts +131 -0
  14. package/tests/examples/__snapshots__/example10.snap.svg +20 -16
  15. package/tests/examples/__snapshots__/example31.snap.svg +8 -6
  16. package/tests/examples/__snapshots__/example33.snap.svg +32 -30
  17. package/tests/examples/__snapshots__/example46.snap.svg +5 -3
  18. package/tests/examples/__snapshots__/example48.snap.svg +25 -23
  19. package/tests/examples/__snapshots__/example50.snap.svg +32 -30
  20. package/tests/repros/__snapshots__/repro-atmega328p-fault-pullup.snap.svg +48 -46
  21. package/tests/repros/__snapshots__/repro-board-589-regulator-section.snap.svg +69 -0
  22. package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +7 -5
  23. package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +24 -18
  24. package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +467 -0
  25. package/tests/repros/__snapshots__/repro51-overlap-junction-crossing.snap.svg +25 -23
  26. package/tests/repros/__snapshots__/small-variant-resistor-facing-direction.snap.svg +46 -44
  27. package/tests/repros/assets/repro-board-589-regulator-section.input.json +270 -0
  28. package/tests/repros/assets/repro-usb-power-vbus-label-detour.input.json +2382 -0
  29. package/tests/repros/repro-board-589-regulator-section.test.ts +16 -0
  30. package/tests/repros/repro-max-msp-pair-distance-fallback.test.ts +45 -0
  31. package/tests/repros/repro-missing-trace-netlabel.test.ts +9 -11
  32. package/tests/repros/repro-usb-power-vbus-label-detour.test.ts +16 -0
  33. package/tests/repros/rotated-components-rail-label.test.ts +5 -11
package/dist/index.d.ts CHANGED
@@ -621,6 +621,7 @@ declare class LongDistancePairSolver extends BaseSolver {
621
621
  private netConnMap;
622
622
  private newlyConnectedPinIds;
623
623
  private allSolvedTraces;
624
+ private maxMspPairDistance;
624
625
  constructor(params: {
625
626
  inputProblem: InputProblem;
626
627
  alreadySolvedTraces: SolvedTracePath[];
@@ -778,6 +779,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
778
779
  currentLabelIndex: number | null;
779
780
  currentLabel: NetLabelPlacement | null;
780
781
  currentCandidateResults: EvaluatedCandidate[];
782
+ private crowdedPortOnlyLabelIndices;
781
783
  blockedStandaloneLabelIndex: number | null;
782
784
  blockedStandaloneLabelTargetAnchorX: number | null;
783
785
  private traceMap;
@@ -792,13 +794,16 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
792
794
  netLabelPlacements: NetLabelPlacement[];
793
795
  };
794
796
  private getProcessableLabelIndices;
797
+ private sortCrowdedTopBankByPin;
795
798
  private shouldProcessLabel;
799
+ private getCrowdedPortOnlyLabelIndices;
796
800
  private processLabel;
797
801
  private applyCandidate;
798
802
  private addConnectorTrace;
799
803
  private finish;
800
804
  private setCurrentLabel;
801
805
  private findCorrectedCandidate;
806
+ private findValidCrowdedTopVerticalFanoutCandidate;
802
807
  private hasTraceContinuingInOrientation;
803
808
  private findValidRotatedCandidate;
804
809
  private findValidTraceAnchorCandidate;
@@ -841,10 +846,12 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
841
846
  private hasOppositeVerticalRailOnSameChipSide;
842
847
  private getSingleChipSideForNetConnection;
843
848
  private isPortOnlyLabel;
849
+ private shouldIgnorePendingCrowdedLabel;
844
850
  private intersectsAnyOtherNetLabel;
845
851
  private sharesChipBoundary;
846
852
  private getChipSideForPoint;
847
853
  private getPerpendicularOutwardDirection;
854
+ private getChipOutwardDirection;
848
855
  private getContainingChipSide;
849
856
  private getOutsideChipSide;
850
857
  private getNearestChipSide;
package/dist/index.js CHANGED
@@ -4618,6 +4618,7 @@ var NEAREST_NEIGHBOR_COUNT = 3;
4618
4618
  var distance3 = (p1, p2) => {
4619
4619
  return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
4620
4620
  };
4621
+ var manhattanDistance = (p1, p2) => Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
4621
4622
  var LongDistancePairSolver = class extends BaseSolver {
4622
4623
  constructor(params) {
4623
4624
  super();
@@ -4625,6 +4626,7 @@ var LongDistancePairSolver = class extends BaseSolver {
4625
4626
  const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } = this.params;
4626
4627
  this.inputProblem = inputProblem;
4627
4628
  this.allSolvedTraces = [...alreadySolvedTraces];
4629
+ this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
4628
4630
  const primaryConnectedPinIds = /* @__PURE__ */ new Set();
4629
4631
  for (const pair of primaryMspConnectionPairs) {
4630
4632
  primaryConnectedPinIds.add(pair.pins[0].pinId);
@@ -4660,6 +4662,12 @@ var LongDistancePairSolver = class extends BaseSolver {
4660
4662
  const neighbors = allPinIdsInNet.filter((otherPinId) => otherPinId !== unconnectedPinId).flatMap((otherPinId) => {
4661
4663
  const targetPin = pinMap.get(otherPinId);
4662
4664
  if (!targetPin) return [];
4665
+ const isNamedTwoPinConnection = inputProblem.netConnections.some(
4666
+ (connection) => connection.pinIds.length === 2 && connection.pinIds.includes(sourcePin.pinId) && connection.pinIds.includes(targetPin.pinId)
4667
+ );
4668
+ if (isNamedTwoPinConnection && manhattanDistance(sourcePin, targetPin) > this.maxMspPairDistance) {
4669
+ return [];
4670
+ }
4663
4671
  return [
4664
4672
  {
4665
4673
  pin: targetPin,
@@ -4694,6 +4702,7 @@ var LongDistancePairSolver = class extends BaseSolver {
4694
4702
  netConnMap;
4695
4703
  newlyConnectedPinIds = /* @__PURE__ */ new Set();
4696
4704
  allSolvedTraces = [];
4705
+ maxMspPairDistance;
4697
4706
  getConstructorParams() {
4698
4707
  return this.params;
4699
4708
  }
@@ -8053,6 +8062,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8053
8062
  currentLabelIndex = null;
8054
8063
  currentLabel = null;
8055
8064
  currentCandidateResults = [];
8065
+ crowdedPortOnlyLabelIndices = /* @__PURE__ */ new Set();
8056
8066
  blockedStandaloneLabelIndex = null;
8057
8067
  blockedStandaloneLabelTargetAnchorX = null;
8058
8068
  traceMap;
@@ -8071,6 +8081,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8071
8081
  this.pinMap = getPinMap(params.inputProblem);
8072
8082
  this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
8073
8083
  this.maxSearchDistance = getMaxSearchDistance(params.inputProblem);
8084
+ this.crowdedPortOnlyLabelIndices = this.getCrowdedPortOnlyLabelIndices();
8074
8085
  this.queuedLabelIndices = this.getProcessableLabelIndices();
8075
8086
  this.setCurrentLabel(this.queuedLabelIndices[0] ?? null);
8076
8087
  }
@@ -8098,10 +8109,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8098
8109
  getProcessableLabelIndices() {
8099
8110
  const indices = [];
8100
8111
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
8101
- if (this.shouldProcessLabel(this.outputNetLabelPlacements[i])) {
8112
+ if (this.shouldProcessLabel(this.outputNetLabelPlacements[i], i)) {
8102
8113
  indices.push(i);
8103
8114
  }
8104
8115
  }
8116
+ this.sortCrowdedTopBankByPin(indices);
8105
8117
  const blockedStandaloneLabelIndex = indices.find((index) => {
8106
8118
  const label = this.outputNetLabelPlacements[index];
8107
8119
  const orientations = this.getAvailableOrientations(label);
@@ -8150,9 +8162,87 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8150
8162
  }
8151
8163
  return indices;
8152
8164
  }
8153
- shouldProcessLabel(label) {
8165
+ sortCrowdedTopBankByPin(indices) {
8166
+ const slotsByChip = /* @__PURE__ */ new Map();
8167
+ for (let slot = 0; slot < indices.length; slot++) {
8168
+ const labelIndex = indices[slot];
8169
+ if (!this.crowdedPortOnlyLabelIndices.has(labelIndex)) continue;
8170
+ const label = this.outputNetLabelPlacements[labelIndex];
8171
+ if (this.getChipSideForPoint(label.anchorPoint) !== "top") continue;
8172
+ const pin = this.pinMap[label.pinIds[0]];
8173
+ if (!pin) continue;
8174
+ const slots = slotsByChip.get(pin.chipId) ?? [];
8175
+ slots.push(slot);
8176
+ slotsByChip.set(pin.chipId, slots);
8177
+ }
8178
+ for (const slots of slotsByChip.values()) {
8179
+ const labelIndices = slots.map((slot) => indices[slot]).sort((a, b) => {
8180
+ const aPin = this.pinMap[this.outputNetLabelPlacements[a].pinIds[0]];
8181
+ const bPin = this.pinMap[this.outputNetLabelPlacements[b].pinIds[0]];
8182
+ return aPin.x - bPin.x;
8183
+ });
8184
+ for (let position = 0; position < slots.length; position++) {
8185
+ indices[slots[position]] = labelIndices[position];
8186
+ }
8187
+ }
8188
+ }
8189
+ shouldProcessLabel(label, labelIndex) {
8154
8190
  const orientations = this.getAvailableOrientations(label);
8155
- return orientations.length > 0 && !orientations.includes(label.orientation);
8191
+ if (orientations.length === 0) return false;
8192
+ if (!orientations.includes(label.orientation)) return true;
8193
+ if (!this.crowdedPortOnlyLabelIndices.has(labelIndex)) return false;
8194
+ const bounds = getRectBounds(label.center, label.width, label.height);
8195
+ return this.outputNetLabelPlacements.some((otherLabel, otherIndex) => {
8196
+ if (otherIndex === labelIndex) return false;
8197
+ if (otherLabel.globalConnNetId === label.globalConnNetId) return false;
8198
+ return rectsOverlap2(
8199
+ bounds,
8200
+ getRectBounds(otherLabel.center, otherLabel.width, otherLabel.height)
8201
+ );
8202
+ });
8203
+ }
8204
+ getCrowdedPortOnlyLabelIndices() {
8205
+ const labelIndicesByChipAndSide = /* @__PURE__ */ new Map();
8206
+ for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
8207
+ const label = this.outputNetLabelPlacements[i];
8208
+ if (!this.isPortOnlyLabel(label)) continue;
8209
+ if (!this.isStandaloneSinglePinNetLabel(label)) continue;
8210
+ if (this.getAvailableOrientations(label).length === 0) continue;
8211
+ const pin = this.pinMap[label.pinIds[0]];
8212
+ if (!pin) continue;
8213
+ const chipSide = this.getChipSideForPoint(label.anchorPoint);
8214
+ if (!chipSide) continue;
8215
+ const labelIndicesBySide = labelIndicesByChipAndSide.get(pin.chipId) ?? /* @__PURE__ */ new Map();
8216
+ const labelIndices = labelIndicesBySide.get(chipSide) ?? [];
8217
+ labelIndices.push(i);
8218
+ labelIndicesBySide.set(chipSide, labelIndices);
8219
+ labelIndicesByChipAndSide.set(pin.chipId, labelIndicesBySide);
8220
+ }
8221
+ const crowdedIndices = /* @__PURE__ */ new Set();
8222
+ for (const labelIndicesBySide of labelIndicesByChipAndSide.values()) {
8223
+ for (const labelIndices of labelIndicesBySide.values()) {
8224
+ const hasWrongOrientation = labelIndices.some((index) => {
8225
+ const label = this.outputNetLabelPlacements[index];
8226
+ return !this.getAvailableOrientations(label).includes(
8227
+ label.orientation
8228
+ );
8229
+ });
8230
+ const hasOverlap = labelIndices.some((index, position) => {
8231
+ const label = this.outputNetLabelPlacements[index];
8232
+ const bounds = getRectBounds(label.center, label.width, label.height);
8233
+ return labelIndices.slice(position + 1).some((otherIndex) => {
8234
+ const other = this.outputNetLabelPlacements[otherIndex];
8235
+ return rectsOverlap2(
8236
+ bounds,
8237
+ getRectBounds(other.center, other.width, other.height)
8238
+ );
8239
+ });
8240
+ });
8241
+ if (!hasWrongOrientation || !hasOverlap) continue;
8242
+ for (const index of labelIndices) crowdedIndices.add(index);
8243
+ }
8244
+ }
8245
+ return crowdedIndices;
8156
8246
  }
8157
8247
  processLabel(labelIndex) {
8158
8248
  const label = this.outputNetLabelPlacements[labelIndex];
@@ -8171,7 +8261,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8171
8261
  }
8172
8262
  addConnectorTrace(label, candidate, labelIndex) {
8173
8263
  if (candidate.phase === "trace-anchor") return;
8174
- const tracePath = this.getCandidateConnectorTrace(label, candidate);
8264
+ const tracePath = this.getCandidateConnectorTrace(
8265
+ label,
8266
+ candidate,
8267
+ labelIndex
8268
+ );
8175
8269
  if (tracePath.length < 2) return;
8176
8270
  const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
8177
8271
  const connectorTrace = {
@@ -8200,6 +8294,14 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8200
8294
  findCorrectedCandidate(label, labelIndex) {
8201
8295
  const orientations = this.getAvailableOrientations(label);
8202
8296
  const requiredOrientation = orientations[0];
8297
+ if (this.crowdedPortOnlyLabelIndices.has(labelIndex) && this.getChipSideForPoint(label.anchorPoint) === "top" && isYOrientation(requiredOrientation)) {
8298
+ const topFanoutCandidate = this.findValidCrowdedTopVerticalFanoutCandidate(
8299
+ label,
8300
+ labelIndex,
8301
+ requiredOrientation
8302
+ );
8303
+ if (topFanoutCandidate) return topFanoutCandidate;
8304
+ }
8203
8305
  const netConnection = this.inputProblem.netConnections.find(
8204
8306
  (connection) => connection.netId === label.netId
8205
8307
  );
@@ -8254,6 +8356,77 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8254
8356
  labelIndex
8255
8357
  );
8256
8358
  }
8359
+ findValidCrowdedTopVerticalFanoutCandidate(label, labelIndex, orientation) {
8360
+ const pin = this.pinMap[label.pinIds[0]];
8361
+ const chip = pin ? this.chipObstacleSpatialIndex.chips.find(
8362
+ (candidate) => candidate.chipId === pin.chipId
8363
+ ) : void 0;
8364
+ if (!chip) return null;
8365
+ const { width, height } = getDimsForOrientation({
8366
+ orientation,
8367
+ netLabelWidth: this.getNetLabelWidth(label),
8368
+ netLabelHeight: this.getNetLabelHeight(label)
8369
+ });
8370
+ const nextPinToRight = this.outputNetLabelPlacements.map((otherLabel, index) => ({
8371
+ index,
8372
+ pin: this.pinMap[otherLabel.pinIds[0]]
8373
+ })).filter(
8374
+ ({ index, pin: otherPin }) => this.crowdedPortOnlyLabelIndices.has(index) && otherPin?.chipId === pin.chipId && otherPin.x > pin.x + EPS11
8375
+ ).sort((a, b) => a.pin.x - b.pin.x)[0]?.pin;
8376
+ if (orientation === "y-" && nextPinToRight) return null;
8377
+ const anchorX = orientation === "y-" ? Math.max(
8378
+ label.anchorPoint.x + LABEL_SEARCH_STEP,
8379
+ chip.bounds.maxX + width / 2 + LABEL_TRACE_CLEARANCE2
8380
+ ) : Math.min(
8381
+ label.anchorPoint.x,
8382
+ (nextPinToRight?.x ?? chip.bounds.maxX) - LABEL_SEARCH_STEP - width / 2
8383
+ );
8384
+ let highestPlacedLabelY = chip.bounds.maxY;
8385
+ let lowestUpFacingLabelY = Number.POSITIVE_INFINITY;
8386
+ let highestUpFacingLabelY = Number.NEGATIVE_INFINITY;
8387
+ for (let index = 0; index < this.outputNetLabelPlacements.length; index++) {
8388
+ if (index === labelIndex) continue;
8389
+ if (!this.crowdedPortOnlyLabelIndices.has(index)) continue;
8390
+ if (this.queuedLabelIndices.includes(index)) continue;
8391
+ const placedLabel = this.outputNetLabelPlacements[index];
8392
+ const placedBounds = getRectBounds(
8393
+ placedLabel.center,
8394
+ placedLabel.width,
8395
+ placedLabel.height
8396
+ );
8397
+ highestPlacedLabelY = Math.max(highestPlacedLabelY, placedBounds.maxY);
8398
+ if (placedLabel.orientation !== "y+") continue;
8399
+ lowestUpFacingLabelY = Math.min(lowestUpFacingLabelY, placedBounds.minY);
8400
+ highestUpFacingLabelY = Math.max(highestUpFacingLabelY, placedBounds.maxY);
8401
+ }
8402
+ const stackedAnchorY = highestPlacedLabelY + LABEL_SEARCH_STEP + (orientation === "y-" ? height : 0);
8403
+ const hasUpFacingStack = Number.isFinite(lowestUpFacingLabelY) && Number.isFinite(highestUpFacingLabelY);
8404
+ const centeredAnchorY = hasUpFacingStack ? (lowestUpFacingLabelY + highestUpFacingLabelY) / 2 : stackedAnchorY;
8405
+ const anchorYs = orientation === "y-" ? [centeredAnchorY, stackedAnchorY] : [stackedAnchorY];
8406
+ for (const anchorY of anchorYs) {
8407
+ if (anchorY - label.anchorPoint.y > this.maxSearchDistance + EPS11) {
8408
+ continue;
8409
+ }
8410
+ const candidate = this.createCandidate(
8411
+ label,
8412
+ { x: anchorX, y: anchorY },
8413
+ orientation
8414
+ );
8415
+ const result = this.evaluateCandidate(
8416
+ candidate,
8417
+ label,
8418
+ labelIndex,
8419
+ "lateral-shift",
8420
+ anchorY - label.anchorPoint.y,
8421
+ anchorX - label.anchorPoint.x
8422
+ );
8423
+ this.currentCandidateResults.push(result);
8424
+ if (result.status !== "valid") continue;
8425
+ result.selected = true;
8426
+ return result;
8427
+ }
8428
+ return null;
8429
+ }
8257
8430
  hasTraceContinuingInOrientation(label, orientation) {
8258
8431
  const direction = dir(orientation);
8259
8432
  return Object.values(this.traceMap).some((trace) => {
@@ -8521,12 +8694,22 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8521
8694
  })
8522
8695
  };
8523
8696
  }
8524
- getCandidateConnectorTrace(label, candidate) {
8697
+ getCandidateConnectorTrace(label, candidate, labelIndex) {
8698
+ if (this.crowdedPortOnlyLabelIndices.has(labelIndex) && this.getChipSideForPoint(label.anchorPoint) === "top" && isYOrientation(candidate.orientation)) {
8699
+ return simplifyOrthogonalPath([
8700
+ label.anchorPoint,
8701
+ { x: label.anchorPoint.x, y: candidate.anchorPoint.y },
8702
+ candidate.anchorPoint
8703
+ ]);
8704
+ }
8525
8705
  if (candidate.phase === "lateral-shift") {
8526
- const orientDir = dir(candidate.orientation);
8706
+ const chipOutwardDir = this.crowdedPortOnlyLabelIndices.has(labelIndex) ? this.getChipOutwardDirection(label.anchorPoint) : (() => {
8707
+ const orientDir = dir(candidate.orientation);
8708
+ return { x: -orientDir.x, y: -orientDir.y };
8709
+ })();
8527
8710
  const kickedSource = {
8528
- x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
8529
- y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP
8711
+ x: label.anchorPoint.x + chipOutwardDir.x * LABEL_SEARCH_STEP,
8712
+ y: label.anchorPoint.y + chipOutwardDir.y * LABEL_SEARCH_STEP
8530
8713
  };
8531
8714
  return simplifyOrthogonalPath([
8532
8715
  label.anchorPoint,
@@ -8741,11 +8924,15 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8741
8924
  });
8742
8925
  if (nonChipBoundsStatus !== "valid") return nonChipBoundsStatus;
8743
8926
  }
8744
- const connectorTrace = this.getCandidateConnectorTrace(label, {
8745
- anchorPoint: candidate.anchorPoint,
8746
- orientation: candidate.orientation,
8747
- phase
8748
- });
8927
+ const connectorTrace = this.getCandidateConnectorTrace(
8928
+ label,
8929
+ {
8930
+ anchorPoint: candidate.anchorPoint,
8931
+ orientation: candidate.orientation,
8932
+ phase
8933
+ },
8934
+ labelIndex
8935
+ );
8749
8936
  for (const chip of this.chipObstacleSpatialIndex.chips) {
8750
8937
  if (tracePathCrossesAnyBounds(connectorTrace, chip.bounds)) {
8751
8938
  return "chip-collision";
@@ -8753,6 +8940,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8753
8940
  }
8754
8941
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
8755
8942
  if (i === labelIndex) continue;
8943
+ if (this.shouldIgnorePendingCrowdedLabel(labelIndex, i)) continue;
8756
8944
  const otherLabel = this.outputNetLabelPlacements[i];
8757
8945
  if (tracePathIntersectsBounds(
8758
8946
  connectorTrace,
@@ -8891,9 +9079,13 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8891
9079
  isPortOnlyLabel(label) {
8892
9080
  return label.mspConnectionPairIds.length === 0;
8893
9081
  }
9082
+ shouldIgnorePendingCrowdedLabel(labelIndex, otherLabelIndex) {
9083
+ return this.crowdedPortOnlyLabelIndices.has(labelIndex) && this.crowdedPortOnlyLabelIndices.has(otherLabelIndex) && this.queuedLabelIndices.includes(otherLabelIndex);
9084
+ }
8894
9085
  intersectsAnyOtherNetLabel(bounds, labelIndex) {
8895
9086
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
8896
9087
  if (i === labelIndex) continue;
9088
+ if (this.shouldIgnorePendingCrowdedLabel(labelIndex, i)) continue;
8897
9089
  const label = this.outputNetLabelPlacements[i];
8898
9090
  const otherBounds = getRectBounds(label.center, label.width, label.height);
8899
9091
  if (i === this.blockedStandaloneLabelIndex && isYOrientation(label.orientation)) {
@@ -8951,6 +9143,20 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
8951
9143
  }
8952
9144
  return { x: 0, y: 0 };
8953
9145
  }
9146
+ getChipOutwardDirection(point) {
9147
+ switch (this.getChipSideForPoint(point)) {
9148
+ case "left":
9149
+ return { x: -1, y: 0 };
9150
+ case "right":
9151
+ return { x: 1, y: 0 };
9152
+ case "bottom":
9153
+ return { x: 0, y: -1 };
9154
+ case "top":
9155
+ return { x: 0, y: 1 };
9156
+ default:
9157
+ return { x: 0, y: 0 };
9158
+ }
9159
+ }
8954
9160
  getContainingChipSide(point) {
8955
9161
  let nearestSide = null;
8956
9162
  let nearestDistance = Number.POSITIVE_INFINITY;