@tscircuit/schematic-trace-solver 0.0.81 → 0.0.83

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.
package/biome.json CHANGED
@@ -19,6 +19,11 @@
19
19
  "bracketSameLine": false
20
20
  }
21
21
  },
22
+ "json": {
23
+ "formatter": {
24
+ "enabled": false
25
+ }
26
+ },
22
27
  "linter": {
23
28
  "enabled": true,
24
29
  "rules": {
package/dist/index.d.ts CHANGED
@@ -666,6 +666,7 @@ declare class Example28Solver extends BaseSolver {
666
666
  currentOverlap: TraceLabelOverlap | null;
667
667
  currentCandidateResults: RerouteCandidateResult[];
668
668
  private chipObstacles;
669
+ private pinMap;
669
670
  constructor(params: Example28SolverParams);
670
671
  getConstructorParams(): ConstructorParameters<typeof Example28Solver>[0];
671
672
  _step(): void;
@@ -679,6 +680,7 @@ declare class Example28Solver extends BaseSolver {
679
680
  private isOverlapStillPresent;
680
681
  private getCurrentTrace;
681
682
  private rerouteOverlappingTrace;
683
+ private tryMoveLabelOutward;
682
684
  visualize(): GraphicsObject;
683
685
  }
684
686
 
@@ -695,7 +697,7 @@ type CandidateLabel = {
695
697
  height: number;
696
698
  };
697
699
  type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
698
- type CandidatePhase = "rotate" | "shift" | "lateral-shift";
700
+ type CandidatePhase = "rotate" | "trace-anchor" | "shift" | "lateral-shift";
699
701
  type EvaluatedCandidate = CandidateLabel & {
700
702
  status: CandidateStatus$2;
701
703
  selected: boolean;
@@ -733,6 +735,8 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
733
735
  private setCurrentLabel;
734
736
  private findCorrectedCandidate;
735
737
  private findValidRotatedCandidate;
738
+ private findValidTraceAnchorCandidate;
739
+ private getTraceAnchorCandidatePoints;
736
740
  private getAvailableOrientations;
737
741
  private findValidShiftedCandidate;
738
742
  /**
@@ -757,6 +761,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
757
761
  private getNetLabelWidth;
758
762
  private getNetLabelHeight;
759
763
  private getCandidateStatus;
764
+ private isAcceptableTraceAnchorChipCollision;
760
765
  private getBoundsStatus;
761
766
  private isTraceTooCloseToLabel;
762
767
  private getLabelTraceClearanceBounds;
package/dist/index.js CHANGED
@@ -5162,6 +5162,54 @@ var TraceCleanupSolver = class extends BaseSolver {
5162
5162
  }
5163
5163
  };
5164
5164
 
5165
+ // lib/solvers/AvailableNetOrientationSolver/traces.ts
5166
+ var getPinMap = (inputProblem) => {
5167
+ const pinMap = {};
5168
+ for (const chip of inputProblem.chips) {
5169
+ for (const pin of chip.pins) {
5170
+ pinMap[pin.pinId] = { ...pin, chipId: chip.chipId };
5171
+ }
5172
+ }
5173
+ return pinMap;
5174
+ };
5175
+ var getTracePins = (label, pinMap) => {
5176
+ const pins = label.pinIds.flatMap((pinId) => {
5177
+ const pin = pinMap[pinId];
5178
+ return pin ? [pin] : [];
5179
+ });
5180
+ if (pins.length >= 2) return [pins[0], pins[1]];
5181
+ if (pins.length === 1) return [pins[0], pins[0]];
5182
+ const syntheticPin = {
5183
+ pinId: `${label.globalConnNetId}-netlabel-anchor`,
5184
+ x: label.anchorPoint.x,
5185
+ y: label.anchorPoint.y,
5186
+ chipId: "available-net-orientation"
5187
+ };
5188
+ return [syntheticPin, syntheticPin];
5189
+ };
5190
+ var toNetLabelPlacementPatch = (candidate) => ({
5191
+ orientation: candidate.orientation,
5192
+ anchorPoint: candidate.anchorPoint,
5193
+ center: candidate.center,
5194
+ width: candidate.width,
5195
+ height: candidate.height
5196
+ });
5197
+
5198
+ // lib/utils/dir.ts
5199
+ var dir = (facingDirection) => {
5200
+ switch (facingDirection) {
5201
+ case "x+":
5202
+ return { x: 1, y: 0 };
5203
+ case "x-":
5204
+ return { x: -1, y: 0 };
5205
+ case "y+":
5206
+ return { x: 0, y: 1 };
5207
+ case "y-":
5208
+ return { x: 0, y: -1 };
5209
+ }
5210
+ throw new Error(`Invalid facing direction: ${facingDirection}`);
5211
+ };
5212
+
5165
5213
  // lib/solvers/Example28Solver/types.ts
5166
5214
  var EPS7 = 1e-9;
5167
5215
 
@@ -5369,24 +5417,8 @@ var moveAttachedLabelsToReroutedTrace = ({
5369
5417
  };
5370
5418
  });
5371
5419
 
5372
- // lib/utils/dir.ts
5373
- var dir = (facingDirection) => {
5374
- switch (facingDirection) {
5375
- case "x+":
5376
- return { x: 1, y: 0 };
5377
- case "x-":
5378
- return { x: -1, y: 0 };
5379
- case "y+":
5380
- return { x: 0, y: 1 };
5381
- case "y-":
5382
- return { x: 0, y: -1 };
5383
- }
5384
- throw new Error(`Invalid facing direction: ${facingDirection}`);
5385
- };
5386
-
5387
5420
  // lib/solvers/Example28Solver/reroute.ts
5388
- var LABEL_SIDE_CLEARANCE = 0.1;
5389
- var LABEL_HUG_CLEARANCE = 1e-3;
5421
+ var LABEL_CLEARANCE = 0.1;
5390
5422
  var findBestReroutePath = ({
5391
5423
  trace,
5392
5424
  obstacleLabel,
@@ -5447,7 +5479,7 @@ var generateRerouteCandidateResults = ({
5447
5479
  trace,
5448
5480
  label,
5449
5481
  problem: inputProblem,
5450
- paddingBuffer: LABEL_SIDE_CLEARANCE,
5482
+ paddingBuffer: LABEL_CLEARANCE,
5451
5483
  detourCount: 0
5452
5484
  }),
5453
5485
  ...generateEndpointDetourCandidates(trace, label)
@@ -5513,8 +5545,10 @@ var generateEndpointDetourCandidates = (trace, label) => {
5513
5545
  const start = trace.tracePath[0];
5514
5546
  const end = trace.tracePath[trace.tracePath.length - 1];
5515
5547
  if (!start || !end) return [];
5548
+ const startExit = trace.tracePath[1] ?? start;
5549
+ const endEntry = trace.tracePath[trace.tracePath.length - 2] ?? end;
5516
5550
  const bounds = getRectBounds(label.center, label.width, label.height);
5517
- const padding = LABEL_SIDE_CLEARANCE;
5551
+ const padding = LABEL_CLEARANCE;
5518
5552
  const labelDirection = dir(label.orientation);
5519
5553
  const labelSideX = labelDirection.x < 0 ? bounds.minX - padding : bounds.maxX + padding;
5520
5554
  const oppositeSideX = labelDirection.x < 0 ? bounds.maxX + padding : bounds.minX - padding;
@@ -5525,18 +5559,22 @@ var generateEndpointDetourCandidates = (trace, label) => {
5525
5559
  candidates.push(
5526
5560
  [
5527
5561
  start,
5528
- { x: start.x, y: topY },
5562
+ startExit,
5563
+ { x: startExit.x, y: topY },
5529
5564
  { x: sideX, y: topY },
5530
5565
  { x: sideX, y: bottomY },
5531
- { x: end.x, y: bottomY },
5566
+ { x: endEntry.x, y: bottomY },
5567
+ endEntry,
5532
5568
  end
5533
5569
  ],
5534
5570
  [
5535
5571
  start,
5536
- { x: start.x, y: bottomY },
5572
+ startExit,
5573
+ { x: startExit.x, y: bottomY },
5537
5574
  { x: sideX, y: bottomY },
5538
5575
  { x: sideX, y: topY },
5539
- { x: end.x, y: topY },
5576
+ { x: endEntry.x, y: topY },
5577
+ endEntry,
5540
5578
  end
5541
5579
  ]
5542
5580
  );
@@ -5547,30 +5585,36 @@ var generateLabelHugCandidates = (trace, label) => {
5547
5585
  const start = trace.tracePath[0];
5548
5586
  const end = trace.tracePath[trace.tracePath.length - 1];
5549
5587
  if (!start || !end) return [];
5588
+ const startExit = trace.tracePath[1] ?? start;
5589
+ const endEntry = trace.tracePath[trace.tracePath.length - 2] ?? end;
5550
5590
  const labelDirection = dir(label.orientation);
5551
5591
  if (labelDirection.x === 0) return [];
5552
5592
  const bounds = getRectBounds(label.center, label.width, label.height);
5553
- const sideX = labelDirection.x < 0 ? bounds.minX - LABEL_SIDE_CLEARANCE : bounds.maxX + LABEL_SIDE_CLEARANCE;
5554
- const topY = bounds.maxY + LABEL_HUG_CLEARANCE;
5555
- const bottomY = bounds.minY - LABEL_HUG_CLEARANCE;
5556
- const startsAboveEnd = start.y >= end.y;
5593
+ const sideX = labelDirection.x < 0 ? bounds.minX - LABEL_CLEARANCE : bounds.maxX + LABEL_CLEARANCE;
5594
+ const topY = bounds.maxY + LABEL_CLEARANCE;
5595
+ const bottomY = bounds.minY - LABEL_CLEARANCE;
5596
+ const startsAboveEnd = startExit.y >= endEntry.y;
5557
5597
  const firstY = startsAboveEnd ? topY : bottomY;
5558
5598
  const secondY = startsAboveEnd ? bottomY : topY;
5559
5599
  return [
5560
5600
  [
5561
5601
  start,
5562
- { x: start.x, y: firstY },
5602
+ startExit,
5603
+ { x: startExit.x, y: firstY },
5563
5604
  { x: sideX, y: firstY },
5564
5605
  { x: sideX, y: secondY },
5565
- { x: end.x, y: secondY },
5606
+ { x: endEntry.x, y: secondY },
5607
+ endEntry,
5566
5608
  end
5567
5609
  ],
5568
5610
  [
5569
5611
  start,
5570
- { x: start.x, y: secondY },
5612
+ startExit,
5613
+ { x: startExit.x, y: secondY },
5571
5614
  { x: sideX, y: secondY },
5572
5615
  { x: sideX, y: firstY },
5573
- { x: end.x, y: firstY },
5616
+ { x: endEntry.x, y: firstY },
5617
+ endEntry,
5574
5618
  end
5575
5619
  ]
5576
5620
  ];
@@ -5625,9 +5669,9 @@ var generateHorizontalSegmentPushCandidate = (trace, label) => {
5625
5669
  if (Math.abs(previousAnchor.y - verticalStart.y) >= 1e-9 || Math.abs(verticalEnd.y - nextAnchor.y) >= 1e-9) {
5626
5670
  return null;
5627
5671
  }
5628
- let segmentPushX = bounds.minX - LABEL_SIDE_CLEARANCE;
5672
+ let segmentPushX = bounds.minX - LABEL_CLEARANCE;
5629
5673
  if (labelDirection.x > 0) {
5630
- segmentPushX = bounds.maxX + LABEL_SIDE_CLEARANCE;
5674
+ segmentPushX = bounds.maxX + LABEL_CLEARANCE;
5631
5675
  }
5632
5676
  const segmentPushStartY = getClearedHorizontalY({
5633
5677
  start: previousAnchor,
@@ -5665,9 +5709,9 @@ var getClearedHorizontalY = ({
5665
5709
  if (!segmentIntersectsRect2(start, end, labelBounds)) return start.y;
5666
5710
  const labelCenterY = (labelBounds.minY + labelBounds.maxY) / 2;
5667
5711
  if (start.y >= labelCenterY) {
5668
- return labelBounds.maxY + LABEL_HUG_CLEARANCE;
5712
+ return labelBounds.maxY + LABEL_CLEARANCE;
5669
5713
  }
5670
- return labelBounds.minY - LABEL_HUG_CLEARANCE;
5714
+ return labelBounds.minY - LABEL_CLEARANCE;
5671
5715
  };
5672
5716
  var markSelectedCandidate = (candidateResults, bestPath) => {
5673
5717
  if (!bestPath) return;
@@ -5800,6 +5844,9 @@ ${label.netId ?? label.globalConnNetId}`
5800
5844
  };
5801
5845
 
5802
5846
  // lib/solvers/Example28Solver/Example28Solver.ts
5847
+ var LABEL_OUTWARD_STEP = 0.1;
5848
+ var LABEL_MAX_OUTWARD_STEPS = 10;
5849
+ var LABEL_TRACE_CLEARANCE = 0.1;
5803
5850
  var Example28Solver = class extends BaseSolver {
5804
5851
  inputProblem;
5805
5852
  traces;
@@ -5810,6 +5857,7 @@ var Example28Solver = class extends BaseSolver {
5810
5857
  currentOverlap = null;
5811
5858
  currentCandidateResults = [];
5812
5859
  chipObstacles;
5860
+ pinMap;
5813
5861
  constructor(params) {
5814
5862
  super();
5815
5863
  this.inputProblem = params.inputProblem;
@@ -5818,6 +5866,7 @@ var Example28Solver = class extends BaseSolver {
5818
5866
  this.outputTraces = [...params.traces];
5819
5867
  this.outputNetLabelPlacements = [...params.netLabelPlacements];
5820
5868
  this.chipObstacles = getObstacleRects(params.inputProblem);
5869
+ this.pinMap = getPinMap(params.inputProblem);
5821
5870
  this.initializeQueuedOverlaps();
5822
5871
  this.currentOverlap = this.queuedOverlaps[0] ?? null;
5823
5872
  }
@@ -5900,6 +5949,7 @@ var Example28Solver = class extends BaseSolver {
5900
5949
  );
5901
5950
  if (traceIndex === -1) return;
5902
5951
  const currentTrace = this.outputTraces[traceIndex];
5952
+ if (this.tryMoveLabelOutward(overlap.label)) return;
5903
5953
  const rerouteResult = findBestReroutePath({
5904
5954
  trace: currentTrace,
5905
5955
  obstacleLabel: overlap.label,
@@ -5922,6 +5972,62 @@ var Example28Solver = class extends BaseSolver {
5922
5972
  netLabelPlacements: this.outputNetLabelPlacements
5923
5973
  });
5924
5974
  }
5975
+ tryMoveLabelOutward(labelToMove) {
5976
+ const labelIndex = this.outputNetLabelPlacements.findIndex(
5977
+ (label2) => label2.globalConnNetId === labelToMove.globalConnNetId && label2.anchorPoint.x === labelToMove.anchorPoint.x && label2.anchorPoint.y === labelToMove.anchorPoint.y && label2.pinIds.join(",") === labelToMove.pinIds.join(",")
5978
+ );
5979
+ if (labelIndex === -1) return false;
5980
+ const label = this.outputNetLabelPlacements[labelIndex];
5981
+ if (label.mspConnectionPairIds.length > 0) return false;
5982
+ if (label.pinIds.length !== 1) return false;
5983
+ if (this.outputNetLabelPlacements.filter(
5984
+ (otherLabel) => otherLabel.globalConnNetId === label.globalConnNetId
5985
+ ).length !== 1) {
5986
+ return false;
5987
+ }
5988
+ const outward = dir(label.orientation);
5989
+ if (outward.x === 0 && outward.y === 0) return false;
5990
+ for (let step = 1; step <= LABEL_MAX_OUTWARD_STEPS; step++) {
5991
+ const distance3 = step * LABEL_OUTWARD_STEP;
5992
+ const candidate = {
5993
+ ...label,
5994
+ anchorPoint: {
5995
+ x: label.anchorPoint.x + outward.x * distance3,
5996
+ y: label.anchorPoint.y + outward.y * distance3
5997
+ },
5998
+ center: {
5999
+ x: label.center.x + outward.x * distance3,
6000
+ y: label.center.y + outward.y * distance3
6001
+ }
6002
+ };
6003
+ const candidateWithClearance = {
6004
+ ...candidate,
6005
+ width: candidate.width + LABEL_TRACE_CLEARANCE * 2,
6006
+ height: candidate.height + LABEL_TRACE_CLEARANCE * 2
6007
+ };
6008
+ if (detectTraceLabelOverlap({
6009
+ traces: this.outputTraces,
6010
+ netLabels: [candidateWithClearance]
6011
+ }).length > 0) {
6012
+ continue;
6013
+ }
6014
+ const connectorTrace = createPortOnlyLabelConnectorTrace({
6015
+ label,
6016
+ movedLabel: candidate,
6017
+ pinMap: this.pinMap
6018
+ });
6019
+ if (detectTraceLabelOverlap({
6020
+ traces: [connectorTrace],
6021
+ netLabels: this.outputNetLabelPlacements
6022
+ }).length > 0) {
6023
+ continue;
6024
+ }
6025
+ this.outputNetLabelPlacements[labelIndex] = candidate;
6026
+ this.outputTraces.push(connectorTrace);
6027
+ return true;
6028
+ }
6029
+ return false;
6030
+ }
5925
6031
  visualize() {
5926
6032
  return visualizeExample28Solver({
5927
6033
  inputProblem: this.inputProblem,
@@ -5933,6 +6039,24 @@ var Example28Solver = class extends BaseSolver {
5933
6039
  });
5934
6040
  }
5935
6041
  };
6042
+ var getPortOnlyLabelConnectorMspPairId = (label) => `port-only-label-connector-${label.globalConnNetId}-${label.pinIds[0]}`;
6043
+ var createPortOnlyLabelConnectorTrace = ({
6044
+ label,
6045
+ movedLabel,
6046
+ pinMap
6047
+ }) => {
6048
+ const mspPairId = getPortOnlyLabelConnectorMspPairId(label);
6049
+ return {
6050
+ mspPairId,
6051
+ dcConnNetId: label.dcConnNetId ?? label.globalConnNetId,
6052
+ globalConnNetId: label.globalConnNetId,
6053
+ userNetId: label.netId,
6054
+ pins: getTracePins(label, pinMap),
6055
+ tracePath: [label.anchorPoint, movedLabel.anchorPoint],
6056
+ mspConnectionPairIds: [mspPairId],
6057
+ pinIds: label.pinIds
6058
+ };
6059
+ };
5936
6060
 
5937
6061
  // lib/solvers/AvailableNetOrientationSolver/constants.ts
5938
6062
  var LABEL_SEARCH_STEP = 0.05;
@@ -6046,39 +6170,6 @@ var getMaxSearchDistance = (inputProblem) => {
6046
6170
  return maxChipWidth * 3;
6047
6171
  };
6048
6172
 
6049
- // lib/solvers/AvailableNetOrientationSolver/traces.ts
6050
- var getPinMap = (inputProblem) => {
6051
- const pinMap = {};
6052
- for (const chip of inputProblem.chips) {
6053
- for (const pin of chip.pins) {
6054
- pinMap[pin.pinId] = { ...pin, chipId: chip.chipId };
6055
- }
6056
- }
6057
- return pinMap;
6058
- };
6059
- var getTracePins = (label, pinMap) => {
6060
- const pins = label.pinIds.flatMap((pinId) => {
6061
- const pin = pinMap[pinId];
6062
- return pin ? [pin] : [];
6063
- });
6064
- if (pins.length >= 2) return [pins[0], pins[1]];
6065
- if (pins.length === 1) return [pins[0], pins[0]];
6066
- const syntheticPin = {
6067
- pinId: `${label.globalConnNetId}-netlabel-anchor`,
6068
- x: label.anchorPoint.x,
6069
- y: label.anchorPoint.y,
6070
- chipId: "available-net-orientation"
6071
- };
6072
- return [syntheticPin, syntheticPin];
6073
- };
6074
- var toNetLabelPlacementPatch = (candidate) => ({
6075
- orientation: candidate.orientation,
6076
- anchorPoint: candidate.anchorPoint,
6077
- center: candidate.center,
6078
- width: candidate.width,
6079
- height: candidate.height
6080
- });
6081
-
6082
6173
  // lib/solvers/AvailableNetOrientationSolver/visualize.ts
6083
6174
  var visualizeAvailableNetOrientationSolver = (params) => {
6084
6175
  const graphics = visualizeInputProblem(params.inputProblem);
@@ -6167,7 +6258,7 @@ var ensureGraphicsArrays = (graphics) => {
6167
6258
  };
6168
6259
 
6169
6260
  // lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
6170
- var LABEL_TRACE_CLEARANCE = 0.1;
6261
+ var LABEL_TRACE_CLEARANCE2 = 0.1;
6171
6262
  var AvailableNetOrientationSolver = class extends BaseSolver {
6172
6263
  inputProblem;
6173
6264
  traces;
@@ -6246,6 +6337,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6246
6337
  this.addConnectorTrace(label, candidate, labelIndex);
6247
6338
  }
6248
6339
  addConnectorTrace(label, candidate, labelIndex) {
6340
+ if (candidate.phase === "trace-anchor") return;
6249
6341
  const tracePath = this.getCandidateConnectorTrace(label, candidate);
6250
6342
  if (tracePath.length < 2) return;
6251
6343
  const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
@@ -6280,6 +6372,12 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6280
6372
  orientations
6281
6373
  );
6282
6374
  if (rotatedCandidate) return rotatedCandidate;
6375
+ const traceAnchorCandidate = this.findValidTraceAnchorCandidate(
6376
+ label,
6377
+ orientations[0],
6378
+ labelIndex
6379
+ );
6380
+ if (traceAnchorCandidate) return traceAnchorCandidate;
6283
6381
  const shiftedCandidate = this.findValidShiftedCandidate(
6284
6382
  label,
6285
6383
  orientations[0],
@@ -6313,6 +6411,46 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6313
6411
  }
6314
6412
  return null;
6315
6413
  }
6414
+ findValidTraceAnchorCandidate(label, orientation, labelIndex) {
6415
+ const direction = dir(orientation);
6416
+ const candidatePoints = this.getTraceAnchorCandidatePoints(label).sort(
6417
+ (a, b) => {
6418
+ const aAlongDirection = a.x * direction.x + a.y * direction.y;
6419
+ const bAlongDirection = b.x * direction.x + b.y * direction.y;
6420
+ return bAlongDirection - aAlongDirection;
6421
+ }
6422
+ );
6423
+ for (const anchorPoint of candidatePoints) {
6424
+ const candidate = this.createCandidate(label, anchorPoint, orientation);
6425
+ const result = this.evaluateCandidate(
6426
+ candidate,
6427
+ label,
6428
+ labelIndex,
6429
+ "trace-anchor"
6430
+ );
6431
+ this.currentCandidateResults.push(result);
6432
+ if (result.status === "valid") {
6433
+ result.selected = true;
6434
+ return result;
6435
+ }
6436
+ }
6437
+ return null;
6438
+ }
6439
+ getTraceAnchorCandidatePoints(label) {
6440
+ const seen = /* @__PURE__ */ new Set();
6441
+ const points = [];
6442
+ for (const traceId of label.mspConnectionPairIds ?? []) {
6443
+ const trace = this.traceMap[traceId];
6444
+ if (!trace) continue;
6445
+ for (const point of trace.tracePath) {
6446
+ const key = `${point.x.toFixed(9)},${point.y.toFixed(9)}`;
6447
+ if (seen.has(key)) continue;
6448
+ seen.add(key);
6449
+ points.push(point);
6450
+ }
6451
+ }
6452
+ return points;
6453
+ }
6316
6454
  getAvailableOrientations(label) {
6317
6455
  const effectiveNetId = label.netId ?? label.globalConnNetId;
6318
6456
  return this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? [];
@@ -6581,16 +6719,28 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6581
6719
  }
6582
6720
  getCandidateStatus(params) {
6583
6721
  const { candidate, label, labelIndex, phase } = params;
6722
+ const bounds = getRectBounds(
6723
+ candidate.center,
6724
+ candidate.width,
6725
+ candidate.height
6726
+ );
6584
6727
  const boundsStatus = this.getBoundsStatus({
6585
- bounds: getRectBounds(
6586
- candidate.center,
6587
- candidate.width,
6588
- candidate.height
6589
- ),
6728
+ bounds,
6590
6729
  labelIndex,
6591
6730
  label
6592
6731
  });
6593
- if (boundsStatus !== "valid") return boundsStatus;
6732
+ if (boundsStatus !== "valid") {
6733
+ if (phase !== "trace-anchor" || boundsStatus !== "chip-collision" || !this.isAcceptableTraceAnchorChipCollision(candidate, label, bounds)) {
6734
+ return boundsStatus;
6735
+ }
6736
+ const nonChipBoundsStatus = this.getBoundsStatus({
6737
+ bounds,
6738
+ labelIndex,
6739
+ label,
6740
+ ignoreChipCollisions: true
6741
+ });
6742
+ if (nonChipBoundsStatus !== "valid") return nonChipBoundsStatus;
6743
+ }
6594
6744
  const connectorTrace = this.getCandidateConnectorTrace(label, {
6595
6745
  anchorPoint: candidate.anchorPoint,
6596
6746
  orientation: candidate.orientation,
@@ -6613,17 +6763,35 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6613
6763
  }
6614
6764
  return "valid";
6615
6765
  }
6766
+ isAcceptableTraceAnchorChipCollision(candidate, label, bounds) {
6767
+ const collidingChips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds);
6768
+ if (collidingChips.length === 0) return false;
6769
+ const labelChipIds = new Set(
6770
+ label.pinIds.map((pinId) => this.pinMap[pinId]?.chipId).filter((chipId) => Boolean(chipId))
6771
+ );
6772
+ return collidingChips.every((chip) => {
6773
+ if (!labelChipIds.has(chip.chipId)) return false;
6774
+ const { anchorPoint, orientation } = candidate;
6775
+ const chipBounds = chip.bounds;
6776
+ if (isYOrientation(orientation)) {
6777
+ return anchorPoint.x < chipBounds.minX - EPS8 || anchorPoint.x > chipBounds.maxX + EPS8;
6778
+ }
6779
+ return anchorPoint.y < chipBounds.minY - EPS8 || anchorPoint.y > chipBounds.maxY + EPS8;
6780
+ });
6781
+ }
6616
6782
  getBoundsStatus(candidateBoundsCheck) {
6617
- const { bounds, labelIndex, label } = candidateBoundsCheck;
6618
- if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
6619
- return "chip-collision";
6783
+ const { bounds, labelIndex, label, ignoreChipCollisions } = candidateBoundsCheck;
6784
+ if (!ignoreChipCollisions) {
6785
+ if (this.chipObstacleSpatialIndex.getChipsInBounds(bounds).length > 0) {
6786
+ return "chip-collision";
6787
+ }
6788
+ if (this.sharesChipBoundary(bounds)) {
6789
+ return "chip-collision";
6790
+ }
6620
6791
  }
6621
6792
  if (rectIntersectsAnyTextBox(bounds, this.inputProblem)) {
6622
6793
  return "text-collision";
6623
6794
  }
6624
- if (this.sharesChipBoundary(bounds)) {
6625
- return "chip-collision";
6626
- }
6627
6795
  if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
6628
6796
  return "trace-collision";
6629
6797
  }
@@ -6648,10 +6816,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6648
6816
  }
6649
6817
  getLabelTraceClearanceBounds(bounds) {
6650
6818
  return {
6651
- minX: bounds.minX - LABEL_TRACE_CLEARANCE,
6652
- minY: bounds.minY - LABEL_TRACE_CLEARANCE,
6653
- maxX: bounds.maxX + LABEL_TRACE_CLEARANCE,
6654
- maxY: bounds.maxY + LABEL_TRACE_CLEARANCE
6819
+ minX: bounds.minX - LABEL_TRACE_CLEARANCE2,
6820
+ minY: bounds.minY - LABEL_TRACE_CLEARANCE2,
6821
+ maxX: bounds.maxX + LABEL_TRACE_CLEARANCE2,
6822
+ maxY: bounds.maxY + LABEL_TRACE_CLEARANCE2
6655
6823
  };
6656
6824
  }
6657
6825
  shouldCheckTraceClearanceForLabel(label) {