@tscircuit/schematic-trace-solver 0.0.61 → 0.0.63

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/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() {
@@ -6084,7 +6111,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6084
6111
  const anchorPoint = this.getWickOffsetAnchor(label.anchorPoint, orientation);
6085
6112
  const { width, height } = getDimsForOrientation({
6086
6113
  orientation,
6087
- netLabelWidth: this.getNetLabelWidth(label)
6114
+ netLabelWidth: this.getNetLabelWidth(label),
6115
+ netLabelHeight: this.getNetLabelHeight(label)
6088
6116
  });
6089
6117
  return this.getSideOffsetAnchor({
6090
6118
  anchorPoint,
@@ -6133,7 +6161,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6133
6161
  getSearchDistanceLimit(label, orientation) {
6134
6162
  const { width, height } = getDimsForOrientation({
6135
6163
  orientation,
6136
- netLabelWidth: this.getNetLabelWidth(label)
6164
+ netLabelWidth: this.getNetLabelWidth(label),
6165
+ netLabelHeight: this.getNetLabelHeight(label)
6137
6166
  });
6138
6167
  const labelLength = orientation === "y+" || orientation === "y-" ? height : width;
6139
6168
  return Math.min(this.maxSearchDistance, labelLength * 2);
@@ -6156,7 +6185,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6156
6185
  createCandidate(label, anchorPoint, orientation) {
6157
6186
  const { width, height } = getDimsForOrientation({
6158
6187
  orientation,
6159
- netLabelWidth: this.getNetLabelWidth(label)
6188
+ netLabelWidth: this.getNetLabelWidth(label),
6189
+ netLabelHeight: this.getNetLabelHeight(label)
6160
6190
  });
6161
6191
  return {
6162
6192
  orientation,
@@ -6185,6 +6215,17 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6185
6215
  (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
6186
6216
  )?.netLabelWidth;
6187
6217
  }
6218
+ getNetLabelHeight(label) {
6219
+ if (label.netId) {
6220
+ const ncHeight = this.inputProblem.netConnections.find(
6221
+ (connection) => connection.netId === label.netId
6222
+ )?.netLabelHeight;
6223
+ if (ncHeight !== void 0) return ncHeight;
6224
+ }
6225
+ return this.inputProblem.netConnections.find(
6226
+ (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
6227
+ )?.netLabelHeight;
6228
+ }
6188
6229
  getCandidateStatus(candidate, label, labelIndex) {
6189
6230
  const boundsStatus = this.getBoundsStatus(
6190
6231
  getRectBounds(candidate.center, candidate.width, candidate.height),
@@ -6798,6 +6839,7 @@ var generateCandidatesAlongTrace = (params) => {
6798
6839
  getTraceVertexDistances(traceLocation.trace)
6799
6840
  );
6800
6841
  const netLabelWidth = getNetLabelWidth(inputProblem, label);
6842
+ const netLabelHeight = getNetLabelHeight(inputProblem, label);
6801
6843
  const candidates = [];
6802
6844
  const seenCandidateKeys = /* @__PURE__ */ new Set();
6803
6845
  for (const pathDistance of candidateDistances) {
@@ -6820,6 +6862,7 @@ var generateCandidatesAlongTrace = (params) => {
6820
6862
  point,
6821
6863
  orientation,
6822
6864
  netLabelWidth,
6865
+ netLabelHeight,
6823
6866
  traceLocation,
6824
6867
  pathDistance,
6825
6868
  label
@@ -6857,13 +6900,15 @@ var createCandidate = (params) => {
6857
6900
  point,
6858
6901
  orientation,
6859
6902
  netLabelWidth,
6903
+ netLabelHeight,
6860
6904
  traceLocation,
6861
6905
  pathDistance,
6862
6906
  label
6863
6907
  } = params;
6864
6908
  const { width, height } = getDimsForOrientation({
6865
6909
  orientation,
6866
- netLabelWidth
6910
+ netLabelWidth,
6911
+ netLabelHeight
6867
6912
  });
6868
6913
  return {
6869
6914
  anchorPoint: point,
@@ -7026,6 +7071,15 @@ var getNetLabelWidth = (inputProblem, label) => {
7026
7071
  }
7027
7072
  return label.width;
7028
7073
  };
7074
+ var getNetLabelHeight = (inputProblem, label) => {
7075
+ const ncHeightByNetId = inputProblem.netConnections.find(
7076
+ (connection) => connection.netId === label.netId
7077
+ )?.netLabelHeight;
7078
+ if (ncHeightByNetId !== void 0) return ncHeightByNetId;
7079
+ return inputProblem.netConnections.find(
7080
+ (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
7081
+ )?.netLabelHeight;
7082
+ };
7029
7083
  var roundDistance = (distance3) => Number(distance3.toFixed(6));
7030
7084
  var dedupeOrientations = (orientations) => [
7031
7085
  ...new Set(orientations)
@@ -7605,6 +7659,345 @@ globalConnNetId: ${label.globalConnNetId}`
7605
7659
  }
7606
7660
  };
7607
7661
 
7662
+ // lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts
7663
+ var ANCHOR_TRACE_CLEARANCE = 1e-4;
7664
+ var SEGMENT_PARALLEL_EPS = 1e-6;
7665
+ var CANDIDATE_STEP2 = 0.1;
7666
+ var OUTWARD_DIR = {
7667
+ "x+": { x: 1, y: 0 },
7668
+ "x-": { x: -1, y: 0 },
7669
+ "y+": { x: 0, y: 1 },
7670
+ "y-": { x: 0, y: -1 }
7671
+ };
7672
+ var CANDIDATE_STATUS_COLOR = {
7673
+ ok: "green",
7674
+ "label-collision": "orange",
7675
+ "trace-collision": "darkorange",
7676
+ "chip-collision": "red"
7677
+ };
7678
+ var CANDIDATE_STATUS_FILL = {
7679
+ ok: "rgba(0, 200, 0, 0.25)",
7680
+ "label-collision": "rgba(255, 160, 0, 0.2)",
7681
+ "trace-collision": "rgba(200, 80, 0, 0.2)",
7682
+ "chip-collision": "rgba(220, 0, 0, 0.15)"
7683
+ };
7684
+ function boundsOverlap(a, b) {
7685
+ return a.minX < b.maxX - 1e-9 && a.maxX > b.minX + 1e-9 && a.minY < b.maxY - 1e-9 && a.maxY > b.minY + 1e-9;
7686
+ }
7687
+ function sampleAnchorsAlongSegment(a, b) {
7688
+ const dx = b.x - a.x;
7689
+ const dy = b.y - a.y;
7690
+ const len = Math.sqrt(dx * dx + dy * dy);
7691
+ const steps = Math.max(1, Math.round(len / CANDIDATE_STEP2));
7692
+ const anchors = [];
7693
+ for (let k = 0; k <= steps; k++) {
7694
+ const t = k / steps;
7695
+ anchors.push({ x: a.x + t * dx, y: a.y + t * dy });
7696
+ }
7697
+ return anchors;
7698
+ }
7699
+ var NetLabelNetLabelCollisionSolver = class extends BaseSolver {
7700
+ inputProblem;
7701
+ traces;
7702
+ netLabelPlacements;
7703
+ outputNetLabelPlacements;
7704
+ currentCollision = null;
7705
+ currentLabelToMove = null;
7706
+ candidateResults = [];
7707
+ chipIndex;
7708
+ traceMap;
7709
+ skippedCollisionKeys = /* @__PURE__ */ new Set();
7710
+ labelsToTry = [];
7711
+ candidateQueue = [];
7712
+ candidateIndex = 0;
7713
+ constructor(params) {
7714
+ super();
7715
+ this.inputProblem = params.inputProblem;
7716
+ this.traces = params.traces;
7717
+ this.netLabelPlacements = params.netLabelPlacements;
7718
+ this.outputNetLabelPlacements = [...params.netLabelPlacements];
7719
+ this.chipIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
7720
+ this.traceMap = Object.fromEntries(
7721
+ params.traces.map((t) => [t.mspPairId, t])
7722
+ );
7723
+ }
7724
+ getConstructorParams() {
7725
+ return {
7726
+ inputProblem: this.inputProblem,
7727
+ traces: this.traces,
7728
+ netLabelPlacements: this.netLabelPlacements
7729
+ };
7730
+ }
7731
+ getOutput() {
7732
+ return { netLabelPlacements: this.outputNetLabelPlacements };
7733
+ }
7734
+ labelBounds(label) {
7735
+ return getRectBounds(label.center, label.width, label.height);
7736
+ }
7737
+ collisionKey(a, b) {
7738
+ return [a.globalConnNetId, b.globalConnNetId].sort().join("::");
7739
+ }
7740
+ findNextCollidingPair() {
7741
+ const labels = this.outputNetLabelPlacements;
7742
+ for (let i = 0; i < labels.length; i++) {
7743
+ for (let j = i + 1; j < labels.length; j++) {
7744
+ const a = labels[i];
7745
+ const b = labels[j];
7746
+ if (a.globalConnNetId === b.globalConnNetId) continue;
7747
+ if (this.skippedCollisionKeys.has(this.collisionKey(a, b))) continue;
7748
+ if (boundsOverlap(this.labelBounds(a), this.labelBounds(b)))
7749
+ return [a, b];
7750
+ }
7751
+ }
7752
+ return null;
7753
+ }
7754
+ netLabelWidthOf(label) {
7755
+ if (label.orientation === "x+" || label.orientation === "x-")
7756
+ return label.width;
7757
+ return label.height;
7758
+ }
7759
+ netLabelHeightOf(label) {
7760
+ if (label.orientation === "x+" || label.orientation === "x-")
7761
+ return label.height;
7762
+ return label.width;
7763
+ }
7764
+ buildCandidatesForLabel(label) {
7765
+ const netLabelWidth = this.netLabelWidthOf(label);
7766
+ const netLabelHeight = this.netLabelHeightOf(label);
7767
+ const candidates = [];
7768
+ const buildCandidate = (orientation, anchor, hostPairId, hostSegIndex) => {
7769
+ const { width, height } = getDimsForOrientation({
7770
+ orientation,
7771
+ netLabelWidth,
7772
+ netLabelHeight
7773
+ });
7774
+ const baseCenter = getCenterFromAnchor(anchor, orientation, width, height);
7775
+ const outwardDir = OUTWARD_DIR[orientation];
7776
+ const center = {
7777
+ x: baseCenter.x + outwardDir.x * ANCHOR_TRACE_CLEARANCE,
7778
+ y: baseCenter.y + outwardDir.y * ANCHOR_TRACE_CLEARANCE
7779
+ };
7780
+ return {
7781
+ orientation,
7782
+ anchor,
7783
+ center,
7784
+ width,
7785
+ height,
7786
+ bounds: getRectBounds(center, width, height),
7787
+ hostPairId,
7788
+ hostSegIndex,
7789
+ status: null
7790
+ };
7791
+ };
7792
+ const isPortOnly = label.mspConnectionPairIds.length === 0;
7793
+ if (isPortOnly) {
7794
+ const allOrientations = ["x+", "x-", "y+", "y-"];
7795
+ const orderedOrientations = [
7796
+ label.orientation,
7797
+ ...allOrientations.filter((o) => o !== label.orientation)
7798
+ ];
7799
+ for (const orientation of orderedOrientations) {
7800
+ candidates.push(buildCandidate(orientation, label.anchorPoint));
7801
+ }
7802
+ } else {
7803
+ for (const mspPairId of label.mspConnectionPairIds) {
7804
+ const trace = this.traceMap[mspPairId];
7805
+ if (!trace) continue;
7806
+ const pts = trace.tracePath;
7807
+ for (let si = 0; si < pts.length - 1; si++) {
7808
+ const segStart = pts[si];
7809
+ const segEnd = pts[si + 1];
7810
+ const isHorizontal3 = Math.abs(segStart.y - segEnd.y) < SEGMENT_PARALLEL_EPS;
7811
+ const isVertical4 = Math.abs(segStart.x - segEnd.x) < SEGMENT_PARALLEL_EPS;
7812
+ if (!isHorizontal3 && !isVertical4) continue;
7813
+ let perpendicularOrientations;
7814
+ if (isHorizontal3) {
7815
+ perpendicularOrientations = ["y+", "y-"];
7816
+ } else {
7817
+ perpendicularOrientations = ["x+", "x-"];
7818
+ }
7819
+ for (const anchor of sampleAnchorsAlongSegment(segStart, segEnd)) {
7820
+ for (const orientation of perpendicularOrientations) {
7821
+ candidates.push(
7822
+ buildCandidate(
7823
+ orientation,
7824
+ anchor,
7825
+ mspPairId,
7826
+ si
7827
+ )
7828
+ );
7829
+ }
7830
+ }
7831
+ }
7832
+ }
7833
+ }
7834
+ return candidates;
7835
+ }
7836
+ checkCandidate(candidate, movingLabelNetId, obstacleLabels) {
7837
+ const { bounds, hostPairId, hostSegIndex } = candidate;
7838
+ if (this.chipIndex.getChipsInBounds(bounds).length > 0)
7839
+ return "chip-collision";
7840
+ if (rectIntersectsAnyTrace(bounds, this.traceMap, hostPairId, hostSegIndex).hasIntersection) {
7841
+ return "trace-collision";
7842
+ }
7843
+ for (const obstacle of obstacleLabels) {
7844
+ if (obstacle.globalConnNetId === movingLabelNetId) continue;
7845
+ if (boundsOverlap(bounds, this.labelBounds(obstacle)))
7846
+ return "label-collision";
7847
+ }
7848
+ return "ok";
7849
+ }
7850
+ beginSearchForLabel(label) {
7851
+ this.currentLabelToMove = label;
7852
+ this.candidateQueue = this.buildCandidatesForLabel(label);
7853
+ this.candidateIndex = 0;
7854
+ this.candidateResults = [];
7855
+ }
7856
+ clearActiveSearch() {
7857
+ this.currentCollision = null;
7858
+ this.currentLabelToMove = null;
7859
+ this.labelsToTry = [];
7860
+ this.candidateQueue = [];
7861
+ this.candidateIndex = 0;
7862
+ this.candidateResults = [];
7863
+ }
7864
+ _step() {
7865
+ if (!this.currentCollision) {
7866
+ const pair = this.findNextCollidingPair();
7867
+ if (!pair) {
7868
+ this.solved = true;
7869
+ return;
7870
+ }
7871
+ this.currentCollision = pair;
7872
+ this.labelsToTry = [pair[1], pair[0]];
7873
+ this.beginSearchForLabel(this.labelsToTry.shift());
7874
+ return;
7875
+ }
7876
+ if (this.candidateIndex >= this.candidateQueue.length) {
7877
+ if (this.labelsToTry.length > 0) {
7878
+ this.beginSearchForLabel(this.labelsToTry.shift());
7879
+ } else {
7880
+ this.skippedCollisionKeys.add(
7881
+ this.collisionKey(this.currentCollision[0], this.currentCollision[1])
7882
+ );
7883
+ this.clearActiveSearch();
7884
+ }
7885
+ return;
7886
+ }
7887
+ const candidate = this.candidateQueue[this.candidateIndex++];
7888
+ const [labelA, labelB] = this.currentCollision;
7889
+ let fixedLabel;
7890
+ if (this.currentLabelToMove === labelB) {
7891
+ fixedLabel = labelA;
7892
+ } else {
7893
+ fixedLabel = labelB;
7894
+ }
7895
+ const obstacleLabels = [
7896
+ ...this.outputNetLabelPlacements.filter(
7897
+ (l) => l !== labelA && l !== labelB
7898
+ ),
7899
+ fixedLabel
7900
+ ];
7901
+ const status = this.checkCandidate(
7902
+ candidate,
7903
+ this.currentLabelToMove.globalConnNetId,
7904
+ obstacleLabels
7905
+ );
7906
+ candidate.status = status;
7907
+ this.candidateResults.push({ ...candidate });
7908
+ if (status === "ok") {
7909
+ const idx = this.outputNetLabelPlacements.indexOf(
7910
+ this.currentLabelToMove
7911
+ );
7912
+ if (idx !== -1) {
7913
+ this.outputNetLabelPlacements[idx] = {
7914
+ ...this.currentLabelToMove,
7915
+ orientation: candidate.orientation,
7916
+ anchorPoint: candidate.anchor,
7917
+ width: candidate.width,
7918
+ height: candidate.height,
7919
+ center: candidate.center
7920
+ };
7921
+ }
7922
+ this.clearActiveSearch();
7923
+ }
7924
+ }
7925
+ visualize() {
7926
+ const graphics = visualizeInputProblem(this.inputProblem);
7927
+ if (!graphics.lines) graphics.lines = [];
7928
+ if (!graphics.rects) graphics.rects = [];
7929
+ if (!graphics.points) graphics.points = [];
7930
+ for (const trace of this.traces) {
7931
+ graphics.lines.push({
7932
+ points: trace.tracePath,
7933
+ strokeColor: "purple"
7934
+ });
7935
+ }
7936
+ for (const label of this.outputNetLabelPlacements) {
7937
+ const isInActiveCollision = this.currentCollision != null && (label === this.currentCollision[0] || label === this.currentCollision[1]);
7938
+ let labelFill;
7939
+ let labelStroke;
7940
+ let labelText;
7941
+ let pointColor;
7942
+ if (isInActiveCollision) {
7943
+ labelFill = "rgba(255, 0, 0, 0.2)";
7944
+ labelStroke = "red";
7945
+ labelText = `netId: ${label.netId}
7946
+ globalConnNetId: ${label.globalConnNetId}
7947
+ \u26A0 COLLIDING`;
7948
+ pointColor = "red";
7949
+ } else {
7950
+ labelFill = getColorFromString(label.globalConnNetId, 0.35);
7951
+ labelStroke = getColorFromString(label.globalConnNetId, 0.9);
7952
+ labelText = `netId: ${label.netId}
7953
+ globalConnNetId: ${label.globalConnNetId}`;
7954
+ pointColor = getColorFromString(label.globalConnNetId, 0.9);
7955
+ }
7956
+ graphics.rects.push({
7957
+ center: label.center,
7958
+ width: label.width,
7959
+ height: label.height,
7960
+ fill: labelFill,
7961
+ strokeColor: labelStroke,
7962
+ label: labelText
7963
+ });
7964
+ graphics.points.push({
7965
+ x: label.anchorPoint.x,
7966
+ y: label.anchorPoint.y,
7967
+ color: pointColor,
7968
+ label: `anchorPoint
7969
+ orientation: ${label.orientation}`
7970
+ });
7971
+ }
7972
+ const movingNetId = this.currentLabelToMove ? this.currentLabelToMove.netId : "?";
7973
+ for (const c of this.candidateResults) {
7974
+ const statusColor = CANDIDATE_STATUS_COLOR[c.status];
7975
+ const statusFill = CANDIDATE_STATUS_FILL[c.status];
7976
+ let strokeDash;
7977
+ if (c.status !== "ok") strokeDash = "4 2";
7978
+ graphics.rects.push({
7979
+ center: c.center,
7980
+ width: c.width,
7981
+ height: c.height,
7982
+ fill: statusFill,
7983
+ strokeColor: statusColor,
7984
+ strokeDash,
7985
+ label: `candidate: ${c.status}
7986
+ orientation: ${c.orientation}
7987
+ moving: ${movingNetId}`
7988
+ });
7989
+ graphics.points.push({
7990
+ x: c.anchor.x,
7991
+ y: c.anchor.y,
7992
+ color: statusColor,
7993
+ label: `candidate anchor
7994
+ ${c.status}`
7995
+ });
7996
+ }
7997
+ return graphics;
7998
+ }
7999
+ };
8000
+
7608
8001
  // lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
7609
8002
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
7610
8003
  return {
@@ -7630,6 +8023,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
7630
8023
  vccNetLabelCornerPlacementSolver;
7631
8024
  traceAnchoredNetLabelOverlapSolver;
7632
8025
  netLabelTraceCollisionSolver;
8026
+ netLabelNetLabelCollisionSolver;
7633
8027
  startTimeOfPhase;
7634
8028
  endTimeOfPhase;
7635
8029
  timeSpentOnPhase;
@@ -7821,6 +8215,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
7821
8215
  netLabelPlacements: instance.traceAnchoredNetLabelOverlapSolver.outputNetLabelPlacements
7822
8216
  }
7823
8217
  ]
8218
+ ),
8219
+ definePipelineStep(
8220
+ "netLabelNetLabelCollisionSolver",
8221
+ NetLabelNetLabelCollisionSolver,
8222
+ (instance) => [
8223
+ {
8224
+ inputProblem: instance.inputProblem,
8225
+ traces: instance.netLabelTraceCollisionSolver.getOutput().traces,
8226
+ netLabelPlacements: instance.netLabelTraceCollisionSolver.getOutput().netLabelPlacements
8227
+ }
8228
+ ]
7824
8229
  )
7825
8230
  ];
7826
8231
  constructor(inputProblem) {
@@ -429,6 +429,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
429
429
  const { width, height } = getDimsForOrientation({
430
430
  orientation,
431
431
  netLabelWidth: this.getNetLabelWidth(label),
432
+ netLabelHeight: this.getNetLabelHeight(label),
432
433
  })
433
434
 
434
435
  return this.getSideOffsetAnchor({
@@ -494,6 +495,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
494
495
  const { width, height } = getDimsForOrientation({
495
496
  orientation,
496
497
  netLabelWidth: this.getNetLabelWidth(label),
498
+ netLabelHeight: this.getNetLabelHeight(label),
497
499
  })
498
500
  const labelLength =
499
501
  orientation === "y+" || orientation === "y-" ? height : width
@@ -534,6 +536,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
534
536
  const { width, height } = getDimsForOrientation({
535
537
  orientation,
536
538
  netLabelWidth: this.getNetLabelWidth(label),
539
+ netLabelHeight: this.getNetLabelHeight(label),
537
540
  })
538
541
  return {
539
542
  orientation,
@@ -567,6 +570,19 @@ export class AvailableNetOrientationSolver extends BaseSolver {
567
570
  )?.netLabelWidth
568
571
  }
569
572
 
573
+ private getNetLabelHeight(label: NetLabelPlacement) {
574
+ if (label.netId) {
575
+ const ncHeight = this.inputProblem.netConnections.find(
576
+ (connection) => connection.netId === label.netId,
577
+ )?.netLabelHeight
578
+ if (ncHeight !== undefined) return ncHeight
579
+ }
580
+
581
+ return this.inputProblem.netConnections.find((nc) =>
582
+ nc.pinIds.some((pid) => label.pinIds.includes(pid)),
583
+ )?.netLabelHeight
584
+ }
585
+
570
586
  private getCandidateStatus(
571
587
  candidate: CandidateLabel,
572
588
  label: NetLabelPlacement,