@tscircuit/schematic-trace-solver 0.0.62 → 0.0.64

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 (24) hide show
  1. package/dist/index.d.ts +7 -0
  2. package/dist/index.js +126 -52
  3. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +67 -36
  4. package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +13 -0
  5. package/lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts +8 -0
  6. package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +24 -0
  7. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts +7 -1
  8. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts +9 -4
  9. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts +3 -0
  10. package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +9 -13
  11. package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts +19 -0
  12. package/lib/types/InputProblem.ts +1 -0
  13. package/package.json +1 -1
  14. package/tests/assets/example41.json +2 -1
  15. package/tests/examples/__snapshots__/example34.snap.svg +23 -19
  16. package/tests/examples/__snapshots__/example41.snap.svg +2 -2
  17. package/tests/examples/example34.test.ts +8 -0
  18. package/tests/fixtures/traceLabelCollisions.ts +42 -0
  19. package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +273 -0
  20. package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +113 -0
  21. package/tests/repros/assets/repro-netlabel-connector-through-label.input.json +139 -0
  22. package/tests/repros/assets/repro126-trace-overlap-box-resistor.input.json +74 -0
  23. package/tests/repros/netlabel-connector-through-rail-label.test.ts +19 -0
  24. package/tests/repros/trace-overlap-box-resistor.test.ts +14 -0
package/dist/index.d.ts CHANGED
@@ -83,6 +83,7 @@ interface InputNetConnection {
83
83
  netId: string;
84
84
  pinIds: Array<PinId>;
85
85
  netLabelWidth?: number;
86
+ netLabelHeight?: number;
86
87
  }
87
88
  interface InputProblem {
88
89
  chips: Array<InputChip>;
@@ -288,6 +289,7 @@ declare class SingleNetLabelPlacementSolver extends BaseSolver {
288
289
  availableOrientations: Array<FacingDirection>;
289
290
  chipObstacleSpatialIndex: ChipObstacleSpatialIndex;
290
291
  netLabelWidth?: number;
292
+ netLabelHeight?: number;
291
293
  netLabelPlacement: NetLabelPlacement | null;
292
294
  testedCandidates: Array<{
293
295
  center: {
@@ -316,6 +318,7 @@ declare class SingleNetLabelPlacementSolver extends BaseSolver {
316
318
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup;
317
319
  availableOrientations: FacingDirection[];
318
320
  netLabelWidth?: number;
321
+ netLabelHeight?: number;
319
322
  });
320
323
  getConstructorParams(): ConstructorParameters<typeof SingleNetLabelPlacementSolver>[0];
321
324
  _step(): void;
@@ -385,6 +388,7 @@ declare class NetLabelPlacementSolver extends BaseSolver {
385
388
  });
386
389
  computeOverlappingSameNetTraceGroups(): Array<OverlappingSameNetTraceGroup>;
387
390
  private getNetLabelWidthForGroup;
391
+ private getNetLabelHeightForGroup;
388
392
  _step(): void;
389
393
  visualize(): GraphicsObject;
390
394
  }
@@ -700,6 +704,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
700
704
  private findValidLateralShiftedCandidate;
701
705
  private findValidCandidateInShiftColumn;
702
706
  private evaluateCandidate;
707
+ private getCandidateConnectorTrace;
703
708
  private getSearchStartAnchor;
704
709
  private getWickOffsetAnchor;
705
710
  private getSideOffsetAnchor;
@@ -707,6 +712,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
707
712
  private getLateralColumnMaxDistance;
708
713
  private createCandidate;
709
714
  private getNetLabelWidth;
715
+ private getNetLabelHeight;
710
716
  private getCandidateStatus;
711
717
  private getBoundsStatus;
712
718
  private intersectsAnyOtherNetLabel;
@@ -911,6 +917,7 @@ declare class NetLabelNetLabelCollisionSolver extends BaseSolver {
911
917
  private collisionKey;
912
918
  private findNextCollidingPair;
913
919
  private netLabelWidthOf;
920
+ private netLabelHeightOf;
914
921
  private buildCandidatesForLabel;
915
922
  private checkCandidate;
916
923
  private beginSearchForLabel;
package/dist/index.js CHANGED
@@ -1511,18 +1511,19 @@ var ChipObstacleSpatialIndex = class {
1511
1511
  var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
1512
1512
  var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
1513
1513
  function getDimsForOrientation(params) {
1514
- const { orientation, netLabelWidth } = params;
1514
+ const { orientation, netLabelWidth, netLabelHeight } = params;
1515
1515
  const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
1516
+ const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
1516
1517
  if (orientation === "y+" || orientation === "y-") {
1517
1518
  return {
1518
- // Rotated, so width/height swap
1519
- width: NET_LABEL_HORIZONTAL_HEIGHT,
1519
+ // Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
1520
+ width: horizHeight,
1520
1521
  height: horizWidth
1521
1522
  };
1522
1523
  }
1523
1524
  return {
1524
1525
  width: horizWidth,
1525
- height: NET_LABEL_HORIZONTAL_HEIGHT
1526
+ height: horizHeight
1526
1527
  };
1527
1528
  }
1528
1529
  function getCenterFromAnchor(anchor, orientation, width, height) {
@@ -1651,7 +1652,8 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1651
1652
  chipObstacleSpatialIndex,
1652
1653
  overlappingSameNetTraceGroup,
1653
1654
  availableOrientations,
1654
- netLabelWidth
1655
+ netLabelWidth,
1656
+ netLabelHeight
1655
1657
  } = params;
1656
1658
  const pinId = overlappingSameNetTraceGroup.portOnlyPinId;
1657
1659
  if (!pinId) {
@@ -1685,7 +1687,8 @@ function solveNetLabelPlacementForPortOnlyPin(params) {
1685
1687
  for (const orientation of orientations) {
1686
1688
  const { width: width2, height: height2 } = getDimsForOrientation({
1687
1689
  orientation,
1688
- netLabelWidth
1690
+ netLabelWidth,
1691
+ netLabelHeight
1689
1692
  });
1690
1693
  const baseCenter2 = getCenterFromAnchor(anchor, orientation, width2, height2);
1691
1694
  const outward2 = outwardOf(orientation);
@@ -1857,8 +1860,9 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1857
1860
  overlappingSameNetTraceGroup;
1858
1861
  availableOrientations;
1859
1862
  chipObstacleSpatialIndex;
1860
- // Optional override for the width of the net label (per netId)
1863
+ // Optional override for the width/height of the net label (per netId)
1861
1864
  netLabelWidth;
1865
+ netLabelHeight;
1862
1866
  netLabelPlacement = null;
1863
1867
  testedCandidates = [];
1864
1868
  constructor(params) {
@@ -1868,6 +1872,7 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1868
1872
  this.overlappingSameNetTraceGroup = params.overlappingSameNetTraceGroup;
1869
1873
  this.availableOrientations = params.availableOrientations;
1870
1874
  this.netLabelWidth = params.netLabelWidth;
1875
+ this.netLabelHeight = params.netLabelHeight;
1871
1876
  this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
1872
1877
  }
1873
1878
  getConstructorParams() {
@@ -1876,7 +1881,8 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1876
1881
  inputTraceMap: this.inputTraceMap,
1877
1882
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
1878
1883
  availableOrientations: this.availableOrientations,
1879
- netLabelWidth: this.netLabelWidth
1884
+ netLabelWidth: this.netLabelWidth,
1885
+ netLabelHeight: this.netLabelHeight
1880
1886
  };
1881
1887
  }
1882
1888
  _step() {
@@ -1891,7 +1897,8 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1891
1897
  chipObstacleSpatialIndex: this.chipObstacleSpatialIndex,
1892
1898
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
1893
1899
  availableOrientations: this.availableOrientations,
1894
- netLabelWidth: this.netLabelWidth
1900
+ netLabelWidth: this.netLabelWidth,
1901
+ netLabelHeight: this.netLabelHeight
1895
1902
  });
1896
1903
  this.testedCandidates.push(...res.testedCandidates);
1897
1904
  if (res.placement) {
@@ -1961,7 +1968,8 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1961
1968
  for (const orientation of candidateOrients) {
1962
1969
  const { width, height } = getDimsForOrientation({
1963
1970
  orientation,
1964
- netLabelWidth: this.netLabelWidth
1971
+ netLabelWidth: this.netLabelWidth,
1972
+ netLabelHeight: this.netLabelHeight
1965
1973
  });
1966
1974
  const center = getCenterFromAnchor(
1967
1975
  anchor,
@@ -2250,6 +2258,21 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2250
2258
  (nc) => nc.pinIds.some((pid) => pinIds.includes(pid))
2251
2259
  )?.netLabelWidth;
2252
2260
  }
2261
+ getNetLabelHeightForGroup(group) {
2262
+ if (group.netId) {
2263
+ const ncHeight = this.inputProblem.netConnections.find(
2264
+ (nc) => nc.netId === group.netId
2265
+ )?.netLabelHeight;
2266
+ if (ncHeight !== void 0) return ncHeight;
2267
+ }
2268
+ const pinIds = group.overlappingTraces?.pins.map((p) => p.pinId) ?? [];
2269
+ if (group.portOnlyPinId) {
2270
+ pinIds.push(group.portOnlyPinId);
2271
+ }
2272
+ return this.inputProblem.netConnections.find(
2273
+ (nc) => nc.pinIds.some((pid) => pinIds.includes(pid))
2274
+ )?.netLabelHeight;
2275
+ }
2253
2276
  _step() {
2254
2277
  if (this.activeSubSolver?.solved) {
2255
2278
  this.netLabelPlacements.push(this.activeSubSolver.netLabelPlacement);
@@ -2265,12 +2288,14 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2265
2288
  if (!this.triedAnyOrientationFallbackForCurrentGroup && !isAlreadyFull && this.currentGroup) {
2266
2289
  this.triedAnyOrientationFallbackForCurrentGroup = true;
2267
2290
  const netLabelWidth2 = this.getNetLabelWidthForGroup(this.currentGroup);
2291
+ const netLabelHeight2 = this.getNetLabelHeightForGroup(this.currentGroup);
2268
2292
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
2269
2293
  inputProblem: this.inputProblem,
2270
2294
  inputTraceMap: this.inputTraceMap,
2271
2295
  overlappingSameNetTraceGroup: this.currentGroup,
2272
2296
  availableOrientations: fullOrients,
2273
- netLabelWidth: netLabelWidth2
2297
+ netLabelWidth: netLabelWidth2,
2298
+ netLabelHeight: netLabelHeight2
2274
2299
  });
2275
2300
  return;
2276
2301
  }
@@ -2291,12 +2316,14 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2291
2316
  this.currentGroup = nextOverlappingSameNetTraceGroup;
2292
2317
  this.triedAnyOrientationFallbackForCurrentGroup = false;
2293
2318
  const netLabelWidth = this.getNetLabelWidthForGroup(this.currentGroup);
2319
+ const netLabelHeight = this.getNetLabelHeightForGroup(this.currentGroup);
2294
2320
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
2295
2321
  inputProblem: this.inputProblem,
2296
2322
  inputTraceMap: this.inputTraceMap,
2297
2323
  overlappingSameNetTraceGroup: nextOverlappingSameNetTraceGroup,
2298
2324
  availableOrientations: this.inputProblem.availableNetLabelOrientations[netId] ?? ["x+", "x-", "y+", "y-"],
2299
- netLabelWidth
2325
+ netLabelWidth,
2326
+ netLabelHeight
2300
2327
  });
2301
2328
  }
2302
2329
  visualize() {
@@ -5609,6 +5636,14 @@ var tracePathCrossesAnyBounds = (tracePath, bounds) => {
5609
5636
  }
5610
5637
  return false;
5611
5638
  };
5639
+ var tracePathIntersectsBounds = (tracePath, bounds) => {
5640
+ for (let i = 0; i < tracePath.length - 1; i++) {
5641
+ if (segmentIntersectsRect2(tracePath[i], tracePath[i + 1], bounds)) {
5642
+ return true;
5643
+ }
5644
+ }
5645
+ return false;
5646
+ };
5612
5647
  var tracePathCrossesAnyTrace = (tracePath, traceMap) => {
5613
5648
  for (const trace of Object.values(traceMap)) {
5614
5649
  const points = trace.tracePath;
@@ -5873,28 +5908,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5873
5908
  this.addConnectorTrace(label, candidate, labelIndex);
5874
5909
  }
5875
5910
  addConnectorTrace(label, candidate, labelIndex) {
5876
- let tracePath;
5877
- if (candidate.phase === "lateral-shift") {
5878
- const orientDir = dir(candidate.orientation);
5879
- const kickedSource = {
5880
- x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
5881
- y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP
5882
- };
5883
- tracePath = simplifyOrthogonalPath([
5884
- label.anchorPoint,
5885
- ...getConnectorTracePath(
5886
- kickedSource,
5887
- candidate.anchorPoint,
5888
- candidate.orientation
5889
- )
5890
- ]);
5891
- } else {
5892
- tracePath = getConnectorTracePath(
5893
- label.anchorPoint,
5894
- candidate.anchorPoint,
5895
- candidate.orientation
5896
- );
5897
- }
5911
+ const tracePath = this.getCandidateConnectorTrace(label, candidate);
5898
5912
  if (tracePath.length < 2) return;
5899
5913
  const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
5900
5914
  const connectorTrace = {
@@ -6077,14 +6091,42 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6077
6091
  distance: distance3,
6078
6092
  outwardDistance,
6079
6093
  selected: false,
6080
- status: this.getCandidateStatus(candidate, label, labelIndex)
6094
+ status: this.getCandidateStatus({
6095
+ candidate,
6096
+ label,
6097
+ labelIndex,
6098
+ phase
6099
+ })
6081
6100
  };
6082
6101
  }
6102
+ getCandidateConnectorTrace(label, candidate) {
6103
+ if (candidate.phase === "lateral-shift") {
6104
+ const orientDir = dir(candidate.orientation);
6105
+ const kickedSource = {
6106
+ x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
6107
+ y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP
6108
+ };
6109
+ return simplifyOrthogonalPath([
6110
+ label.anchorPoint,
6111
+ ...getConnectorTracePath(
6112
+ kickedSource,
6113
+ candidate.anchorPoint,
6114
+ candidate.orientation
6115
+ )
6116
+ ]);
6117
+ }
6118
+ return getConnectorTracePath(
6119
+ label.anchorPoint,
6120
+ candidate.anchorPoint,
6121
+ candidate.orientation
6122
+ );
6123
+ }
6083
6124
  getSearchStartAnchor(label, orientation) {
6084
6125
  const anchorPoint = this.getWickOffsetAnchor(label.anchorPoint, orientation);
6085
6126
  const { width, height } = getDimsForOrientation({
6086
6127
  orientation,
6087
- netLabelWidth: this.getNetLabelWidth(label)
6128
+ netLabelWidth: this.getNetLabelWidth(label),
6129
+ netLabelHeight: this.getNetLabelHeight(label)
6088
6130
  });
6089
6131
  return this.getSideOffsetAnchor({
6090
6132
  anchorPoint,
@@ -6133,7 +6175,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6133
6175
  getSearchDistanceLimit(label, orientation) {
6134
6176
  const { width, height } = getDimsForOrientation({
6135
6177
  orientation,
6136
- netLabelWidth: this.getNetLabelWidth(label)
6178
+ netLabelWidth: this.getNetLabelWidth(label),
6179
+ netLabelHeight: this.getNetLabelHeight(label)
6137
6180
  });
6138
6181
  const labelLength = orientation === "y+" || orientation === "y-" ? height : width;
6139
6182
  return Math.min(this.maxSearchDistance, labelLength * 2);
@@ -6156,7 +6199,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6156
6199
  createCandidate(label, anchorPoint, orientation) {
6157
6200
  const { width, height } = getDimsForOrientation({
6158
6201
  orientation,
6159
- netLabelWidth: this.getNetLabelWidth(label)
6202
+ netLabelWidth: this.getNetLabelWidth(label),
6203
+ netLabelHeight: this.getNetLabelHeight(label)
6160
6204
  });
6161
6205
  return {
6162
6206
  orientation,
@@ -6185,17 +6229,29 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6185
6229
  (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
6186
6230
  )?.netLabelWidth;
6187
6231
  }
6188
- getCandidateStatus(candidate, label, labelIndex) {
6232
+ getNetLabelHeight(label) {
6233
+ if (label.netId) {
6234
+ const ncHeight = this.inputProblem.netConnections.find(
6235
+ (connection) => connection.netId === label.netId
6236
+ )?.netLabelHeight;
6237
+ if (ncHeight !== void 0) return ncHeight;
6238
+ }
6239
+ return this.inputProblem.netConnections.find(
6240
+ (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
6241
+ )?.netLabelHeight;
6242
+ }
6243
+ getCandidateStatus(params) {
6244
+ const { candidate, label, labelIndex, phase } = params;
6189
6245
  const boundsStatus = this.getBoundsStatus(
6190
6246
  getRectBounds(candidate.center, candidate.width, candidate.height),
6191
6247
  labelIndex
6192
6248
  );
6193
6249
  if (boundsStatus !== "valid") return boundsStatus;
6194
- const connectorTrace = getConnectorTracePath(
6195
- label.anchorPoint,
6196
- candidate.anchorPoint,
6197
- candidate.orientation
6198
- );
6250
+ const connectorTrace = this.getCandidateConnectorTrace(label, {
6251
+ anchorPoint: candidate.anchorPoint,
6252
+ orientation: candidate.orientation,
6253
+ phase
6254
+ });
6199
6255
  if (tracePathCrossesAnyTrace(connectorTrace, this.traceMap)) {
6200
6256
  return "trace-collision";
6201
6257
  }
@@ -6207,7 +6263,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6207
6263
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
6208
6264
  if (i === labelIndex) continue;
6209
6265
  const otherLabel = this.outputNetLabelPlacements[i];
6210
- if (tracePathCrossesAnyBounds(
6266
+ if (tracePathIntersectsBounds(
6211
6267
  connectorTrace,
6212
6268
  getRectBounds(otherLabel.center, otherLabel.width, otherLabel.height)
6213
6269
  )) {
@@ -6798,6 +6854,7 @@ var generateCandidatesAlongTrace = (params) => {
6798
6854
  getTraceVertexDistances(traceLocation.trace)
6799
6855
  );
6800
6856
  const netLabelWidth = getNetLabelWidth(inputProblem, label);
6857
+ const netLabelHeight = getNetLabelHeight(inputProblem, label);
6801
6858
  const candidates = [];
6802
6859
  const seenCandidateKeys = /* @__PURE__ */ new Set();
6803
6860
  for (const pathDistance of candidateDistances) {
@@ -6820,6 +6877,7 @@ var generateCandidatesAlongTrace = (params) => {
6820
6877
  point,
6821
6878
  orientation,
6822
6879
  netLabelWidth,
6880
+ netLabelHeight,
6823
6881
  traceLocation,
6824
6882
  pathDistance,
6825
6883
  label
@@ -6857,13 +6915,15 @@ var createCandidate = (params) => {
6857
6915
  point,
6858
6916
  orientation,
6859
6917
  netLabelWidth,
6918
+ netLabelHeight,
6860
6919
  traceLocation,
6861
6920
  pathDistance,
6862
6921
  label
6863
6922
  } = params;
6864
6923
  const { width, height } = getDimsForOrientation({
6865
6924
  orientation,
6866
- netLabelWidth
6925
+ netLabelWidth,
6926
+ netLabelHeight
6867
6927
  });
6868
6928
  return {
6869
6929
  anchorPoint: point,
@@ -7026,6 +7086,15 @@ var getNetLabelWidth = (inputProblem, label) => {
7026
7086
  }
7027
7087
  return label.width;
7028
7088
  };
7089
+ var getNetLabelHeight = (inputProblem, label) => {
7090
+ const ncHeightByNetId = inputProblem.netConnections.find(
7091
+ (connection) => connection.netId === label.netId
7092
+ )?.netLabelHeight;
7093
+ if (ncHeightByNetId !== void 0) return ncHeightByNetId;
7094
+ return inputProblem.netConnections.find(
7095
+ (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
7096
+ )?.netLabelHeight;
7097
+ };
7029
7098
  var roundDistance = (distance3) => Number(distance3.toFixed(6));
7030
7099
  var dedupeOrientations = (orientations) => [
7031
7100
  ...new Set(orientations)
@@ -7437,16 +7506,14 @@ function buildMergedObstacleLabel(seedLabel, allLabels) {
7437
7506
  height: h
7438
7507
  };
7439
7508
  }
7440
- function detectInnerTraceLabelOverlaps(traces, labels) {
7509
+ function detectTraceLabelOverlaps(traces, labels) {
7441
7510
  const overlaps = [];
7442
7511
  for (const trace of traces) {
7443
7512
  for (const label of labels) {
7444
7513
  if (trace.globalConnNetId === label.globalConnNetId) continue;
7445
7514
  const bounds = getRectBounds(label.center, label.width, label.height);
7446
7515
  const path = trace.tracePath;
7447
- const innerStart = 1;
7448
- const innerEnd = path.length - 2;
7449
- for (let i = innerStart; i < innerEnd; i++) {
7516
+ for (let i = 0; i < path.length - 1; i++) {
7450
7517
  if (segmentIntersectsRect2(path[i], path[i + 1], bounds)) {
7451
7518
  overlaps.push({ trace, label });
7452
7519
  break;
@@ -7512,7 +7579,7 @@ var NetLabelTraceCollisionSolver = class extends BaseSolver {
7512
7579
  }
7513
7580
  return;
7514
7581
  }
7515
- const overlaps = detectInnerTraceLabelOverlaps(
7582
+ const overlaps = detectTraceLabelOverlaps(
7516
7583
  this.outputTraces,
7517
7584
  this.outputNetLabelPlacements
7518
7585
  );
@@ -7702,13 +7769,20 @@ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
7702
7769
  return label.width;
7703
7770
  return label.height;
7704
7771
  }
7772
+ netLabelHeightOf(label) {
7773
+ if (label.orientation === "x+" || label.orientation === "x-")
7774
+ return label.height;
7775
+ return label.width;
7776
+ }
7705
7777
  buildCandidatesForLabel(label) {
7706
7778
  const netLabelWidth = this.netLabelWidthOf(label);
7779
+ const netLabelHeight = this.netLabelHeightOf(label);
7707
7780
  const candidates = [];
7708
7781
  const buildCandidate = (orientation, anchor, hostPairId, hostSegIndex) => {
7709
7782
  const { width, height } = getDimsForOrientation({
7710
7783
  orientation,
7711
- netLabelWidth
7784
+ netLabelWidth,
7785
+ netLabelHeight
7712
7786
  });
7713
7787
  const baseCenter = getCenterFromAnchor(anchor, orientation, width, height);
7714
7788
  const outwardDir = OUTWARD_DIR[orientation];
@@ -22,6 +22,7 @@ import {
22
22
  rectsOverlap,
23
23
  simplifyOrthogonalPath,
24
24
  traceCrossesBoundsInterior,
25
+ tracePathIntersectsBounds,
25
26
  tracePathCrossesAnyBounds,
26
27
  tracePathCrossesAnyTrace,
27
28
  } from "./geometry"
@@ -143,30 +144,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
143
144
  candidate: EvaluatedCandidate,
144
145
  labelIndex: number,
145
146
  ) {
146
- let tracePath: Point[]
147
-
148
- if (candidate.phase === "lateral-shift") {
149
- const orientDir = dir(candidate.orientation)
150
- const kickedSource = {
151
- x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
152
- y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP,
153
- }
154
- tracePath = simplifyOrthogonalPath([
155
- label.anchorPoint,
156
- ...getConnectorTracePath(
157
- kickedSource,
158
- candidate.anchorPoint,
159
- candidate.orientation,
160
- ),
161
- ])
162
- } else {
163
- tracePath = getConnectorTracePath(
164
- label.anchorPoint,
165
- candidate.anchorPoint,
166
- candidate.orientation,
167
- )
168
- }
169
-
147
+ const tracePath = this.getCandidateConnectorTrace(label, candidate)
170
148
  if (tracePath.length < 2) return
171
149
 
172
150
  const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`
@@ -417,8 +395,43 @@ export class AvailableNetOrientationSolver extends BaseSolver {
417
395
  distance,
418
396
  outwardDistance,
419
397
  selected: false,
420
- status: this.getCandidateStatus(candidate, label, labelIndex),
398
+ status: this.getCandidateStatus({
399
+ candidate,
400
+ label,
401
+ labelIndex,
402
+ phase,
403
+ }),
404
+ }
405
+ }
406
+
407
+ private getCandidateConnectorTrace(
408
+ label: NetLabelPlacement,
409
+ candidate: Pick<
410
+ EvaluatedCandidate,
411
+ "anchorPoint" | "orientation" | "phase"
412
+ >,
413
+ ) {
414
+ if (candidate.phase === "lateral-shift") {
415
+ const orientDir = dir(candidate.orientation)
416
+ const kickedSource = {
417
+ x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
418
+ y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP,
419
+ }
420
+ return simplifyOrthogonalPath([
421
+ label.anchorPoint,
422
+ ...getConnectorTracePath(
423
+ kickedSource,
424
+ candidate.anchorPoint,
425
+ candidate.orientation,
426
+ ),
427
+ ])
421
428
  }
429
+
430
+ return getConnectorTracePath(
431
+ label.anchorPoint,
432
+ candidate.anchorPoint,
433
+ candidate.orientation,
434
+ )
422
435
  }
423
436
 
424
437
  private getSearchStartAnchor(
@@ -429,6 +442,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
429
442
  const { width, height } = getDimsForOrientation({
430
443
  orientation,
431
444
  netLabelWidth: this.getNetLabelWidth(label),
445
+ netLabelHeight: this.getNetLabelHeight(label),
432
446
  })
433
447
 
434
448
  return this.getSideOffsetAnchor({
@@ -494,6 +508,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
494
508
  const { width, height } = getDimsForOrientation({
495
509
  orientation,
496
510
  netLabelWidth: this.getNetLabelWidth(label),
511
+ netLabelHeight: this.getNetLabelHeight(label),
497
512
  })
498
513
  const labelLength =
499
514
  orientation === "y+" || orientation === "y-" ? height : width
@@ -534,6 +549,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
534
549
  const { width, height } = getDimsForOrientation({
535
550
  orientation,
536
551
  netLabelWidth: this.getNetLabelWidth(label),
552
+ netLabelHeight: this.getNetLabelHeight(label),
537
553
  })
538
554
  return {
539
555
  orientation,
@@ -567,22 +583,37 @@ export class AvailableNetOrientationSolver extends BaseSolver {
567
583
  )?.netLabelWidth
568
584
  }
569
585
 
570
- private getCandidateStatus(
571
- candidate: CandidateLabel,
572
- label: NetLabelPlacement,
573
- labelIndex: number,
574
- ) {
586
+ private getNetLabelHeight(label: NetLabelPlacement) {
587
+ if (label.netId) {
588
+ const ncHeight = this.inputProblem.netConnections.find(
589
+ (connection) => connection.netId === label.netId,
590
+ )?.netLabelHeight
591
+ if (ncHeight !== undefined) return ncHeight
592
+ }
593
+
594
+ return this.inputProblem.netConnections.find((nc) =>
595
+ nc.pinIds.some((pid) => label.pinIds.includes(pid)),
596
+ )?.netLabelHeight
597
+ }
598
+
599
+ private getCandidateStatus(params: {
600
+ candidate: CandidateLabel
601
+ label: NetLabelPlacement
602
+ labelIndex: number
603
+ phase: CandidatePhase
604
+ }) {
605
+ const { candidate, label, labelIndex, phase } = params
575
606
  const boundsStatus = this.getBoundsStatus(
576
607
  getRectBounds(candidate.center, candidate.width, candidate.height),
577
608
  labelIndex,
578
609
  )
579
610
  if (boundsStatus !== "valid") return boundsStatus
580
611
 
581
- const connectorTrace = getConnectorTracePath(
582
- label.anchorPoint,
583
- candidate.anchorPoint,
584
- candidate.orientation,
585
- )
612
+ const connectorTrace = this.getCandidateConnectorTrace(label, {
613
+ anchorPoint: candidate.anchorPoint,
614
+ orientation: candidate.orientation,
615
+ phase,
616
+ })
586
617
 
587
618
  if (tracePathCrossesAnyTrace(connectorTrace, this.traceMap)) {
588
619
  return "trace-collision"
@@ -598,7 +629,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
598
629
  if (i === labelIndex) continue
599
630
  const otherLabel = this.outputNetLabelPlacements[i]!
600
631
  if (
601
- tracePathCrossesAnyBounds(
632
+ tracePathIntersectsBounds(
602
633
  connectorTrace,
603
634
  getRectBounds(otherLabel.center, otherLabel.width, otherLabel.height),
604
635
  )
@@ -2,6 +2,7 @@ import type { Point } from "@tscircuit/math-utils"
2
2
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
3
3
  import type { InputProblem } from "lib/types/InputProblem"
4
4
  import type { FacingDirection } from "lib/utils/dir"
5
+ import { segmentIntersectsRect } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
5
6
  import { EPS, TRACE_BOUNDARY_TOLERANCE } from "./constants"
6
7
  import type { Bounds, ChipSide } from "./types"
7
8
 
@@ -102,6 +103,18 @@ export const tracePathCrossesAnyBounds = (
102
103
  return false
103
104
  }
104
105
 
106
+ export const tracePathIntersectsBounds = (
107
+ tracePath: Point[],
108
+ bounds: Bounds,
109
+ ) => {
110
+ for (let i = 0; i < tracePath.length - 1; i++) {
111
+ if (segmentIntersectsRect(tracePath[i]!, tracePath[i + 1]!, bounds)) {
112
+ return true
113
+ }
114
+ }
115
+ return false
116
+ }
117
+
105
118
  export const tracePathCrossesAnyTrace = (
106
119
  tracePath: Point[],
107
120
  traceMap: Record<string, SolvedTracePath>,
@@ -170,8 +170,15 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
170
170
  return label.height
171
171
  }
172
172
 
173
+ private netLabelHeightOf(label: NetLabelPlacement): number | undefined {
174
+ if (label.orientation === "x+" || label.orientation === "x-")
175
+ return label.height
176
+ return label.width
177
+ }
178
+
173
179
  private buildCandidatesForLabel(label: NetLabelPlacement): Candidate[] {
174
180
  const netLabelWidth = this.netLabelWidthOf(label)
181
+ const netLabelHeight = this.netLabelHeightOf(label)
175
182
  const candidates: Candidate[] = []
176
183
 
177
184
  const buildCandidate = (
@@ -183,6 +190,7 @@ export class NetLabelNetLabelCollisionSolver extends BaseSolver {
183
190
  const { width, height } = getDimsForOrientation({
184
191
  orientation,
185
192
  netLabelWidth,
193
+ netLabelHeight,
186
194
  })
187
195
  const baseCenter = getCenterFromAnchor(anchor, orientation, width, height)
188
196
  const outwardDir = OUTWARD_DIR[orientation]