@tscircuit/schematic-trace-solver 0.0.50 → 0.0.52

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
@@ -271,19 +271,19 @@ var doesPairCrossRestrictedCenterLines = (params) => {
271
271
  chipMap
272
272
  });
273
273
  if (restrictedCenterLines.size === 0) return false;
274
- const EPS7 = 1e-9;
274
+ const EPS9 = 1e-9;
275
275
  const crossesSegment = (a, b) => {
276
276
  for (const [, rcl] of restrictedCenterLines) {
277
277
  if (rcl.axes.has("x") && typeof rcl.x === "number") {
278
- if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS7) return true;
278
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS9) return true;
279
279
  }
280
280
  if (rcl.axes.has("y") && typeof rcl.y === "number") {
281
- if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS7) return true;
281
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS9) return true;
282
282
  }
283
283
  }
284
284
  return false;
285
285
  };
286
- if (Math.abs(p1.x - p2.x) < EPS7 || Math.abs(p1.y - p2.y) < EPS7) {
286
+ if (Math.abs(p1.x - p2.x) < EPS9 || Math.abs(p1.y - p2.y) < EPS9) {
287
287
  return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y });
288
288
  }
289
289
  const elbowHV = { x: p2.x, y: p1.y };
@@ -810,8 +810,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
810
810
  if (!collision) {
811
811
  const first = path[0];
812
812
  const last = path[path.length - 1];
813
- const EPS7 = 1e-9;
814
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS7 && Math.abs(p.y - q.y) < EPS7;
813
+ const EPS9 = 1e-9;
814
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS9 && Math.abs(p.y - q.y) < EPS9;
815
815
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
816
816
  this.solvedTracePath = path;
817
817
  this.solved = true;
@@ -1010,17 +1010,17 @@ var applyJogToTerminalSegment = ({
1010
1010
  segmentIndex: si,
1011
1011
  offset,
1012
1012
  JOG_SIZE,
1013
- EPS: EPS7 = 1e-6
1013
+ EPS: EPS9 = 1e-6
1014
1014
  }) => {
1015
1015
  if (si !== 0 && si !== pts.length - 2) return;
1016
1016
  const start = pts[si];
1017
1017
  const end = pts[si + 1];
1018
- const isVertical3 = Math.abs(start.x - end.x) < EPS7;
1019
- const isHorizontal2 = Math.abs(start.y - end.y) < EPS7;
1020
- if (!isVertical3 && !isHorizontal2) return;
1021
- const segDir = isVertical3 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1018
+ const isVertical4 = Math.abs(start.x - end.x) < EPS9;
1019
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS9;
1020
+ if (!isVertical4 && !isHorizontal3) return;
1021
+ const segDir = isVertical4 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1022
1022
  if (si === 0) {
1023
- if (isVertical3) {
1023
+ if (isVertical4) {
1024
1024
  const jogY = start.y + segDir * JOG_SIZE;
1025
1025
  pts.splice(
1026
1026
  1,
@@ -1040,7 +1040,7 @@ var applyJogToTerminalSegment = ({
1040
1040
  );
1041
1041
  }
1042
1042
  } else {
1043
- if (isVertical3) {
1043
+ if (isVertical4) {
1044
1044
  const jogY = end.y - segDir * JOG_SIZE;
1045
1045
  pts.splice(
1046
1046
  si,
@@ -1083,13 +1083,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1083
1083
  }
1084
1084
  }
1085
1085
  _step() {
1086
- const EPS7 = 1e-6;
1086
+ const EPS9 = 1e-6;
1087
1087
  const offsets = this.overlappingTraceSegments.map((_, idx) => {
1088
1088
  const n = Math.floor(idx / 2) + 1;
1089
1089
  const signed = idx % 2 === 0 ? -n : n;
1090
1090
  return signed * this.SHIFT_DISTANCE;
1091
1091
  });
1092
- const eq = (a, b) => Math.abs(a - b) < EPS7;
1092
+ const eq = (a, b) => Math.abs(a - b) < EPS9;
1093
1093
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
1094
1094
  this.overlappingTraceSegments.forEach((group, gidx) => {
1095
1095
  const offset = offsets[gidx];
@@ -1115,15 +1115,15 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1115
1115
  segmentIndex: si,
1116
1116
  offset,
1117
1117
  JOG_SIZE,
1118
- EPS: EPS7
1118
+ EPS: EPS9
1119
1119
  });
1120
1120
  } else {
1121
1121
  const start = pts[si];
1122
1122
  const end = pts[si + 1];
1123
- const isVertical3 = Math.abs(start.x - end.x) < EPS7;
1124
- const isHorizontal2 = Math.abs(start.y - end.y) < EPS7;
1125
- if (!isVertical3 && !isHorizontal2) continue;
1126
- if (isVertical3) {
1123
+ const isVertical4 = Math.abs(start.x - end.x) < EPS9;
1124
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS9;
1125
+ if (!isVertical4 && !isHorizontal3) continue;
1126
+ if (isVertical4) {
1127
1127
  start.x += offset;
1128
1128
  end.x += offset;
1129
1129
  } else {
@@ -1218,7 +1218,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1218
1218
  return islands;
1219
1219
  }
1220
1220
  findNextOverlapIssue() {
1221
- const EPS7 = 2e-3;
1221
+ const EPS9 = 2e-3;
1222
1222
  const netIds = Object.keys(this.traceNetIslands);
1223
1223
  for (let i = 0; i < netIds.length; i++) {
1224
1224
  for (let j = i + 1; j < netIds.length; j++) {
@@ -1236,7 +1236,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1236
1236
  const minB = Math.min(b1, b2);
1237
1237
  const maxB = Math.max(b1, b2);
1238
1238
  const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
1239
- return overlap > EPS7;
1239
+ return overlap > EPS9;
1240
1240
  };
1241
1241
  for (let pa = 0; pa < pathsA.length; pa++) {
1242
1242
  const pathA = pathsA[pa];
@@ -1244,8 +1244,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1244
1244
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
1245
1245
  const a1 = ptsA[sa];
1246
1246
  const a2 = ptsA[sa + 1];
1247
- const aVert = Math.abs(a1.x - a2.x) < EPS7;
1248
- const aHorz = Math.abs(a1.y - a2.y) < EPS7;
1247
+ const aVert = Math.abs(a1.x - a2.x) < EPS9;
1248
+ const aHorz = Math.abs(a1.y - a2.y) < EPS9;
1249
1249
  if (!aVert && !aHorz) continue;
1250
1250
  for (let pb = 0; pb < pathsB.length; pb++) {
1251
1251
  const pathB = pathsB[pb];
@@ -1253,11 +1253,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1253
1253
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
1254
1254
  const b1 = ptsB[sb];
1255
1255
  const b2 = ptsB[sb + 1];
1256
- const bVert = Math.abs(b1.x - b2.x) < EPS7;
1257
- const bHorz = Math.abs(b1.y - b2.y) < EPS7;
1256
+ const bVert = Math.abs(b1.x - b2.x) < EPS9;
1257
+ const bHorz = Math.abs(b1.y - b2.y) < EPS9;
1258
1258
  if (!bVert && !bHorz) continue;
1259
1259
  if (aVert && bVert) {
1260
- if (Math.abs(a1.x - b1.x) < EPS7) {
1260
+ if (Math.abs(a1.x - b1.x) < EPS9) {
1261
1261
  if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
1262
1262
  const keyA = `${pa}:${sa}`;
1263
1263
  const keyB = `${pb}:${sb}`;
@@ -1278,7 +1278,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1278
1278
  }
1279
1279
  }
1280
1280
  } else if (aHorz && bHorz) {
1281
- if (Math.abs(a1.y - b1.y) < EPS7) {
1281
+ if (Math.abs(a1.y - b1.y) < EPS9) {
1282
1282
  if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
1283
1283
  const keyA = `${pa}:${sa}`;
1284
1284
  const keyB = `${pb}:${sb}`;
@@ -1316,15 +1316,15 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1316
1316
  return null;
1317
1317
  }
1318
1318
  findNextDiagonalSegment() {
1319
- const EPS7 = 2e-3;
1319
+ const EPS9 = 2e-3;
1320
1320
  for (const mspPairId in this.correctedTraceMap) {
1321
1321
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
1322
1322
  for (let i = 0; i < tracePath.length - 1; i++) {
1323
1323
  const p1 = tracePath[i];
1324
1324
  const p2 = tracePath[i + 1];
1325
- const isHorizontal2 = Math.abs(p1.y - p2.y) < EPS7;
1326
- const isVertical3 = Math.abs(p1.x - p2.x) < EPS7;
1327
- if (!isHorizontal2 && !isVertical3) {
1325
+ const isHorizontal3 = Math.abs(p1.y - p2.y) < EPS9;
1326
+ const isVertical4 = Math.abs(p1.x - p2.x) < EPS9;
1327
+ if (!isHorizontal3 && !isVertical4) {
1328
1328
  return { mspPairId, tracePath, i, p1, p2 };
1329
1329
  }
1330
1330
  }
@@ -1337,13 +1337,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1337
1337
  return false;
1338
1338
  }
1339
1339
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
1340
- const EPS7 = 2e-3;
1340
+ const EPS9 = 2e-3;
1341
1341
  const p0 = i > 0 ? tracePath[i - 1] : null;
1342
1342
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
1343
- const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS7 : false;
1344
- const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS7 : false;
1345
- const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS7 : false;
1346
- const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS7 : false;
1343
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS9 : false;
1344
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS9 : false;
1345
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS9 : false;
1346
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS9 : false;
1347
1347
  const elbow1 = { x: p1.x, y: p2.y };
1348
1348
  const elbow2 = { x: p2.x, y: p1.y };
1349
1349
  let score1 = 0;
@@ -1507,24 +1507,24 @@ function getRectBounds(center, w, h) {
1507
1507
  }
1508
1508
 
1509
1509
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
1510
- function segmentIntersectsRect2(p1, p2, rect, EPS7 = 1e-9) {
1511
- const isVert = Math.abs(p1.x - p2.x) < EPS7;
1512
- const isHorz = Math.abs(p1.y - p2.y) < EPS7;
1510
+ function segmentIntersectsRect2(p1, p2, rect, EPS9 = 1e-9) {
1511
+ const isVert = Math.abs(p1.x - p2.x) < EPS9;
1512
+ const isHorz = Math.abs(p1.y - p2.y) < EPS9;
1513
1513
  if (!isVert && !isHorz) return false;
1514
1514
  if (isVert) {
1515
1515
  const x = p1.x;
1516
- if (x < rect.minX - EPS7 || x > rect.maxX + EPS7) return false;
1516
+ if (x < rect.minX - EPS9 || x > rect.maxX + EPS9) return false;
1517
1517
  const segMinY = Math.min(p1.y, p2.y);
1518
1518
  const segMaxY = Math.max(p1.y, p2.y);
1519
1519
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
1520
- return overlap > EPS7;
1520
+ return overlap > EPS9;
1521
1521
  } else {
1522
1522
  const y = p1.y;
1523
- if (y < rect.minY - EPS7 || y > rect.maxY + EPS7) return false;
1523
+ if (y < rect.minY - EPS9 || y > rect.maxY + EPS9) return false;
1524
1524
  const segMinX = Math.min(p1.x, p2.x);
1525
1525
  const segMaxX = Math.max(p1.x, p2.x);
1526
1526
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
1527
- return overlap > EPS7;
1527
+ return overlap > EPS9;
1528
1528
  }
1529
1529
  }
1530
1530
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -1902,14 +1902,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1902
1902
  };
1903
1903
  let bestCandidate = null;
1904
1904
  let bestScore = -Infinity;
1905
- const EPS7 = 1e-6;
1905
+ const EPS9 = 1e-6;
1906
1906
  for (const curr of tracesToScan) {
1907
1907
  const pts = curr.tracePath.slice();
1908
1908
  for (let si = 0; si < pts.length - 1; si++) {
1909
1909
  const a = pts[si];
1910
1910
  const b = pts[si + 1];
1911
- const isH = Math.abs(a.y - b.y) < EPS7;
1912
- const isV = Math.abs(a.x - b.x) < EPS7;
1911
+ const isH = Math.abs(a.y - b.y) < EPS9;
1912
+ const isV = Math.abs(a.x - b.x) < EPS9;
1913
1913
  if (!isH && !isV) continue;
1914
1914
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
1915
1915
  const candidateOrients = orientations.filter(
@@ -4810,12 +4810,12 @@ var isPathCollidingWithChipInterior = (path, chipObstacles) => {
4810
4810
  return false;
4811
4811
  };
4812
4812
  var segmentRunsAlongRectBoundary = (start, end, rect) => {
4813
- const isVertical3 = Math.abs(start.x - end.x) < EPS5;
4814
- const isHorizontal2 = Math.abs(start.y - end.y) < EPS5;
4815
- if (isVertical3) {
4813
+ const isVertical4 = Math.abs(start.x - end.x) < EPS5;
4814
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS5;
4815
+ if (isVertical4) {
4816
4816
  return Math.abs(start.x - rect.minX) < EPS5 || Math.abs(start.x - rect.maxX) < EPS5;
4817
4817
  }
4818
- if (isHorizontal2) {
4818
+ if (isHorizontal3) {
4819
4819
  return Math.abs(start.y - rect.minY) < EPS5 || Math.abs(start.y - rect.maxY) < EPS5;
4820
4820
  }
4821
4821
  return false;
@@ -6054,6 +6054,1078 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6054
6054
  }
6055
6055
  };
6056
6056
 
6057
+ // lib/solvers/VccNetLabelCornerPlacementSolver/geometry.ts
6058
+ var EPS7 = 1e-6;
6059
+ var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
6060
+ var rectsOverlap2 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS7 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS7;
6061
+ var getTraceCorners = (path) => {
6062
+ const corners = [];
6063
+ for (let i = 1; i < path.length - 1; i++) {
6064
+ const previousPoint = path[i - 1];
6065
+ const cornerPoint = path[i];
6066
+ const nextPoint = path[i + 1];
6067
+ if (isTraceCorner(previousPoint, cornerPoint, nextPoint)) {
6068
+ corners.push(cornerPoint);
6069
+ }
6070
+ }
6071
+ return corners;
6072
+ };
6073
+ var tracePathContainsPoint = (path, point) => {
6074
+ for (let i = 0; i < path.length - 1; i++) {
6075
+ if (isPointOnSegment(point, path[i], path[i + 1])) return true;
6076
+ }
6077
+ return false;
6078
+ };
6079
+ var getDistance2 = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
6080
+ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
6081
+ var isTraceCorner = (a, b, c) => getSegmentOrientation2(a, b) !== getSegmentOrientation2(b, c);
6082
+ var isPointOnSegment = (point, start, end) => {
6083
+ if (getSegmentOrientation2(start, end) === "horizontal") {
6084
+ return Math.abs(point.y - start.y) <= EPS7 && point.x >= Math.min(start.x, end.x) - EPS7 && point.x <= Math.max(start.x, end.x) + EPS7;
6085
+ }
6086
+ return Math.abs(point.x - start.x) <= EPS7 && point.y >= Math.min(start.y, end.y) - EPS7 && point.y <= Math.max(start.y, end.y) + EPS7;
6087
+ };
6088
+ var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS7 ? "horizontal" : "vertical";
6089
+
6090
+ // lib/solvers/VccNetLabelCornerPlacementSolver/visualize.ts
6091
+ var CANDIDATE_SELECTED_COLOR3 = "blue";
6092
+ var CANDIDATE_REJECTED_COLOR3 = "red";
6093
+ var visualizeVccNetLabelCornerPlacementSolver = (state) => {
6094
+ const graphics = visualizeInputProblem(state.inputProblem);
6095
+ ensureGraphicsArrays2(graphics);
6096
+ drawTraces3(graphics, state.traces);
6097
+ drawNetLabels3(graphics, state.outputNetLabelPlacements);
6098
+ if (!state.solved) {
6099
+ drawCurrentLabel2(graphics, state.currentLabel);
6100
+ drawCurrentCandidates3(graphics, state.currentCandidateResults);
6101
+ }
6102
+ return graphics;
6103
+ };
6104
+ var drawTraces3 = (graphics, traces) => {
6105
+ for (const trace of traces) {
6106
+ graphics.lines.push({
6107
+ points: trace.tracePath,
6108
+ strokeColor: "purple"
6109
+ });
6110
+ }
6111
+ };
6112
+ var drawNetLabels3 = (graphics, labels) => {
6113
+ for (const label of labels) {
6114
+ graphics.rects.push({
6115
+ center: label.center,
6116
+ width: label.width,
6117
+ height: label.height,
6118
+ fill: getColorFromString(label.globalConnNetId, 0.35),
6119
+ strokeColor: getColorFromString(label.globalConnNetId, 0.9),
6120
+ label: `netId: ${label.netId}
6121
+ globalConnNetId: ${label.globalConnNetId}`
6122
+ });
6123
+ graphics.points.push({
6124
+ x: label.anchorPoint.x,
6125
+ y: label.anchorPoint.y,
6126
+ color: getColorFromString(label.globalConnNetId, 0.9),
6127
+ label: `anchorPoint
6128
+ orientation: ${label.orientation}`
6129
+ });
6130
+ }
6131
+ };
6132
+ var drawCurrentLabel2 = (graphics, currentLabel) => {
6133
+ if (!currentLabel) return;
6134
+ graphics.rects.push({
6135
+ center: currentLabel.center,
6136
+ width: currentLabel.width,
6137
+ height: currentLabel.height,
6138
+ fill: "rgba(255, 0, 0, 0.2)",
6139
+ strokeColor: CANDIDATE_REJECTED_COLOR3,
6140
+ label: `trace-line VCC target
6141
+ ${currentLabel.netId ?? currentLabel.globalConnNetId}`
6142
+ });
6143
+ };
6144
+ var drawCurrentCandidates3 = (graphics, candidates) => {
6145
+ for (const candidate of candidates) {
6146
+ const color = candidate.selected ? CANDIDATE_SELECTED_COLOR3 : CANDIDATE_REJECTED_COLOR3;
6147
+ graphics.rects.push({
6148
+ center: candidate.center,
6149
+ width: candidate.width,
6150
+ height: candidate.height,
6151
+ fill: candidate.selected ? "rgba(0, 0, 255, 0.2)" : "rgba(255, 0, 0, 0.15)",
6152
+ strokeColor: color,
6153
+ strokeDash: candidate.selected ? void 0 : "4 2",
6154
+ label: `${candidate.selected ? "selected" : candidate.status} trace corner
6155
+ trace: ${candidate.traceId}
6156
+ distance: ${candidate.distance.toFixed(3)}`
6157
+ });
6158
+ graphics.points.push({
6159
+ ...candidate.anchorPoint,
6160
+ color,
6161
+ label: `candidate anchor
6162
+ ${candidate.status}`
6163
+ });
6164
+ }
6165
+ };
6166
+ var ensureGraphicsArrays2 = (graphics) => {
6167
+ if (!graphics.lines) graphics.lines = [];
6168
+ if (!graphics.points) graphics.points = [];
6169
+ if (!graphics.rects) graphics.rects = [];
6170
+ if (!graphics.circles) graphics.circles = [];
6171
+ if (!graphics.texts) graphics.texts = [];
6172
+ };
6173
+
6174
+ // lib/solvers/VccNetLabelCornerPlacementSolver/VccNetLabelCornerPlacementSolver.ts
6175
+ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
6176
+ inputProblem;
6177
+ traces;
6178
+ netLabelPlacements;
6179
+ outputNetLabelPlacements;
6180
+ queuedLabelIndices = [];
6181
+ currentLabelIndex = null;
6182
+ currentLabel = null;
6183
+ currentCandidateResults = [];
6184
+ queuedCornerCandidates = [];
6185
+ shouldAdvanceToNextLabel = false;
6186
+ traceMap;
6187
+ constructor(params) {
6188
+ super();
6189
+ this.inputProblem = params.inputProblem;
6190
+ this.traces = params.traces;
6191
+ this.netLabelPlacements = params.netLabelPlacements;
6192
+ this.outputNetLabelPlacements = [...params.netLabelPlacements];
6193
+ this.traceMap = Object.fromEntries(
6194
+ params.traces.map((trace) => [trace.mspPairId, trace])
6195
+ );
6196
+ this.queuedLabelIndices = this.getProcessableLabelIndices();
6197
+ this.prepareNextLabel();
6198
+ }
6199
+ getConstructorParams() {
6200
+ return {
6201
+ inputProblem: this.inputProblem,
6202
+ traces: this.traces,
6203
+ netLabelPlacements: this.netLabelPlacements
6204
+ };
6205
+ }
6206
+ _step() {
6207
+ if (this.shouldAdvanceToNextLabel) {
6208
+ this.advanceToNextLabel();
6209
+ return;
6210
+ }
6211
+ const label = this.currentLabel;
6212
+ const labelIndex = this.currentLabelIndex;
6213
+ if (label === null || labelIndex === null) {
6214
+ this.finish();
6215
+ return;
6216
+ }
6217
+ const candidate = this.queuedCornerCandidates.shift();
6218
+ if (!candidate) {
6219
+ this.shouldAdvanceToNextLabel = true;
6220
+ return;
6221
+ }
6222
+ const result = this.evaluateCornerCandidate(label, labelIndex, candidate);
6223
+ this.currentCandidateResults.push(result);
6224
+ if (result.status !== "valid") return;
6225
+ result.selected = true;
6226
+ this.applyCandidate(labelIndex, label, result);
6227
+ this.shouldAdvanceToNextLabel = true;
6228
+ }
6229
+ getOutput() {
6230
+ return {
6231
+ netLabelPlacements: this.outputNetLabelPlacements
6232
+ };
6233
+ }
6234
+ getProcessableLabelIndices() {
6235
+ const labelIndices = [];
6236
+ for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
6237
+ const label = this.outputNetLabelPlacements[i];
6238
+ if (!this.shouldProcessLabel(label)) continue;
6239
+ labelIndices.push(i);
6240
+ }
6241
+ return labelIndices;
6242
+ }
6243
+ prepareNextLabel() {
6244
+ const labelIndex = this.queuedLabelIndices.shift();
6245
+ if (labelIndex === void 0) {
6246
+ this.clearCurrentLabel();
6247
+ return false;
6248
+ }
6249
+ const label = this.outputNetLabelPlacements[labelIndex];
6250
+ this.currentLabelIndex = labelIndex;
6251
+ this.currentLabel = label;
6252
+ this.currentCandidateResults = [];
6253
+ this.queuedCornerCandidates = this.getCornerCandidatesForLabel(label);
6254
+ return true;
6255
+ }
6256
+ advanceToNextLabel() {
6257
+ this.shouldAdvanceToNextLabel = false;
6258
+ if (!this.prepareNextLabel()) this.finish();
6259
+ }
6260
+ clearCurrentLabel() {
6261
+ this.currentLabelIndex = null;
6262
+ this.currentLabel = null;
6263
+ this.currentCandidateResults = [];
6264
+ this.queuedCornerCandidates = [];
6265
+ }
6266
+ finish() {
6267
+ this.clearCurrentLabel();
6268
+ this.solved = true;
6269
+ }
6270
+ evaluateCornerCandidate(label, labelIndex, candidate) {
6271
+ const center = getCenterFromAnchor(
6272
+ candidate.anchorPoint,
6273
+ label.orientation,
6274
+ label.width,
6275
+ label.height
6276
+ );
6277
+ const bounds = getRectBounds(center, label.width, label.height);
6278
+ return {
6279
+ ...candidate,
6280
+ center,
6281
+ width: label.width,
6282
+ height: label.height,
6283
+ status: this.getCandidateStatus(bounds, labelIndex),
6284
+ selected: false
6285
+ };
6286
+ }
6287
+ getCandidateStatus(bounds, labelIndex) {
6288
+ if (this.intersectsAnyChip(bounds)) return "chip-collision";
6289
+ if (traceCrossesBoundsInterior(bounds, this.traceMap)) {
6290
+ return "trace-collision";
6291
+ }
6292
+ if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
6293
+ return "netlabel-collision";
6294
+ }
6295
+ return "valid";
6296
+ }
6297
+ applyCandidate(labelIndex, label, candidate) {
6298
+ this.outputNetLabelPlacements[labelIndex] = {
6299
+ ...label,
6300
+ anchorPoint: candidate.anchorPoint,
6301
+ center: candidate.center
6302
+ };
6303
+ }
6304
+ shouldProcessLabel(label) {
6305
+ return label.netId === "VCC" && this.getCornerCandidatesForLabel(label).length > 0;
6306
+ }
6307
+ intersectsAnyChip(bounds) {
6308
+ return this.inputProblem.chips.some(
6309
+ (chip) => rectsOverlap2(bounds, getRectBounds(chip.center, chip.width, chip.height))
6310
+ );
6311
+ }
6312
+ intersectsAnyOtherNetLabel(bounds, labelIndex) {
6313
+ return this.outputNetLabelPlacements.some((label, index) => {
6314
+ if (index === labelIndex) return false;
6315
+ return rectsOverlap2(
6316
+ bounds,
6317
+ getRectBounds(label.center, label.width, label.height)
6318
+ );
6319
+ });
6320
+ }
6321
+ getCornerCandidatesForLabel(label) {
6322
+ const candidates = [];
6323
+ const seenCornerKeys = /* @__PURE__ */ new Set();
6324
+ for (const trace of this.getTraceLinesForLabel(label)) {
6325
+ for (const anchorPoint of getTraceCorners(trace.tracePath)) {
6326
+ const key = `${anchorPoint.x}:${anchorPoint.y}`;
6327
+ if (seenCornerKeys.has(key)) continue;
6328
+ seenCornerKeys.add(key);
6329
+ candidates.push({
6330
+ anchorPoint,
6331
+ traceId: trace.mspPairId,
6332
+ distance: getDistance2(anchorPoint, label.anchorPoint)
6333
+ });
6334
+ }
6335
+ }
6336
+ return candidates.sort((a, b) => a.distance - b.distance);
6337
+ }
6338
+ getTraceLinesForLabel(label) {
6339
+ return this.traces.filter(
6340
+ (trace) => isTraceLine(trace) && this.isTraceForLabel(trace, label)
6341
+ );
6342
+ }
6343
+ isTraceForLabel(trace, label) {
6344
+ if (!tracePathContainsPoint(trace.tracePath, label.anchorPoint)) {
6345
+ return false;
6346
+ }
6347
+ const traceIds = new Set(label.mspConnectionPairIds);
6348
+ if (traceIds.size > 0) {
6349
+ return traceIds.has(trace.mspPairId);
6350
+ }
6351
+ return trace.globalConnNetId === label.globalConnNetId;
6352
+ }
6353
+ visualize() {
6354
+ return visualizeVccNetLabelCornerPlacementSolver({
6355
+ inputProblem: this.inputProblem,
6356
+ traces: this.traces,
6357
+ outputNetLabelPlacements: this.outputNetLabelPlacements,
6358
+ currentLabel: this.currentLabel,
6359
+ currentCandidateResults: this.currentCandidateResults,
6360
+ solved: this.solved
6361
+ });
6362
+ }
6363
+ };
6364
+
6365
+ // lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
6366
+ var EPS8 = 1e-6;
6367
+ var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
6368
+ var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
6369
+ var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS8 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS8;
6370
+ var traceCrossesBoundsInterior2 = (bounds, traces) => {
6371
+ for (const trace of traces) {
6372
+ const points = trace.tracePath;
6373
+ for (let i = 0; i < points.length - 1; i++) {
6374
+ if (segmentCrossesBoundsInterior2(points[i], points[i + 1], bounds)) {
6375
+ return true;
6376
+ }
6377
+ }
6378
+ }
6379
+ return false;
6380
+ };
6381
+ var getTraceLocationsForPoint = (point, traces) => {
6382
+ const locations = [];
6383
+ for (const trace of traces) {
6384
+ if (getUniquePinCount2(trace.pinIds) < 2) continue;
6385
+ let pathDistance = 0;
6386
+ for (let i = 0; i < trace.tracePath.length - 1; i++) {
6387
+ const start = trace.tracePath[i];
6388
+ const end = trace.tracePath[i + 1];
6389
+ const segmentLength = getManhattanDistance(start, end);
6390
+ if (isPointOnSegment2(point, start, end)) {
6391
+ locations.push({
6392
+ trace,
6393
+ distance: pathDistance + getManhattanDistance(start, point)
6394
+ });
6395
+ }
6396
+ pathDistance += segmentLength;
6397
+ }
6398
+ }
6399
+ return locations;
6400
+ };
6401
+ var getTraceLength = (trace) => {
6402
+ let length = 0;
6403
+ for (let i = 0; i < trace.tracePath.length - 1; i++) {
6404
+ length += getManhattanDistance(trace.tracePath[i], trace.tracePath[i + 1]);
6405
+ }
6406
+ return length;
6407
+ };
6408
+ var getPointAtTraceDistance = (trace, distance3) => {
6409
+ let pathDistance = 0;
6410
+ for (let i = 0; i < trace.tracePath.length - 1; i++) {
6411
+ const start = trace.tracePath[i];
6412
+ const end = trace.tracePath[i + 1];
6413
+ const segmentLength = getManhattanDistance(start, end);
6414
+ const nextDistance = pathDistance + segmentLength;
6415
+ if (distance3 <= nextDistance + EPS8) {
6416
+ const offset = Math.max(
6417
+ 0,
6418
+ Math.min(segmentLength, distance3 - pathDistance)
6419
+ );
6420
+ const direction = getSegmentDirection(start, end);
6421
+ return {
6422
+ x: start.x + direction.x * offset,
6423
+ y: start.y + direction.y * offset
6424
+ };
6425
+ }
6426
+ pathDistance = nextDistance;
6427
+ }
6428
+ return trace.tracePath[trace.tracePath.length - 1];
6429
+ };
6430
+ var getTraceVertexDistances = (trace) => {
6431
+ const distances = [];
6432
+ let pathDistance = 0;
6433
+ for (let i = 0; i < trace.tracePath.length; i++) {
6434
+ distances.push(pathDistance);
6435
+ const nextPoint = trace.tracePath[i + 1];
6436
+ if (nextPoint) {
6437
+ pathDistance += getManhattanDistance(trace.tracePath[i], nextPoint);
6438
+ }
6439
+ }
6440
+ return distances;
6441
+ };
6442
+ var getTraceDistanceCompatibleOrientations = (trace, distance3) => {
6443
+ const orientations = /* @__PURE__ */ new Set();
6444
+ let pathDistance = 0;
6445
+ for (let i = 0; i < trace.tracePath.length - 1; i++) {
6446
+ const start = trace.tracePath[i];
6447
+ const end = trace.tracePath[i + 1];
6448
+ const segmentLength = getManhattanDistance(start, end);
6449
+ const nextDistance = pathDistance + segmentLength;
6450
+ if (distance3 >= pathDistance - EPS8 && distance3 <= nextDistance + EPS8) {
6451
+ addSegmentCompatibleOrientations(orientations, trace, i);
6452
+ if (Math.abs(distance3 - pathDistance) <= EPS8) {
6453
+ addSegmentCompatibleOrientations(orientations, trace, i - 1);
6454
+ }
6455
+ if (Math.abs(distance3 - nextDistance) <= EPS8) {
6456
+ addSegmentCompatibleOrientations(orientations, trace, i + 1);
6457
+ }
6458
+ return orientations;
6459
+ }
6460
+ pathDistance = nextDistance;
6461
+ }
6462
+ return orientations;
6463
+ };
6464
+ var getManhattanDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
6465
+ var addSegmentCompatibleOrientations = (orientations, trace, segmentIndex) => {
6466
+ const start = trace.tracePath[segmentIndex];
6467
+ const end = trace.tracePath[segmentIndex + 1];
6468
+ if (!start || !end) return;
6469
+ if (isHorizontal2(start, end)) {
6470
+ orientations.add("y+");
6471
+ orientations.add("y-");
6472
+ } else if (isVertical3(start, end)) {
6473
+ orientations.add("x+");
6474
+ orientations.add("x-");
6475
+ }
6476
+ };
6477
+ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
6478
+ const interior = {
6479
+ minX: bounds.minX + TRACE_BOUNDARY_TOLERANCE2,
6480
+ minY: bounds.minY + TRACE_BOUNDARY_TOLERANCE2,
6481
+ maxX: bounds.maxX - TRACE_BOUNDARY_TOLERANCE2,
6482
+ maxY: bounds.maxY - TRACE_BOUNDARY_TOLERANCE2
6483
+ };
6484
+ if (interior.minX >= interior.maxX || interior.minY >= interior.maxY) {
6485
+ return false;
6486
+ }
6487
+ if (isVertical3(start, end)) {
6488
+ if (start.x <= interior.minX + EPS8 || start.x >= interior.maxX - EPS8) {
6489
+ return false;
6490
+ }
6491
+ return rangesOverlap2(
6492
+ Math.min(start.y, end.y),
6493
+ Math.max(start.y, end.y),
6494
+ interior.minY,
6495
+ interior.maxY
6496
+ );
6497
+ }
6498
+ if (isHorizontal2(start, end)) {
6499
+ if (start.y <= interior.minY + EPS8 || start.y >= interior.maxY - EPS8) {
6500
+ return false;
6501
+ }
6502
+ return rangesOverlap2(
6503
+ Math.min(start.x, end.x),
6504
+ Math.max(start.x, end.x),
6505
+ interior.minX,
6506
+ interior.maxX
6507
+ );
6508
+ }
6509
+ return false;
6510
+ };
6511
+ var isPointOnSegment2 = (point, start, end) => {
6512
+ if (isHorizontal2(start, end)) {
6513
+ return Math.abs(point.y - start.y) <= EPS8 && point.x >= Math.min(start.x, end.x) - EPS8 && point.x <= Math.max(start.x, end.x) + EPS8;
6514
+ }
6515
+ if (isVertical3(start, end)) {
6516
+ return Math.abs(point.x - start.x) <= EPS8 && point.y >= Math.min(start.y, end.y) - EPS8 && point.y <= Math.max(start.y, end.y) + EPS8;
6517
+ }
6518
+ return false;
6519
+ };
6520
+ var getSegmentDirection = (start, end) => ({
6521
+ x: isVertical3(start, end) ? 0 : Math.sign(end.x - start.x),
6522
+ y: isHorizontal2(start, end) ? 0 : Math.sign(end.y - start.y)
6523
+ });
6524
+ var isHorizontal2 = (start, end) => Math.abs(start.y - end.y) <= EPS8;
6525
+ var isVertical3 = (start, end) => Math.abs(start.x - end.x) <= EPS8;
6526
+ var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS8;
6527
+ var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
6528
+
6529
+ // lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
6530
+ var CANDIDATE_STEP = 0.1;
6531
+ var generateCandidatesAlongTrace = (params) => {
6532
+ const { inputProblem, label, traceLocation } = params;
6533
+ const traceLength = getTraceLength(traceLocation.trace);
6534
+ const orientationConstraint = getOrientationConstraint(inputProblem, label);
6535
+ const candidateDistances = getCandidateDistances(
6536
+ traceLocation,
6537
+ traceLength,
6538
+ getTraceVertexDistances(traceLocation.trace),
6539
+ orientationConstraint
6540
+ );
6541
+ const netLabelWidth = getNetLabelWidth(inputProblem, label);
6542
+ const candidates = [];
6543
+ const seenCandidateKeys = /* @__PURE__ */ new Set();
6544
+ for (const pathDistance of candidateDistances) {
6545
+ const point = getPointAtTraceDistance(traceLocation.trace, pathDistance);
6546
+ const compatibleOrientations = getTraceDistanceCompatibleOrientations(
6547
+ traceLocation.trace,
6548
+ pathDistance
6549
+ );
6550
+ const orientations = getOrientationsForPoint({
6551
+ inputProblem,
6552
+ label,
6553
+ point,
6554
+ compatibleOrientations,
6555
+ orientationConstraint
6556
+ });
6557
+ for (const orientation of orientations) {
6558
+ if (isSamePlacement(label, point, orientation)) continue;
6559
+ const key = [point.x.toFixed(6), point.y.toFixed(6), orientation].join(
6560
+ ":"
6561
+ );
6562
+ if (seenCandidateKeys.has(key)) continue;
6563
+ seenCandidateKeys.add(key);
6564
+ candidates.push(
6565
+ createCandidate({
6566
+ point,
6567
+ orientation,
6568
+ netLabelWidth,
6569
+ traceLocation,
6570
+ pathDistance,
6571
+ label
6572
+ })
6573
+ );
6574
+ }
6575
+ }
6576
+ return candidates.sort(
6577
+ (a, b) => a.distanceFromOriginal - b.distanceFromOriginal || a.pathDistance - b.pathDistance
6578
+ );
6579
+ };
6580
+ var getCandidateDistances = (traceLocation, traceLength, vertexDistances, orientationConstraint) => {
6581
+ const distances = /* @__PURE__ */ new Set();
6582
+ const maxSteps = Math.ceil(traceLength / CANDIDATE_STEP);
6583
+ for (let i = 0; i <= maxSteps; i++) {
6584
+ const distance3 = Math.min(traceLength, i * CANDIDATE_STEP);
6585
+ distances.add(roundDistance(distance3));
6586
+ }
6587
+ for (const distance3 of vertexDistances) {
6588
+ distances.add(roundDistance(distance3));
6589
+ }
6590
+ distances.add(roundDistance(traceLocation.distance));
6591
+ return [...distances].filter((distance3) => distance3 >= -EPS8 && distance3 <= traceLength + EPS8).filter(
6592
+ (distance3) => isDistanceAllowedByOrientationConstraint(
6593
+ traceLocation,
6594
+ distance3,
6595
+ orientationConstraint
6596
+ )
6597
+ ).sort(
6598
+ (a, b) => Math.abs(a - traceLocation.distance) - Math.abs(b - traceLocation.distance) || a - b
6599
+ );
6600
+ };
6601
+ var getOrientationsForPoint = (params) => {
6602
+ const {
6603
+ inputProblem,
6604
+ label,
6605
+ point,
6606
+ compatibleOrientations,
6607
+ orientationConstraint
6608
+ } = params;
6609
+ if (orientationConstraint) {
6610
+ return orientationConstraint.filter(
6611
+ (orientation) => compatibleOrientations.has(orientation)
6612
+ );
6613
+ }
6614
+ return getUnconstrainedOrientations({
6615
+ label,
6616
+ inputProblem,
6617
+ point,
6618
+ compatibleOrientations
6619
+ });
6620
+ };
6621
+ var createCandidate = (params) => {
6622
+ const {
6623
+ point,
6624
+ orientation,
6625
+ netLabelWidth,
6626
+ traceLocation,
6627
+ pathDistance,
6628
+ label
6629
+ } = params;
6630
+ const { width, height } = getDimsForOrientation({
6631
+ orientation,
6632
+ netLabelWidth
6633
+ });
6634
+ return {
6635
+ anchorPoint: point,
6636
+ center: getCenterFromAnchor(point, orientation, width, height),
6637
+ width,
6638
+ height,
6639
+ orientation,
6640
+ traceId: traceLocation.trace.mspPairId,
6641
+ pathDistance,
6642
+ distanceFromOriginal: getManhattanDistance(point, label.anchorPoint),
6643
+ status: "valid",
6644
+ selected: false
6645
+ };
6646
+ };
6647
+ var getOrientationConstraint = (inputProblem, label) => {
6648
+ for (const netId of getOrientationConstraintKeys(inputProblem, label)) {
6649
+ if (Object.hasOwn(inputProblem.availableNetLabelOrientations, netId)) {
6650
+ return inputProblem.availableNetLabelOrientations[netId] ?? [];
6651
+ }
6652
+ }
6653
+ return null;
6654
+ };
6655
+ var isDistanceAllowedByOrientationConstraint = (traceLocation, distance3, constrainedOrientations) => {
6656
+ if (!constrainedOrientations) return true;
6657
+ const compatibleOrientations = getTraceDistanceCompatibleOrientations(
6658
+ traceLocation.trace,
6659
+ distance3
6660
+ );
6661
+ return constrainedOrientations.some(
6662
+ (orientation) => compatibleOrientations.has(orientation)
6663
+ );
6664
+ };
6665
+ var getOrientationConstraintKeys = (inputProblem, label) => dedupeStrings([
6666
+ label.netId,
6667
+ label.globalConnNetId,
6668
+ ...inputProblem.netConnections.filter(
6669
+ (connection) => label.pinIds.some((pinId) => connection.pinIds.includes(pinId))
6670
+ ).map((connection) => connection.netId)
6671
+ ]);
6672
+ var getUnconstrainedOrientations = (params) => {
6673
+ const { label, inputProblem, point, compatibleOrientations } = params;
6674
+ return dedupeOrientations([
6675
+ ...getInitialOrientations(label, compatibleOrientations),
6676
+ ...getOutwardOrientations({
6677
+ inputProblem,
6678
+ point,
6679
+ compatibleOrientations
6680
+ }),
6681
+ ...ALL_ORIENTATIONS.filter(
6682
+ (orientation) => compatibleOrientations.has(orientation)
6683
+ )
6684
+ ]);
6685
+ };
6686
+ var getInitialOrientations = (label, compatibleOrientations) => [label.orientation, getFlippedOrientation(label.orientation)].filter(
6687
+ (orientation) => compatibleOrientations.has(orientation)
6688
+ );
6689
+ var getOutwardOrientations = (params) => {
6690
+ const { inputProblem, point, compatibleOrientations } = params;
6691
+ const chipBounds = getChipBounds(inputProblem);
6692
+ return dedupeOrientations(
6693
+ getOutwardOrientationOptions(point, chipBounds, compatibleOrientations).filter(({ orientation }) => compatibleOrientations.has(orientation)).sort((a, b) => b.outwardScore - a.outwardScore).map(({ orientation }) => orientation)
6694
+ );
6695
+ };
6696
+ var getOutwardOrientationOptions = (point, chipBounds, compatibleOrientations) => {
6697
+ const options = [];
6698
+ if (compatibleOrientations.has("y+") || compatibleOrientations.has("y-")) {
6699
+ options.push(
6700
+ getOutwardAxisOption({
6701
+ value: point.y,
6702
+ min: chipBounds.minY,
6703
+ max: chipBounds.maxY,
6704
+ center: chipBounds.centerY,
6705
+ positiveOrientation: "y+",
6706
+ negativeOrientation: "y-"
6707
+ })
6708
+ );
6709
+ }
6710
+ if (compatibleOrientations.has("x+") || compatibleOrientations.has("x-")) {
6711
+ options.push(
6712
+ getOutwardAxisOption({
6713
+ value: point.x,
6714
+ min: chipBounds.minX,
6715
+ max: chipBounds.maxX,
6716
+ center: chipBounds.centerX,
6717
+ positiveOrientation: "x+",
6718
+ negativeOrientation: "x-"
6719
+ })
6720
+ );
6721
+ }
6722
+ return options;
6723
+ };
6724
+ var getOutwardAxisOption = (params) => {
6725
+ const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
6726
+ if (value > max + EPS8) {
6727
+ return {
6728
+ orientation: positiveOrientation,
6729
+ outwardScore: 1 + value - max
6730
+ };
6731
+ }
6732
+ if (value < min - EPS8) {
6733
+ return {
6734
+ orientation: negativeOrientation,
6735
+ outwardScore: 1 + min - value
6736
+ };
6737
+ }
6738
+ return {
6739
+ orientation: value >= center ? positiveOrientation : negativeOrientation,
6740
+ outwardScore: getNormalizedCenterDistance(value, center, max - min)
6741
+ };
6742
+ };
6743
+ var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS8, span / 2);
6744
+ var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
6745
+ var getFlippedOrientation = (orientation) => {
6746
+ switch (orientation) {
6747
+ case "x+":
6748
+ return "x-";
6749
+ case "x-":
6750
+ return "x+";
6751
+ case "y+":
6752
+ return "y-";
6753
+ case "y-":
6754
+ return "y+";
6755
+ }
6756
+ };
6757
+ var getChipBounds = (inputProblem) => {
6758
+ if (inputProblem.chips.length === 0) {
6759
+ return {
6760
+ minX: 0,
6761
+ minY: 0,
6762
+ maxX: 0,
6763
+ maxY: 0,
6764
+ centerX: 0,
6765
+ centerY: 0
6766
+ };
6767
+ }
6768
+ const bounds = inputProblem.chips.reduce(
6769
+ (acc, chip) => ({
6770
+ minX: Math.min(acc.minX, chip.center.x - chip.width / 2),
6771
+ minY: Math.min(acc.minY, chip.center.y - chip.height / 2),
6772
+ maxX: Math.max(acc.maxX, chip.center.x + chip.width / 2),
6773
+ maxY: Math.max(acc.maxY, chip.center.y + chip.height / 2)
6774
+ }),
6775
+ {
6776
+ minX: Number.POSITIVE_INFINITY,
6777
+ minY: Number.POSITIVE_INFINITY,
6778
+ maxX: Number.NEGATIVE_INFINITY,
6779
+ maxY: Number.NEGATIVE_INFINITY
6780
+ }
6781
+ );
6782
+ return {
6783
+ ...bounds,
6784
+ centerX: (bounds.minX + bounds.maxX) / 2,
6785
+ centerY: (bounds.minY + bounds.maxY) / 2
6786
+ };
6787
+ };
6788
+ var getNetLabelWidth = (inputProblem, label) => {
6789
+ const configuredWidth = inputProblem.netConnections.find(
6790
+ (connection) => connection.netId === label.netId
6791
+ )?.netLabelWidth;
6792
+ if (configuredWidth !== void 0) return configuredWidth;
6793
+ return label.orientation === "y+" || label.orientation === "y-" ? label.height : label.width;
6794
+ };
6795
+ var roundDistance = (distance3) => Number(distance3.toFixed(6));
6796
+ var dedupeOrientations = (orientations) => [
6797
+ ...new Set(orientations)
6798
+ ];
6799
+ var dedupeStrings = (values) => [
6800
+ ...new Set(values.filter((value) => value !== void 0))
6801
+ ];
6802
+ var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS8 && Math.abs(point.y - label.anchorPoint.y) <= EPS8 && orientation === label.orientation;
6803
+
6804
+ // lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
6805
+ var CANDIDATE_SELECTED_COLOR4 = "blue";
6806
+ var CANDIDATE_REJECTED_COLOR4 = "red";
6807
+ var visualizeTraceAnchoredNetLabelOverlapSolver = (state) => {
6808
+ const graphics = visualizeInputProblem(state.inputProblem);
6809
+ ensureGraphicsArrays3(graphics);
6810
+ drawTraces4(graphics, state.traces);
6811
+ drawNetLabels4(graphics, state.outputNetLabelPlacements);
6812
+ if (!state.solved) {
6813
+ drawCurrentOverlap2(graphics, state);
6814
+ drawCurrentCandidates4(graphics, state.currentCandidateResults);
6815
+ }
6816
+ return graphics;
6817
+ };
6818
+ var drawTraces4 = (graphics, traces) => {
6819
+ for (const trace of traces) {
6820
+ graphics.lines.push({
6821
+ points: trace.tracePath,
6822
+ strokeColor: "purple"
6823
+ });
6824
+ }
6825
+ };
6826
+ var drawNetLabels4 = (graphics, labels) => {
6827
+ for (const label of labels) {
6828
+ graphics.rects.push({
6829
+ center: label.center,
6830
+ width: label.width,
6831
+ height: label.height,
6832
+ fill: getColorFromString(label.globalConnNetId, 0.35),
6833
+ strokeColor: getColorFromString(label.globalConnNetId, 0.9),
6834
+ label: `netId: ${label.netId}
6835
+ globalConnNetId: ${label.globalConnNetId}`
6836
+ });
6837
+ graphics.points.push({
6838
+ x: label.anchorPoint.x,
6839
+ y: label.anchorPoint.y,
6840
+ color: getColorFromString(label.globalConnNetId, 0.9),
6841
+ label: `anchorPoint
6842
+ orientation: ${label.orientation}`
6843
+ });
6844
+ }
6845
+ };
6846
+ var drawCurrentOverlap2 = (graphics, state) => {
6847
+ if (!state.currentOverlap) return;
6848
+ for (const labelIndex of [
6849
+ state.currentOverlap.firstLabelIndex,
6850
+ state.currentOverlap.secondLabelIndex
6851
+ ]) {
6852
+ const label = state.outputNetLabelPlacements[labelIndex];
6853
+ if (!label) continue;
6854
+ graphics.rects.push({
6855
+ center: label.center,
6856
+ width: label.width,
6857
+ height: label.height,
6858
+ fill: "rgba(255, 0, 0, 0.2)",
6859
+ strokeColor: CANDIDATE_REJECTED_COLOR4,
6860
+ label: `netlabel overlap target
6861
+ ${label.netId ?? label.globalConnNetId}`
6862
+ });
6863
+ }
6864
+ };
6865
+ var drawCurrentCandidates4 = (graphics, candidates) => {
6866
+ for (const candidate of candidates) {
6867
+ const color = candidate.selected ? CANDIDATE_SELECTED_COLOR4 : CANDIDATE_REJECTED_COLOR4;
6868
+ graphics.rects.push({
6869
+ center: candidate.center,
6870
+ width: candidate.width,
6871
+ height: candidate.height,
6872
+ fill: candidate.selected ? "rgba(0, 0, 255, 0.2)" : "rgba(255, 0, 0, 0.15)",
6873
+ strokeColor: color,
6874
+ strokeDash: candidate.selected ? void 0 : "4 2",
6875
+ label: `${candidate.selected ? "selected" : candidate.status} netlabel overlap candidate
6876
+ trace: ${candidate.traceId}
6877
+ distance: ${candidate.distanceFromOriginal.toFixed(3)}`
6878
+ });
6879
+ graphics.points.push({
6880
+ ...candidate.anchorPoint,
6881
+ color,
6882
+ label: `candidate anchor
6883
+ ${candidate.status}`
6884
+ });
6885
+ }
6886
+ };
6887
+ var ensureGraphicsArrays3 = (graphics) => {
6888
+ if (!graphics.lines) graphics.lines = [];
6889
+ if (!graphics.points) graphics.points = [];
6890
+ if (!graphics.rects) graphics.rects = [];
6891
+ if (!graphics.circles) graphics.circles = [];
6892
+ if (!graphics.texts) graphics.texts = [];
6893
+ };
6894
+
6895
+ // lib/solvers/TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver.ts
6896
+ var TraceAnchoredNetLabelOverlapSolver = class extends BaseSolver {
6897
+ inputProblem;
6898
+ traces;
6899
+ netLabelPlacements;
6900
+ outputNetLabelPlacements;
6901
+ currentOverlap = null;
6902
+ currentCandidateResults = [];
6903
+ activeSearch = null;
6904
+ skippedOverlapKeys = /* @__PURE__ */ new Set();
6905
+ constructor(params) {
6906
+ super();
6907
+ this.inputProblem = params.inputProblem;
6908
+ this.traces = params.traces;
6909
+ this.netLabelPlacements = params.netLabelPlacements;
6910
+ this.outputNetLabelPlacements = [...params.netLabelPlacements];
6911
+ }
6912
+ getConstructorParams() {
6913
+ return {
6914
+ inputProblem: this.inputProblem,
6915
+ traces: this.traces,
6916
+ netLabelPlacements: this.netLabelPlacements
6917
+ };
6918
+ }
6919
+ _step() {
6920
+ if (this.activeSearch?.completed) this.clearActiveSearch();
6921
+ const search = this.getNextSearchWithCandidates();
6922
+ if (!search) {
6923
+ this.finish();
6924
+ return;
6925
+ }
6926
+ this.evaluateNextCandidate(search);
6927
+ }
6928
+ getOutput() {
6929
+ return {
6930
+ netLabelPlacements: this.outputNetLabelPlacements
6931
+ };
6932
+ }
6933
+ findNextOverlap() {
6934
+ for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
6935
+ for (let j = i + 1; j < this.outputNetLabelPlacements.length; j++) {
6936
+ if (!this.isLabelEligible(i) && !this.isLabelEligible(j)) continue;
6937
+ const overlap = {
6938
+ firstLabelIndex: i,
6939
+ secondLabelIndex: j
6940
+ };
6941
+ if (this.skippedOverlapKeys.has(this.getOverlapKey(overlap))) continue;
6942
+ if (this.labelsOverlap(overlap)) return overlap;
6943
+ }
6944
+ }
6945
+ return null;
6946
+ }
6947
+ isLabelEligible(labelIndex) {
6948
+ return this.getTraceLocationsForLabel(labelIndex).length > 0;
6949
+ }
6950
+ labelsOverlap(overlap) {
6951
+ const first = this.outputNetLabelPlacements[overlap.firstLabelIndex];
6952
+ const second = this.outputNetLabelPlacements[overlap.secondLabelIndex];
6953
+ if (!first || !second) return false;
6954
+ return rectsOverlap3(getLabelBounds(first), getLabelBounds(second));
6955
+ }
6956
+ startNextOverlapSearch() {
6957
+ const overlap = this.findNextOverlap();
6958
+ if (!overlap) return null;
6959
+ const search = {
6960
+ overlap,
6961
+ queuedLabelIndices: this.getMoveLabelIndices(overlap),
6962
+ labelIndex: null,
6963
+ candidates: [],
6964
+ candidateIndex: 0,
6965
+ candidateResults: [],
6966
+ completed: false
6967
+ };
6968
+ this.setActiveSearch(search);
6969
+ return search;
6970
+ }
6971
+ getMoveLabelIndices(overlap) {
6972
+ const labelIndices = [];
6973
+ if (this.isLabelEligible(overlap.secondLabelIndex)) {
6974
+ labelIndices.push(overlap.secondLabelIndex);
6975
+ }
6976
+ if (this.isLabelEligible(overlap.firstLabelIndex)) {
6977
+ labelIndices.push(overlap.firstLabelIndex);
6978
+ }
6979
+ return labelIndices;
6980
+ }
6981
+ getNextSearchWithCandidates() {
6982
+ while (true) {
6983
+ const search = this.activeSearch ?? this.startNextOverlapSearch();
6984
+ if (!search) return null;
6985
+ if (this.activateNextLabelSearch(search)) return search;
6986
+ this.skipSearch(search);
6987
+ }
6988
+ }
6989
+ activateNextLabelSearch(search) {
6990
+ if (search.labelIndex !== null && search.candidateIndex < search.candidates.length) {
6991
+ return true;
6992
+ }
6993
+ while (search.queuedLabelIndices.length > 0) {
6994
+ const labelIndex = search.queuedLabelIndices.shift();
6995
+ const label = this.outputNetLabelPlacements[labelIndex];
6996
+ if (!label) continue;
6997
+ search.labelIndex = labelIndex;
6998
+ search.candidates = this.getCandidatesForLabel(label);
6999
+ search.candidateIndex = 0;
7000
+ if (search.candidates.length > 0) return true;
7001
+ }
7002
+ search.labelIndex = null;
7003
+ search.candidates = [];
7004
+ search.candidateIndex = 0;
7005
+ return false;
7006
+ }
7007
+ skipSearch(search) {
7008
+ this.skippedOverlapKeys.add(this.getOverlapKey(search.overlap));
7009
+ this.clearActiveSearch();
7010
+ }
7011
+ evaluateNextCandidate(search) {
7012
+ const labelIndex = search.labelIndex;
7013
+ if (labelIndex === null) return;
7014
+ const label = this.outputNetLabelPlacements[labelIndex];
7015
+ const candidate = search.candidates[search.candidateIndex];
7016
+ if (!label || !candidate) return;
7017
+ search.candidateIndex += 1;
7018
+ const evaluatedCandidate = {
7019
+ ...candidate,
7020
+ status: this.getCandidateStatus(candidate, labelIndex)
7021
+ };
7022
+ search.candidateResults.push(evaluatedCandidate);
7023
+ if (evaluatedCandidate.status !== "valid") return;
7024
+ evaluatedCandidate.selected = true;
7025
+ this.applyCandidate(labelIndex, label, evaluatedCandidate);
7026
+ search.completed = true;
7027
+ }
7028
+ getCandidatesForLabel(label) {
7029
+ const candidates = [];
7030
+ for (const traceLocation of getTraceLocationsForPoint(
7031
+ label.anchorPoint,
7032
+ this.traces
7033
+ ).filter(({ trace }) => this.isTraceAssociatedWithLabel(trace, label))) {
7034
+ candidates.push(
7035
+ ...generateCandidatesAlongTrace({
7036
+ inputProblem: this.inputProblem,
7037
+ label,
7038
+ traceLocation
7039
+ })
7040
+ );
7041
+ }
7042
+ return candidates.sort(
7043
+ (a, b) => a.distanceFromOriginal - b.distanceFromOriginal || a.pathDistance - b.pathDistance
7044
+ );
7045
+ }
7046
+ getCandidateStatus(candidate, labelIndex) {
7047
+ const bounds = getLabelBounds(candidate);
7048
+ if (this.intersectsAnyChip(bounds)) return "chip-collision";
7049
+ if (traceCrossesBoundsInterior2(bounds, this.traces))
7050
+ return "trace-collision";
7051
+ if (this.intersectsAnyOtherLabel(bounds, labelIndex)) {
7052
+ return "netlabel-collision";
7053
+ }
7054
+ return "valid";
7055
+ }
7056
+ applyCandidate(labelIndex, label, candidate) {
7057
+ this.outputNetLabelPlacements[labelIndex] = {
7058
+ ...label,
7059
+ anchorPoint: candidate.anchorPoint,
7060
+ center: candidate.center,
7061
+ width: candidate.width,
7062
+ height: candidate.height,
7063
+ orientation: candidate.orientation
7064
+ };
7065
+ }
7066
+ intersectsAnyChip(bounds) {
7067
+ return this.inputProblem.chips.some(
7068
+ (chip) => rectsOverlap3(bounds, getRectBounds(chip.center, chip.width, chip.height))
7069
+ );
7070
+ }
7071
+ intersectsAnyOtherLabel(bounds, labelIndex) {
7072
+ return this.outputNetLabelPlacements.some((label, index) => {
7073
+ if (index === labelIndex) return false;
7074
+ return rectsOverlap3(bounds, getLabelBounds(label));
7075
+ });
7076
+ }
7077
+ getTraceLocationsForLabel(labelIndex) {
7078
+ const label = this.outputNetLabelPlacements[labelIndex];
7079
+ if (!label) return [];
7080
+ return getTraceLocationsForPoint(label.anchorPoint, this.traces).filter(
7081
+ ({ trace }) => this.isTraceAssociatedWithLabel(trace, label)
7082
+ );
7083
+ }
7084
+ isTraceAssociatedWithLabel(trace, label) {
7085
+ if (label.mspConnectionPairIds.includes(trace.mspPairId)) return true;
7086
+ if (label.netId !== void 0 && label.netId === trace.userNetId) {
7087
+ return true;
7088
+ }
7089
+ return label.globalConnNetId === trace.globalConnNetId;
7090
+ }
7091
+ getOverlapKey(overlap) {
7092
+ const first = this.outputNetLabelPlacements[overlap.firstLabelIndex];
7093
+ const second = this.outputNetLabelPlacements[overlap.secondLabelIndex];
7094
+ return [
7095
+ overlap.firstLabelIndex,
7096
+ overlap.secondLabelIndex,
7097
+ first?.anchorPoint.x,
7098
+ first?.anchorPoint.y,
7099
+ second?.anchorPoint.x,
7100
+ second?.anchorPoint.y
7101
+ ].join(":");
7102
+ }
7103
+ finish() {
7104
+ this.clearActiveSearch();
7105
+ this.solved = true;
7106
+ }
7107
+ setActiveSearch(search) {
7108
+ this.activeSearch = search;
7109
+ this.currentOverlap = search.overlap;
7110
+ this.currentCandidateResults = search.candidateResults;
7111
+ }
7112
+ clearActiveSearch() {
7113
+ this.activeSearch = null;
7114
+ this.currentOverlap = null;
7115
+ this.currentCandidateResults = [];
7116
+ }
7117
+ visualize() {
7118
+ return visualizeTraceAnchoredNetLabelOverlapSolver({
7119
+ inputProblem: this.inputProblem,
7120
+ traces: this.traces,
7121
+ outputNetLabelPlacements: this.outputNetLabelPlacements,
7122
+ currentOverlap: this.currentOverlap,
7123
+ currentCandidateResults: this.currentCandidateResults,
7124
+ solved: this.solved
7125
+ });
7126
+ }
7127
+ };
7128
+
6057
7129
  // lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
6058
7130
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
6059
7131
  return {
@@ -6076,6 +7148,8 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
6076
7148
  traceCleanupSolver;
6077
7149
  example28Solver;
6078
7150
  availableNetOrientationSolver;
7151
+ vccNetLabelCornerPlacementSolver;
7152
+ traceAnchoredNetLabelOverlapSolver;
6079
7153
  startTimeOfPhase;
6080
7154
  endTimeOfPhase;
6081
7155
  timeSpentOnPhase;
@@ -6232,6 +7306,30 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
6232
7306
  netLabelPlacements: instance.example28Solver.outputNetLabelPlacements
6233
7307
  }
6234
7308
  ]
7309
+ ),
7310
+ definePipelineStep(
7311
+ "vccNetLabelCornerPlacementSolver",
7312
+ VccNetLabelCornerPlacementSolver,
7313
+ (instance) => {
7314
+ return [
7315
+ {
7316
+ inputProblem: instance.inputProblem,
7317
+ traces: instance.availableNetOrientationSolver.traces,
7318
+ netLabelPlacements: instance.availableNetOrientationSolver.outputNetLabelPlacements
7319
+ }
7320
+ ];
7321
+ }
7322
+ ),
7323
+ definePipelineStep(
7324
+ "traceAnchoredNetLabelOverlapSolver",
7325
+ TraceAnchoredNetLabelOverlapSolver,
7326
+ (instance) => [
7327
+ {
7328
+ inputProblem: instance.inputProblem,
7329
+ traces: instance.availableNetOrientationSolver.traces,
7330
+ netLabelPlacements: instance.vccNetLabelCornerPlacementSolver.outputNetLabelPlacements
7331
+ }
7332
+ ]
6235
7333
  )
6236
7334
  ];
6237
7335
  constructor(inputProblem) {