@tscircuit/schematic-trace-solver 0.0.57 → 0.0.61

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 (29) hide show
  1. package/dist/index.d.ts +15 -1
  2. package/dist/index.js +367 -136
  3. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +150 -14
  4. package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +1 -1
  5. package/lib/solvers/AvailableNetOrientationSolver/types.ts +1 -1
  6. package/lib/solvers/Example28Solver/reroute.ts +137 -17
  7. package/lib/solvers/Example28Solver/types.ts +1 -0
  8. package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +32 -10
  9. package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts +17 -5
  10. package/lib/solvers/TraceCleanupSolver/is4PointRectangle.ts +7 -2
  11. package/lib/types/InputProblem.ts +1 -0
  12. package/package.json +1 -1
  13. package/site/examples/example35.page.tsx +4 -0
  14. package/site/examples/example36.page.tsx +4 -0
  15. package/site/examples/example37.page.tsx +4 -0
  16. package/site/examples/example38.page.tsx +4 -0
  17. package/tests/assets/example35.json +127 -0
  18. package/tests/assets/example36.json +124 -0
  19. package/tests/assets/example37.json +173 -0
  20. package/tests/assets/example38.json +32 -0
  21. package/tests/examples/__snapshots__/example04.snap.svg +16 -17
  22. package/tests/examples/__snapshots__/example35.snap.svg +177 -0
  23. package/tests/examples/__snapshots__/example36.snap.svg +160 -0
  24. package/tests/examples/__snapshots__/example37.snap.svg +320 -0
  25. package/tests/examples/__snapshots__/example38.snap.svg +77 -0
  26. package/tests/examples/example35.test.ts +12 -0
  27. package/tests/examples/example36.test.ts +12 -0
  28. package/tests/examples/example37.test.ts +12 -0
  29. package/tests/examples/example38.test.ts +12 -0
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 EPS9 = 1e-9;
274
+ const EPS10 = 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) < -EPS9) return true;
278
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS10) 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) < -EPS9) return true;
281
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS10) return true;
282
282
  }
283
283
  }
284
284
  return false;
285
285
  };
286
- if (Math.abs(p1.x - p2.x) < EPS9 || Math.abs(p1.y - p2.y) < EPS9) {
286
+ if (Math.abs(p1.x - p2.x) < EPS10 || Math.abs(p1.y - p2.y) < EPS10) {
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 };
@@ -850,8 +850,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
850
850
  if (!collision) {
851
851
  const first = path[0];
852
852
  const last = path[path.length - 1];
853
- const EPS9 = 1e-9;
854
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS9 && Math.abs(p.y - q.y) < EPS9;
853
+ const EPS10 = 1e-9;
854
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS10 && Math.abs(p.y - q.y) < EPS10;
855
855
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
856
856
  this.solvedTracePath = path;
857
857
  this.solved = true;
@@ -1050,13 +1050,13 @@ var applyJogToTerminalSegment = ({
1050
1050
  segmentIndex: si,
1051
1051
  offset,
1052
1052
  JOG_SIZE,
1053
- EPS: EPS9 = 1e-6
1053
+ EPS: EPS10 = 1e-6
1054
1054
  }) => {
1055
1055
  if (si !== 0 && si !== pts.length - 2) return;
1056
1056
  const start = pts[si];
1057
1057
  const end = pts[si + 1];
1058
- const isVertical4 = Math.abs(start.x - end.x) < EPS9;
1059
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS9;
1058
+ const isVertical4 = Math.abs(start.x - end.x) < EPS10;
1059
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS10;
1060
1060
  if (!isVertical4 && !isHorizontal3) return;
1061
1061
  const segDir = isVertical4 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1062
1062
  if (si === 0) {
@@ -1123,13 +1123,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1123
1123
  }
1124
1124
  }
1125
1125
  _step() {
1126
- const EPS9 = 1e-6;
1126
+ const EPS10 = 1e-6;
1127
1127
  const offsets = this.overlappingTraceSegments.map((_, idx) => {
1128
1128
  const n = Math.floor(idx / 2) + 1;
1129
1129
  const signed = idx % 2 === 0 ? -n : n;
1130
1130
  return signed * this.SHIFT_DISTANCE;
1131
1131
  });
1132
- const eq = (a, b) => Math.abs(a - b) < EPS9;
1132
+ const eq = (a, b) => Math.abs(a - b) < EPS10;
1133
1133
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
1134
1134
  this.overlappingTraceSegments.forEach((group, gidx) => {
1135
1135
  const offset = offsets[gidx];
@@ -1155,13 +1155,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1155
1155
  segmentIndex: si,
1156
1156
  offset,
1157
1157
  JOG_SIZE,
1158
- EPS: EPS9
1158
+ EPS: EPS10
1159
1159
  });
1160
1160
  } else {
1161
1161
  const start = pts[si];
1162
1162
  const end = pts[si + 1];
1163
- const isVertical4 = Math.abs(start.x - end.x) < EPS9;
1164
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS9;
1163
+ const isVertical4 = Math.abs(start.x - end.x) < EPS10;
1164
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS10;
1165
1165
  if (!isVertical4 && !isHorizontal3) continue;
1166
1166
  if (isVertical4) {
1167
1167
  start.x += offset;
@@ -1258,7 +1258,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1258
1258
  return islands;
1259
1259
  }
1260
1260
  findNextOverlapIssue() {
1261
- const EPS9 = 2e-3;
1261
+ const EPS10 = 2e-3;
1262
1262
  const netIds = Object.keys(this.traceNetIslands);
1263
1263
  for (let i = 0; i < netIds.length; i++) {
1264
1264
  for (let j = i + 1; j < netIds.length; j++) {
@@ -1276,7 +1276,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1276
1276
  const minB = Math.min(b1, b2);
1277
1277
  const maxB = Math.max(b1, b2);
1278
1278
  const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
1279
- return overlap > EPS9;
1279
+ return overlap > EPS10;
1280
1280
  };
1281
1281
  for (let pa = 0; pa < pathsA.length; pa++) {
1282
1282
  const pathA = pathsA[pa];
@@ -1284,8 +1284,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1284
1284
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
1285
1285
  const a1 = ptsA[sa];
1286
1286
  const a2 = ptsA[sa + 1];
1287
- const aVert = Math.abs(a1.x - a2.x) < EPS9;
1288
- const aHorz = Math.abs(a1.y - a2.y) < EPS9;
1287
+ const aVert = Math.abs(a1.x - a2.x) < EPS10;
1288
+ const aHorz = Math.abs(a1.y - a2.y) < EPS10;
1289
1289
  if (!aVert && !aHorz) continue;
1290
1290
  for (let pb = 0; pb < pathsB.length; pb++) {
1291
1291
  const pathB = pathsB[pb];
@@ -1293,11 +1293,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1293
1293
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
1294
1294
  const b1 = ptsB[sb];
1295
1295
  const b2 = ptsB[sb + 1];
1296
- const bVert = Math.abs(b1.x - b2.x) < EPS9;
1297
- const bHorz = Math.abs(b1.y - b2.y) < EPS9;
1296
+ const bVert = Math.abs(b1.x - b2.x) < EPS10;
1297
+ const bHorz = Math.abs(b1.y - b2.y) < EPS10;
1298
1298
  if (!bVert && !bHorz) continue;
1299
1299
  if (aVert && bVert) {
1300
- if (Math.abs(a1.x - b1.x) < EPS9) {
1300
+ if (Math.abs(a1.x - b1.x) < EPS10) {
1301
1301
  if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
1302
1302
  const keyA = `${pa}:${sa}`;
1303
1303
  const keyB = `${pb}:${sb}`;
@@ -1318,7 +1318,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1318
1318
  }
1319
1319
  }
1320
1320
  } else if (aHorz && bHorz) {
1321
- if (Math.abs(a1.y - b1.y) < EPS9) {
1321
+ if (Math.abs(a1.y - b1.y) < EPS10) {
1322
1322
  if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
1323
1323
  const keyA = `${pa}:${sa}`;
1324
1324
  const keyB = `${pb}:${sb}`;
@@ -1356,14 +1356,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1356
1356
  return null;
1357
1357
  }
1358
1358
  findNextDiagonalSegment() {
1359
- const EPS9 = 2e-3;
1359
+ const EPS10 = 2e-3;
1360
1360
  for (const mspPairId in this.correctedTraceMap) {
1361
1361
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
1362
1362
  for (let i = 0; i < tracePath.length - 1; i++) {
1363
1363
  const p1 = tracePath[i];
1364
1364
  const p2 = tracePath[i + 1];
1365
- const isHorizontal3 = Math.abs(p1.y - p2.y) < EPS9;
1366
- const isVertical4 = Math.abs(p1.x - p2.x) < EPS9;
1365
+ const isHorizontal3 = Math.abs(p1.y - p2.y) < EPS10;
1366
+ const isVertical4 = Math.abs(p1.x - p2.x) < EPS10;
1367
1367
  if (!isHorizontal3 && !isVertical4) {
1368
1368
  return { mspPairId, tracePath, i, p1, p2 };
1369
1369
  }
@@ -1377,13 +1377,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1377
1377
  return false;
1378
1378
  }
1379
1379
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
1380
- const EPS9 = 2e-3;
1380
+ const EPS10 = 2e-3;
1381
1381
  const p0 = i > 0 ? tracePath[i - 1] : null;
1382
1382
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
1383
- const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS9 : false;
1384
- const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS9 : false;
1385
- const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS9 : false;
1386
- const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS9 : false;
1383
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS10 : false;
1384
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS10 : false;
1385
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS10 : false;
1386
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS10 : false;
1387
1387
  const elbow1 = { x: p1.x, y: p2.y };
1388
1388
  const elbow2 = { x: p2.x, y: p1.y };
1389
1389
  let score1 = 0;
@@ -1547,24 +1547,24 @@ function getRectBounds(center, w, h) {
1547
1547
  }
1548
1548
 
1549
1549
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
1550
- function segmentIntersectsRect2(p1, p2, rect, EPS9 = 1e-9) {
1551
- const isVert = Math.abs(p1.x - p2.x) < EPS9;
1552
- const isHorz = Math.abs(p1.y - p2.y) < EPS9;
1550
+ function segmentIntersectsRect2(p1, p2, rect, EPS10 = 1e-9) {
1551
+ const isVert = Math.abs(p1.x - p2.x) < EPS10;
1552
+ const isHorz = Math.abs(p1.y - p2.y) < EPS10;
1553
1553
  if (!isVert && !isHorz) return false;
1554
1554
  if (isVert) {
1555
1555
  const x = p1.x;
1556
- if (x < rect.minX - EPS9 || x > rect.maxX + EPS9) return false;
1556
+ if (x < rect.minX - EPS10 || x > rect.maxX + EPS10) return false;
1557
1557
  const segMinY = Math.min(p1.y, p2.y);
1558
1558
  const segMaxY = Math.max(p1.y, p2.y);
1559
1559
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
1560
- return overlap > EPS9;
1560
+ return overlap > EPS10;
1561
1561
  } else {
1562
1562
  const y = p1.y;
1563
- if (y < rect.minY - EPS9 || y > rect.maxY + EPS9) return false;
1563
+ if (y < rect.minY - EPS10 || y > rect.maxY + EPS10) return false;
1564
1564
  const segMinX = Math.min(p1.x, p2.x);
1565
1565
  const segMaxX = Math.max(p1.x, p2.x);
1566
1566
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
1567
- return overlap > EPS9;
1567
+ return overlap > EPS10;
1568
1568
  }
1569
1569
  }
1570
1570
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -1942,14 +1942,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1942
1942
  };
1943
1943
  let bestCandidate = null;
1944
1944
  let bestScore = -Infinity;
1945
- const EPS9 = 1e-6;
1945
+ const EPS10 = 1e-6;
1946
1946
  for (const curr of tracesToScan) {
1947
1947
  const pts = curr.tracePath.slice();
1948
1948
  for (let si = 0; si < pts.length - 1; si++) {
1949
1949
  const a = pts[si];
1950
1950
  const b = pts[si + 1];
1951
- const isH = Math.abs(a.y - b.y) < EPS9;
1952
- const isV = Math.abs(a.x - b.x) < EPS9;
1951
+ const isH = Math.abs(a.y - b.y) < EPS10;
1952
+ const isV = Math.abs(a.x - b.x) < EPS10;
1953
1953
  if (!isH && !isV) continue;
1954
1954
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
1955
1955
  const candidateOrients = orientations.filter(
@@ -2227,6 +2227,29 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2227
2227
  }
2228
2228
  return groups;
2229
2229
  }
2230
+ getNetLabelWidthForGroup(group) {
2231
+ if (group.netId) {
2232
+ const ncWidth = this.inputProblem.netConnections.find(
2233
+ (nc) => nc.netId === group.netId
2234
+ )?.netLabelWidth;
2235
+ if (ncWidth !== void 0) return ncWidth;
2236
+ const dcWidthByNetId = this.inputProblem.directConnections.find(
2237
+ (dc) => dc.netId === group.netId
2238
+ )?.netLabelWidth;
2239
+ if (dcWidthByNetId !== void 0) return dcWidthByNetId;
2240
+ }
2241
+ const pinIds = group.overlappingTraces?.pins.map((p) => p.pinId) ?? [];
2242
+ if (group.portOnlyPinId) {
2243
+ pinIds.push(group.portOnlyPinId);
2244
+ }
2245
+ const dcWidthByPinId = this.inputProblem.directConnections.find(
2246
+ (dc) => dc.pinIds.some((pid) => pinIds.includes(pid))
2247
+ )?.netLabelWidth;
2248
+ if (dcWidthByPinId !== void 0) return dcWidthByPinId;
2249
+ return this.inputProblem.netConnections.find(
2250
+ (nc) => nc.pinIds.some((pid) => pinIds.includes(pid))
2251
+ )?.netLabelWidth;
2252
+ }
2230
2253
  _step() {
2231
2254
  if (this.activeSubSolver?.solved) {
2232
2255
  this.netLabelPlacements.push(this.activeSubSolver.netLabelPlacement);
@@ -2241,9 +2264,7 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2241
2264
  const isAlreadyFull = currOrients.length === 4 && fullOrients.every((o) => currOrients.includes(o));
2242
2265
  if (!this.triedAnyOrientationFallbackForCurrentGroup && !isAlreadyFull && this.currentGroup) {
2243
2266
  this.triedAnyOrientationFallbackForCurrentGroup = true;
2244
- const netLabelWidth2 = this.currentGroup.netId ? this.inputProblem.netConnections.find(
2245
- (nc) => nc.netId === this.currentGroup.netId
2246
- )?.netLabelWidth : void 0;
2267
+ const netLabelWidth2 = this.getNetLabelWidthForGroup(this.currentGroup);
2247
2268
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
2248
2269
  inputProblem: this.inputProblem,
2249
2270
  inputTraceMap: this.inputTraceMap,
@@ -2269,9 +2290,7 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2269
2290
  const netId = nextOverlappingSameNetTraceGroup.netId ?? nextOverlappingSameNetTraceGroup.globalConnNetId;
2270
2291
  this.currentGroup = nextOverlappingSameNetTraceGroup;
2271
2292
  this.triedAnyOrientationFallbackForCurrentGroup = false;
2272
- const netLabelWidth = this.currentGroup.netId ? this.inputProblem.netConnections.find(
2273
- (nc) => nc.netId === this.currentGroup.netId
2274
- )?.netLabelWidth : void 0;
2293
+ const netLabelWidth = this.getNetLabelWidthForGroup(this.currentGroup);
2275
2294
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
2276
2295
  inputProblem: this.inputProblem,
2277
2296
  inputTraceMap: this.inputTraceMap,
@@ -4621,11 +4640,14 @@ var UntangleTraceSubsolver = class extends BaseSolver {
4621
4640
  };
4622
4641
 
4623
4642
  // lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
4643
+ var EPS5 = 1e-6;
4644
+ var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS5;
4645
+ var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS5;
4624
4646
  var is4PointRectangle = (path) => {
4625
4647
  if (path.length !== 4) return false;
4626
4648
  const [p0, p1, p2, p3] = path;
4627
- const isHVHC = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y && p0.x === p3.x;
4628
- const isVHVC = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x && p0.y === p3.y;
4649
+ const isHVHC = sameY(p0, p1) && sameX(p1, p2) && sameY(p2, p3) && sameX(p0, p3);
4650
+ const isVHVC = sameX(p0, p1) && sameY(p1, p2) && sameX(p2, p3) && sameY(p0, p3);
4629
4651
  return isHVHC || isVHVC;
4630
4652
  };
4631
4653
 
@@ -4754,7 +4776,7 @@ var TraceCleanupSolver = class extends BaseSolver {
4754
4776
  };
4755
4777
 
4756
4778
  // lib/solvers/Example28Solver/types.ts
4757
- var EPS5 = 1e-9;
4779
+ var EPS6 = 1e-9;
4758
4780
 
4759
4781
  // lib/solvers/Example28Solver/geometry.ts
4760
4782
  var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
@@ -4766,10 +4788,10 @@ var getPathLength = (path) => {
4766
4788
  return length;
4767
4789
  };
4768
4790
  var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
4769
- var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS5 || Math.abs(start.y - end.y) < EPS5;
4770
- var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS5 ? "horizontal" : "vertical";
4791
+ var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS6 || Math.abs(start.y - end.y) < EPS6;
4792
+ var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS6 ? "horizontal" : "vertical";
4771
4793
  var projectPointToSegment = (point, start, end) => {
4772
- if (Math.abs(start.x - end.x) < EPS5) {
4794
+ if (Math.abs(start.x - end.x) < EPS6) {
4773
4795
  return {
4774
4796
  x: start.x,
4775
4797
  y: Math.min(
@@ -4778,7 +4800,7 @@ var projectPointToSegment = (point, start, end) => {
4778
4800
  )
4779
4801
  };
4780
4802
  }
4781
- if (Math.abs(start.y - end.y) < EPS5) {
4803
+ if (Math.abs(start.y - end.y) < EPS6) {
4782
4804
  return {
4783
4805
  x: Math.min(
4784
4806
  Math.max(point.x, Math.min(start.x, end.x)),
@@ -4821,9 +4843,9 @@ var findSegmentContainingPoint = (path, point) => {
4821
4843
  };
4822
4844
  var isPointWithinSegmentPrimaryRange = (point, segment) => {
4823
4845
  if (segment.orientation === "horizontal") {
4824
- return point.x >= Math.min(segment.start.x, segment.end.x) - EPS5 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS5;
4846
+ return point.x >= Math.min(segment.start.x, segment.end.x) - EPS6 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS6;
4825
4847
  }
4826
- return point.y >= Math.min(segment.start.y, segment.end.y) - EPS5 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS5;
4848
+ return point.y >= Math.min(segment.start.y, segment.end.y) - EPS6 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS6;
4827
4849
  };
4828
4850
  var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
4829
4851
  const matchingSegments = getSegments(path).filter(
@@ -4851,18 +4873,18 @@ var projectPointToPath = (point, path) => {
4851
4873
  return bestPoint;
4852
4874
  };
4853
4875
  var segmentsIntersect = (a1, a2, b1, b2) => {
4854
- const aVertical = Math.abs(a1.x - a2.x) < EPS5;
4855
- const bVertical = Math.abs(b1.x - b2.x) < EPS5;
4856
- const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS5 && value <= Math.max(p1, p2) + EPS5;
4876
+ const aVertical = Math.abs(a1.x - a2.x) < EPS6;
4877
+ const bVertical = Math.abs(b1.x - b2.x) < EPS6;
4878
+ const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS6 && value <= Math.max(p1, p2) + EPS6;
4857
4879
  if (aVertical && bVertical) {
4858
- if (Math.abs(a1.x - b1.x) > EPS5) return false;
4880
+ if (Math.abs(a1.x - b1.x) > EPS6) return false;
4859
4881
  const overlap = Math.min(Math.max(a1.y, a2.y), Math.max(b1.y, b2.y)) - Math.max(Math.min(a1.y, a2.y), Math.min(b1.y, b2.y));
4860
- return overlap > EPS5;
4882
+ return overlap > EPS6;
4861
4883
  }
4862
4884
  if (!aVertical && !bVertical) {
4863
- if (Math.abs(a1.y - b1.y) > EPS5) return false;
4885
+ if (Math.abs(a1.y - b1.y) > EPS6) return false;
4864
4886
  const overlap = Math.min(Math.max(a1.x, a2.x), Math.max(b1.x, b2.x)) - Math.max(Math.min(a1.x, a2.x), Math.min(b1.x, b2.x));
4865
- return overlap > EPS5;
4887
+ return overlap > EPS6;
4866
4888
  }
4867
4889
  const verticalA = aVertical ? a1 : b1;
4868
4890
  const verticalB = aVertical ? a2 : b2;
@@ -4894,13 +4916,13 @@ var isPathCollidingWithChipInterior = (path, chipObstacles) => {
4894
4916
  return false;
4895
4917
  };
4896
4918
  var segmentRunsAlongRectBoundary = (start, end, rect) => {
4897
- const isVertical4 = Math.abs(start.x - end.x) < EPS5;
4898
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS5;
4919
+ const isVertical4 = Math.abs(start.x - end.x) < EPS6;
4920
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS6;
4899
4921
  if (isVertical4) {
4900
- return Math.abs(start.x - rect.minX) < EPS5 || Math.abs(start.x - rect.maxX) < EPS5;
4922
+ return Math.abs(start.x - rect.minX) < EPS6 || Math.abs(start.x - rect.maxX) < EPS6;
4901
4923
  }
4902
4924
  if (isHorizontal3) {
4903
- return Math.abs(start.y - rect.minY) < EPS5 || Math.abs(start.y - rect.maxY) < EPS5;
4925
+ return Math.abs(start.y - rect.minY) < EPS6 || Math.abs(start.y - rect.maxY) < EPS6;
4904
4926
  }
4905
4927
  return false;
4906
4928
  };
@@ -5008,9 +5030,37 @@ var generateRerouteCandidateResults = ({
5008
5030
  outputNetLabelPlacements,
5009
5031
  chipObstacles
5010
5032
  }) => {
5011
- const rawCandidates = generateCandidatePaths(trace, label, inputProblem);
5012
5033
  const seen = /* @__PURE__ */ new Set();
5013
5034
  const candidateResults = [];
5035
+ const horizontalSegmentPushCandidate = generateHorizontalSegmentPushCandidate(
5036
+ trace,
5037
+ label
5038
+ );
5039
+ if (horizontalSegmentPushCandidate) {
5040
+ candidateResults.push(
5041
+ createCandidateResult({
5042
+ trace,
5043
+ obstacleLabel: label,
5044
+ path: horizontalSegmentPushCandidate,
5045
+ seen,
5046
+ outputTraces,
5047
+ outputNetLabelPlacements,
5048
+ chipObstacles,
5049
+ usesHorizontalSegmentPush: true
5050
+ })
5051
+ );
5052
+ }
5053
+ const rawCandidates = [
5054
+ ...generateLabelHugCandidates(trace, label),
5055
+ ...generateRerouteCandidates({
5056
+ trace,
5057
+ label,
5058
+ problem: inputProblem,
5059
+ paddingBuffer: LABEL_SIDE_CLEARANCE,
5060
+ detourCount: 0
5061
+ }),
5062
+ ...generateEndpointDetourCandidates(trace, label)
5063
+ ];
5014
5064
  for (const rawCandidate of rawCandidates) {
5015
5065
  candidateResults.push(
5016
5066
  createCandidateResult({
@@ -5026,17 +5076,6 @@ var generateRerouteCandidateResults = ({
5026
5076
  }
5027
5077
  return candidateResults;
5028
5078
  };
5029
- var generateCandidatePaths = (trace, label, inputProblem) => [
5030
- ...generateLabelHugCandidates(trace, label),
5031
- ...generateRerouteCandidates({
5032
- trace,
5033
- label,
5034
- problem: inputProblem,
5035
- paddingBuffer: LABEL_SIDE_CLEARANCE,
5036
- detourCount: 0
5037
- }),
5038
- ...generateEndpointDetourCandidates(trace, label)
5039
- ];
5040
5079
  var createCandidateResult = ({
5041
5080
  trace,
5042
5081
  obstacleLabel,
@@ -5044,13 +5083,15 @@ var createCandidateResult = ({
5044
5083
  seen,
5045
5084
  outputTraces,
5046
5085
  outputNetLabelPlacements,
5047
- chipObstacles
5086
+ chipObstacles,
5087
+ usesHorizontalSegmentPush
5048
5088
  }) => {
5049
5089
  const key = getPathKey(path);
5050
5090
  if (seen.has(key)) {
5051
5091
  return {
5052
5092
  path,
5053
5093
  status: "duplicate",
5094
+ usesHorizontalSegmentPush,
5054
5095
  selected: false
5055
5096
  };
5056
5097
  }
@@ -5059,6 +5100,7 @@ var createCandidateResult = ({
5059
5100
  return {
5060
5101
  path,
5061
5102
  status: "chip-collision",
5103
+ usesHorizontalSegmentPush,
5062
5104
  selected: false
5063
5105
  };
5064
5106
  }
@@ -5072,6 +5114,7 @@ var createCandidateResult = ({
5072
5114
  outputNetLabelPlacements
5073
5115
  }),
5074
5116
  status: "valid",
5117
+ usesHorizontalSegmentPush,
5075
5118
  selected: false
5076
5119
  };
5077
5120
  };
@@ -5148,6 +5191,12 @@ var selectBestReroutePath = ({
5148
5191
  outputNetLabelPlacements,
5149
5192
  candidateResults
5150
5193
  }) => {
5194
+ for (const candidate of candidateResults) {
5195
+ if (!candidate.usesHorizontalSegmentPush) continue;
5196
+ if (candidate.status !== "valid") continue;
5197
+ if (!candidate.score) continue;
5198
+ return candidate.path;
5199
+ }
5151
5200
  let bestPath = null;
5152
5201
  let bestScore = scoreTracePath({
5153
5202
  trace,
@@ -5164,6 +5213,71 @@ var selectBestReroutePath = ({
5164
5213
  }
5165
5214
  return bestPath;
5166
5215
  };
5216
+ var generateHorizontalSegmentPushCandidate = (trace, label) => {
5217
+ const labelDirection = dir(label.orientation);
5218
+ if (labelDirection.x === 0) return null;
5219
+ const path = trace.tracePath;
5220
+ const bounds = getRectBounds(label.center, label.width, label.height);
5221
+ const collidingVerticalSegmentIndex = path.findIndex((start, index) => {
5222
+ const end = path[index + 1];
5223
+ if (!end) return false;
5224
+ if (Math.abs(start.x - end.x) >= 1e-9) return false;
5225
+ return segmentIntersectsRect2(start, end, bounds);
5226
+ });
5227
+ if (collidingVerticalSegmentIndex <= 0 || collidingVerticalSegmentIndex >= path.length - 2) {
5228
+ return null;
5229
+ }
5230
+ const previousAnchor = path[collidingVerticalSegmentIndex - 1];
5231
+ const verticalStart = path[collidingVerticalSegmentIndex];
5232
+ const verticalEnd = path[collidingVerticalSegmentIndex + 1];
5233
+ const nextAnchor = path[collidingVerticalSegmentIndex + 2];
5234
+ if (Math.abs(previousAnchor.y - verticalStart.y) >= 1e-9 || Math.abs(verticalEnd.y - nextAnchor.y) >= 1e-9) {
5235
+ return null;
5236
+ }
5237
+ let segmentPushX = bounds.minX - LABEL_SIDE_CLEARANCE;
5238
+ if (labelDirection.x > 0) {
5239
+ segmentPushX = bounds.maxX + LABEL_SIDE_CLEARANCE;
5240
+ }
5241
+ const segmentPushStartY = getClearedHorizontalY({
5242
+ start: previousAnchor,
5243
+ end: { x: segmentPushX, y: verticalStart.y },
5244
+ labelBounds: bounds
5245
+ });
5246
+ const segmentPushEndY = getClearedHorizontalY({
5247
+ start: { x: segmentPushX, y: verticalEnd.y },
5248
+ end: nextAnchor,
5249
+ labelBounds: bounds
5250
+ });
5251
+ const segmentPushPath = [
5252
+ ...path.slice(0, collidingVerticalSegmentIndex - 1),
5253
+ previousAnchor
5254
+ ];
5255
+ if (Math.abs(previousAnchor.y - segmentPushStartY) >= 1e-9) {
5256
+ segmentPushPath.push({ x: previousAnchor.x, y: segmentPushStartY });
5257
+ }
5258
+ segmentPushPath.push({ x: segmentPushX, y: segmentPushStartY });
5259
+ segmentPushPath.push({ x: segmentPushX, y: segmentPushEndY });
5260
+ if (Math.abs(nextAnchor.y - segmentPushEndY) >= 1e-9) {
5261
+ segmentPushPath.push({ x: nextAnchor.x, y: segmentPushEndY });
5262
+ }
5263
+ segmentPushPath.push(
5264
+ nextAnchor,
5265
+ ...path.slice(collidingVerticalSegmentIndex + 3)
5266
+ );
5267
+ return simplifyPath(segmentPushPath);
5268
+ };
5269
+ var getClearedHorizontalY = ({
5270
+ start,
5271
+ end,
5272
+ labelBounds
5273
+ }) => {
5274
+ if (!segmentIntersectsRect2(start, end, labelBounds)) return start.y;
5275
+ const labelCenterY = (labelBounds.minY + labelBounds.maxY) / 2;
5276
+ if (start.y >= labelCenterY) {
5277
+ return labelBounds.maxY + LABEL_HUG_CLEARANCE;
5278
+ }
5279
+ return labelBounds.minY - LABEL_HUG_CLEARANCE;
5280
+ };
5167
5281
  var markSelectedCandidate = (candidateResults, bestPath) => {
5168
5282
  if (!bestPath) return;
5169
5283
  for (const candidate of candidateResults) {
@@ -5432,16 +5546,16 @@ var Example28Solver = class extends BaseSolver {
5432
5546
  // lib/solvers/AvailableNetOrientationSolver/constants.ts
5433
5547
  var LABEL_SEARCH_STEP = 0.05;
5434
5548
  var WICK_CLEARANCE = 1e-3;
5435
- var EPS6 = 1e-9;
5436
- var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS6;
5549
+ var EPS7 = 1e-9;
5550
+ var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS7;
5437
5551
  var CANDIDATE_SELECTED_COLOR2 = "blue";
5438
5552
  var CANDIDATE_REJECTED_COLOR2 = "red";
5439
5553
 
5440
5554
  // lib/solvers/AvailableNetOrientationSolver/geometry.ts
5441
5555
  var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
5442
5556
  var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
5443
- var rectsOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS6 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS6;
5444
- var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS6;
5557
+ var rectsOverlap = (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;
5558
+ var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS7;
5445
5559
  var traceCrossesBoundsInterior = (bounds, traceMap) => {
5446
5560
  for (const trace of Object.values(traceMap)) {
5447
5561
  const points = trace.tracePath;
@@ -5463,8 +5577,8 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
5463
5577
  if (interiorBounds.minX >= interiorBounds.maxX || interiorBounds.minY >= interiorBounds.maxY) {
5464
5578
  return false;
5465
5579
  }
5466
- if (sameX(p1, p2)) {
5467
- if (p1.x <= interiorBounds.minX + EPS6 || p1.x >= interiorBounds.maxX - EPS6) {
5580
+ if (sameX2(p1, p2)) {
5581
+ if (p1.x <= interiorBounds.minX + EPS7 || p1.x >= interiorBounds.maxX - EPS7) {
5468
5582
  return false;
5469
5583
  }
5470
5584
  return rangesOverlap(
@@ -5474,8 +5588,8 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
5474
5588
  interiorBounds.maxY
5475
5589
  );
5476
5590
  }
5477
- if (sameY(p1, p2)) {
5478
- if (p1.y <= interiorBounds.minY + EPS6 || p1.y >= interiorBounds.maxY - EPS6) {
5591
+ if (sameY2(p1, p2)) {
5592
+ if (p1.y <= interiorBounds.minY + EPS7 || p1.y >= interiorBounds.maxY - EPS7) {
5479
5593
  return false;
5480
5594
  }
5481
5595
  return rangesOverlap(
@@ -5514,11 +5628,11 @@ var tracePathCrossesAnyTrace = (tracePath, traceMap) => {
5514
5628
  return false;
5515
5629
  };
5516
5630
  var segmentsStrictlyCross = (a1, a2, b1, b2) => {
5517
- if (sameX(a1, a2) && sameY(b1, b2)) {
5518
- return a1.x > Math.min(b1.x, b2.x) + EPS6 && a1.x < Math.max(b1.x, b2.x) - EPS6 && b1.y > Math.min(a1.y, a2.y) + EPS6 && b1.y < Math.max(a1.y, a2.y) - EPS6;
5631
+ if (sameX2(a1, a2) && sameY2(b1, b2)) {
5632
+ return a1.x > Math.min(b1.x, b2.x) + EPS7 && a1.x < Math.max(b1.x, b2.x) - EPS7 && b1.y > Math.min(a1.y, a2.y) + EPS7 && b1.y < Math.max(a1.y, a2.y) - EPS7;
5519
5633
  }
5520
- if (sameY(a1, a2) && sameX(b1, b2)) {
5521
- return b1.x > Math.min(a1.x, a2.x) + EPS6 && b1.x < Math.max(a1.x, a2.x) - EPS6 && a1.y > Math.min(b1.y, b2.y) + EPS6 && a1.y < Math.max(b1.y, b2.y) - EPS6;
5634
+ if (sameY2(a1, a2) && sameX2(b1, b2)) {
5635
+ return b1.x > Math.min(a1.x, a2.x) + EPS7 && b1.x < Math.max(a1.x, a2.x) - EPS7 && a1.y > Math.min(b1.y, b2.y) + EPS7 && a1.y < Math.max(b1.y, b2.y) - EPS7;
5522
5636
  }
5523
5637
  return false;
5524
5638
  };
@@ -5541,7 +5655,7 @@ var simplifyOrthogonalPath = (path) => {
5541
5655
  const prev = simplified[simplified.length - 1];
5542
5656
  const point = deduped[i];
5543
5657
  const next = deduped[i + 1];
5544
- if (sameX(prev, point) && sameX(point, next) || sameY(prev, point) && sameY(point, next)) {
5658
+ if (sameX2(prev, point) && sameX2(point, next) || sameY2(prev, point) && sameY2(point, next)) {
5545
5659
  continue;
5546
5660
  }
5547
5661
  simplified.push(point);
@@ -5549,9 +5663,9 @@ var simplifyOrthogonalPath = (path) => {
5549
5663
  simplified.push(deduped[deduped.length - 1]);
5550
5664
  return simplified;
5551
5665
  };
5552
- var pointsEqual = (a, b) => sameX(a, b) && sameY(a, b);
5553
- var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS6;
5554
- var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS6;
5666
+ var pointsEqual = (a, b) => sameX2(a, b) && sameY2(a, b);
5667
+ var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS7;
5668
+ var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS7;
5555
5669
  var getMaxSearchDistance = (inputProblem) => {
5556
5670
  const maxChipWidth = Math.max(
5557
5671
  ...inputProblem.chips.map((chip) => chip.width),
@@ -5759,11 +5873,28 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5759
5873
  this.addConnectorTrace(label, candidate, labelIndex);
5760
5874
  }
5761
5875
  addConnectorTrace(label, candidate, labelIndex) {
5762
- const tracePath = getConnectorTracePath(
5763
- label.anchorPoint,
5764
- candidate.anchorPoint,
5765
- candidate.orientation
5766
- );
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
+ }
5767
5898
  if (tracePath.length < 2) return;
5768
5899
  const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
5769
5900
  const connectorTrace = {
@@ -5796,7 +5927,18 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5796
5927
  labelIndex,
5797
5928
  orientations
5798
5929
  );
5799
- return rotatedCandidate ?? this.findValidShiftedCandidate(label, orientations[0], labelIndex);
5930
+ if (rotatedCandidate) return rotatedCandidate;
5931
+ const shiftedCandidate = this.findValidShiftedCandidate(
5932
+ label,
5933
+ orientations[0],
5934
+ labelIndex
5935
+ );
5936
+ if (shiftedCandidate) return shiftedCandidate;
5937
+ return this.findValidLateralShiftedCandidate(
5938
+ label,
5939
+ orientations[0],
5940
+ labelIndex
5941
+ );
5800
5942
  }
5801
5943
  findValidRotatedCandidate(label, labelIndex, orientations) {
5802
5944
  for (const orientation of orientations) {
@@ -5832,7 +5974,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5832
5974
  );
5833
5975
  const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
5834
5976
  const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
5835
- for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS6; outwardDistance += LABEL_SEARCH_STEP) {
5977
+ for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS7; outwardDistance += LABEL_SEARCH_STEP) {
5836
5978
  const baseAnchor = {
5837
5979
  x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
5838
5980
  y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
@@ -5850,6 +5992,50 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5850
5992
  }
5851
5993
  return null;
5852
5994
  }
5995
+ /**
5996
+ * When all candidates fail for the current (unshifted) position, try
5997
+ * shifting the label anchor laterally — x for y-orientations, y for
5998
+ * x-orientations — and re-attempting the required orientation.
5999
+ *
6000
+ * Offsets are tried in alternating sign order:
6001
+ * -1·step, +1·step, -2·step, +2·step, …
6002
+ * so the nearest escape routes are tested first.
6003
+ */
6004
+ findValidLateralShiftedCandidate(label, orientation, labelIndex) {
6005
+ const direction = dir(orientation);
6006
+ const initialBaseAnchor = this.getSearchStartAnchor(label, orientation);
6007
+ const lateralDir = {
6008
+ x: isYOrientation(orientation) ? 1 : 0,
6009
+ y: isXOrientation(orientation) ? 1 : 0
6010
+ };
6011
+ const maxSteps = Math.ceil(this.maxSearchDistance / LABEL_SEARCH_STEP);
6012
+ for (let step = 1; step <= maxSteps; step++) {
6013
+ for (const sign of [-1, 1]) {
6014
+ const lateralOffset = sign * step * LABEL_SEARCH_STEP;
6015
+ const baseAnchor = {
6016
+ x: initialBaseAnchor.x + lateralDir.x * lateralOffset,
6017
+ y: initialBaseAnchor.y + lateralDir.y * lateralOffset
6018
+ };
6019
+ const maxSearchDistance = this.getLateralColumnMaxDistance(
6020
+ label,
6021
+ orientation,
6022
+ baseAnchor
6023
+ );
6024
+ const candidate = this.findValidCandidateInShiftColumn({
6025
+ label,
6026
+ labelIndex,
6027
+ orientation,
6028
+ direction,
6029
+ baseAnchor,
6030
+ maxSearchDistance,
6031
+ outwardDistance: lateralOffset,
6032
+ phase: "lateral-shift"
6033
+ });
6034
+ if (candidate) return candidate;
6035
+ }
6036
+ }
6037
+ return null;
6038
+ }
5853
6039
  findValidCandidateInShiftColumn(params) {
5854
6040
  const {
5855
6041
  label,
@@ -5858,9 +6044,10 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5858
6044
  direction,
5859
6045
  baseAnchor,
5860
6046
  maxSearchDistance,
5861
- outwardDistance
6047
+ outwardDistance,
6048
+ phase = "shift"
5862
6049
  } = params;
5863
- for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS6; distance3 += LABEL_SEARCH_STEP) {
6050
+ for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS7; distance3 += LABEL_SEARCH_STEP) {
5864
6051
  const anchorPoint = {
5865
6052
  x: baseAnchor.x + direction.x * distance3,
5866
6053
  y: baseAnchor.y + direction.y * distance3
@@ -5870,7 +6057,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5870
6057
  candidate,
5871
6058
  label,
5872
6059
  labelIndex,
5873
- "shift",
6060
+ phase,
5874
6061
  distance3,
5875
6062
  outwardDistance
5876
6063
  );
@@ -5951,6 +6138,21 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5951
6138
  const labelLength = orientation === "y+" || orientation === "y-" ? height : width;
5952
6139
  return Math.min(this.maxSearchDistance, labelLength * 2);
5953
6140
  }
6141
+ getLateralColumnMaxDistance(label, orientation, baseAnchor) {
6142
+ const chipId = label.pinIds.map((pid) => this.pinMap[pid]?.chipId).find(Boolean);
6143
+ const chip = chipId ? this.chipObstacleSpatialIndex.chips.find((c) => c.chipId === chipId) : null;
6144
+ if (chip) {
6145
+ if (orientation === "y-")
6146
+ return Math.max(0, baseAnchor.y - chip.bounds.minY);
6147
+ if (orientation === "y+")
6148
+ return Math.max(0, chip.bounds.maxY - baseAnchor.y);
6149
+ if (orientation === "x-")
6150
+ return Math.max(0, baseAnchor.x - chip.bounds.minX);
6151
+ if (orientation === "x+")
6152
+ return Math.max(0, chip.bounds.maxX - baseAnchor.x);
6153
+ }
6154
+ return this.getSearchDistanceLimit(label, orientation);
6155
+ }
5954
6156
  createCandidate(label, anchorPoint, orientation) {
5955
6157
  const { width, height } = getDimsForOrientation({
5956
6158
  orientation,
@@ -5965,9 +6167,22 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5965
6167
  };
5966
6168
  }
5967
6169
  getNetLabelWidth(label) {
5968
- if (!label.netId) return void 0;
6170
+ if (label.netId) {
6171
+ const ncWidth = this.inputProblem.netConnections.find(
6172
+ (connection) => connection.netId === label.netId
6173
+ )?.netLabelWidth;
6174
+ if (ncWidth !== void 0) return ncWidth;
6175
+ const dcWidthByNetId = this.inputProblem.directConnections.find(
6176
+ (dc) => dc.netId === label.netId
6177
+ )?.netLabelWidth;
6178
+ if (dcWidthByNetId !== void 0) return dcWidthByNetId;
6179
+ }
6180
+ const dcWidthByPinId = this.inputProblem.directConnections.find(
6181
+ (dc) => dc.pinIds.some((pid) => label.pinIds.includes(pid))
6182
+ )?.netLabelWidth;
6183
+ if (dcWidthByPinId !== void 0) return dcWidthByPinId;
5969
6184
  return this.inputProblem.netConnections.find(
5970
- (connection) => connection.netId === label.netId
6185
+ (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
5971
6186
  )?.netLabelWidth;
5972
6187
  }
5973
6188
  getCandidateStatus(candidate, label, labelIndex) {
@@ -5984,6 +6199,11 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5984
6199
  if (tracePathCrossesAnyTrace(connectorTrace, this.traceMap)) {
5985
6200
  return "trace-collision";
5986
6201
  }
6202
+ for (const chip of this.chipObstacleSpatialIndex.chips) {
6203
+ if (tracePathCrossesAnyBounds(connectorTrace, chip.bounds)) {
6204
+ return "chip-collision";
6205
+ }
6206
+ }
5987
6207
  for (let i = 0; i < this.outputNetLabelPlacements.length; i++) {
5988
6208
  if (i === labelIndex) continue;
5989
6209
  const otherLabel = this.outputNetLabelPlacements[i];
@@ -6023,8 +6243,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6023
6243
  sharesChipBoundary(bounds) {
6024
6244
  for (const chip of this.chipObstacleSpatialIndex.chips) {
6025
6245
  const chipBounds = chip.bounds;
6026
- const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS6 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS6;
6027
- const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS6 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS6;
6246
+ const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS7 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS7;
6247
+ const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS7 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS7;
6028
6248
  if (adjacentToVerticalSide && rangesOverlap(
6029
6249
  bounds.minY,
6030
6250
  bounds.maxY,
@@ -6072,7 +6292,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6072
6292
  let nearestDistance = Number.POSITIVE_INFINITY;
6073
6293
  for (const chip of this.chipObstacleSpatialIndex.chips) {
6074
6294
  const bounds = chip.bounds;
6075
- if (point.x < bounds.minX - EPS6 || point.x > bounds.maxX + EPS6 || point.y < bounds.minY - EPS6 || point.y > bounds.maxY + EPS6) {
6295
+ if (point.x < bounds.minX - EPS7 || point.x > bounds.maxX + EPS7 || point.y < bounds.minY - EPS7 || point.y > bounds.maxY + EPS7) {
6076
6296
  continue;
6077
6297
  }
6078
6298
  for (const [side, distance3] of getSideDistances(point, bounds)) {
@@ -6089,8 +6309,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6089
6309
  let nearestDistanceSq = Number.POSITIVE_INFINITY;
6090
6310
  for (const chip of this.chipObstacleSpatialIndex.chips) {
6091
6311
  const bounds = chip.bounds;
6092
- const dx = point.x < bounds.minX - EPS6 ? bounds.minX - point.x : point.x > bounds.maxX + EPS6 ? point.x - bounds.maxX : 0;
6093
- const dy = point.y < bounds.minY - EPS6 ? bounds.minY - point.y : point.y > bounds.maxY + EPS6 ? point.y - bounds.maxY : 0;
6312
+ const dx = point.x < bounds.minX - EPS7 ? bounds.minX - point.x : point.x > bounds.maxX + EPS7 ? point.x - bounds.maxX : 0;
6313
+ const dy = point.y < bounds.minY - EPS7 ? bounds.minY - point.y : point.y > bounds.maxY + EPS7 ? point.y - bounds.maxY : 0;
6094
6314
  if (dx === 0 && dy === 0) continue;
6095
6315
  const distanceSq = dx ** 2 + dy ** 2;
6096
6316
  if (distanceSq >= nearestDistanceSq) continue;
@@ -6129,9 +6349,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6129
6349
  };
6130
6350
 
6131
6351
  // lib/solvers/VccNetLabelCornerPlacementSolver/geometry.ts
6132
- var EPS7 = 1e-6;
6352
+ var EPS8 = 1e-6;
6133
6353
  var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
6134
- 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;
6354
+ var rectsOverlap2 = (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;
6135
6355
  var getTraceCorners = (path) => {
6136
6356
  const corners = [];
6137
6357
  for (let i = 1; i < path.length - 1; i++) {
@@ -6155,11 +6375,11 @@ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
6155
6375
  var isTraceCorner = (a, b, c) => getSegmentOrientation2(a, b) !== getSegmentOrientation2(b, c);
6156
6376
  var isPointOnSegment = (point, start, end) => {
6157
6377
  if (getSegmentOrientation2(start, end) === "horizontal") {
6158
- 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;
6378
+ 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;
6159
6379
  }
6160
- 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;
6380
+ 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;
6161
6381
  };
6162
- var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS7 ? "horizontal" : "vertical";
6382
+ var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS8 ? "horizontal" : "vertical";
6163
6383
 
6164
6384
  // lib/solvers/VccNetLabelCornerPlacementSolver/visualize.ts
6165
6385
  var CANDIDATE_SELECTED_COLOR3 = "blue";
@@ -6437,11 +6657,11 @@ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
6437
6657
  };
6438
6658
 
6439
6659
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
6440
- var EPS8 = 1e-6;
6660
+ var EPS9 = 1e-6;
6441
6661
  var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
6442
6662
  var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
6443
- 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;
6444
- var rectsTouchOrOverlap = (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;
6663
+ var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS9;
6664
+ var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -EPS9 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) >= -EPS9;
6445
6665
  var traceCrossesBoundsInterior2 = (bounds, traces) => {
6446
6666
  for (const trace of traces) {
6447
6667
  const points = trace.tracePath;
@@ -6487,7 +6707,7 @@ var getPointAtTraceDistance = (trace, distance3) => {
6487
6707
  const end = trace.tracePath[i + 1];
6488
6708
  const segmentLength = getManhattanDistance(start, end);
6489
6709
  const nextDistance = pathDistance + segmentLength;
6490
- if (distance3 <= nextDistance + EPS8) {
6710
+ if (distance3 <= nextDistance + EPS9) {
6491
6711
  const offset = Math.max(
6492
6712
  0,
6493
6713
  Math.min(segmentLength, distance3 - pathDistance)
@@ -6526,7 +6746,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
6526
6746
  return false;
6527
6747
  }
6528
6748
  if (isVertical3(start, end)) {
6529
- if (start.x <= interior.minX + EPS8 || start.x >= interior.maxX - EPS8) {
6749
+ if (start.x <= interior.minX + EPS9 || start.x >= interior.maxX - EPS9) {
6530
6750
  return false;
6531
6751
  }
6532
6752
  return rangesOverlap2(
@@ -6537,7 +6757,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
6537
6757
  );
6538
6758
  }
6539
6759
  if (isHorizontal2(start, end)) {
6540
- if (start.y <= interior.minY + EPS8 || start.y >= interior.maxY - EPS8) {
6760
+ if (start.y <= interior.minY + EPS9 || start.y >= interior.maxY - EPS9) {
6541
6761
  return false;
6542
6762
  }
6543
6763
  return rangesOverlap2(
@@ -6551,10 +6771,10 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
6551
6771
  };
6552
6772
  var isPointOnSegment2 = (point, start, end) => {
6553
6773
  if (isHorizontal2(start, end)) {
6554
- 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;
6774
+ return Math.abs(point.y - start.y) <= EPS9 && point.x >= Math.min(start.x, end.x) - EPS9 && point.x <= Math.max(start.x, end.x) + EPS9;
6555
6775
  }
6556
6776
  if (isVertical3(start, end)) {
6557
- 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;
6777
+ return Math.abs(point.x - start.x) <= EPS9 && point.y >= Math.min(start.y, end.y) - EPS9 && point.y <= Math.max(start.y, end.y) + EPS9;
6558
6778
  }
6559
6779
  return false;
6560
6780
  };
@@ -6562,9 +6782,9 @@ var getSegmentDirection = (start, end) => ({
6562
6782
  x: isVertical3(start, end) ? 0 : Math.sign(end.x - start.x),
6563
6783
  y: isHorizontal2(start, end) ? 0 : Math.sign(end.y - start.y)
6564
6784
  });
6565
- var isHorizontal2 = (start, end) => Math.abs(start.y - end.y) <= EPS8;
6566
- var isVertical3 = (start, end) => Math.abs(start.x - end.x) <= EPS8;
6567
- var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS8;
6785
+ var isHorizontal2 = (start, end) => Math.abs(start.y - end.y) <= EPS9;
6786
+ var isVertical3 = (start, end) => Math.abs(start.x - end.x) <= EPS9;
6787
+ var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS9;
6568
6788
  var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
6569
6789
 
6570
6790
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
@@ -6619,7 +6839,7 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
6619
6839
  for (const distance3 of vertexDistances) {
6620
6840
  distances.add(roundDistance(distance3));
6621
6841
  }
6622
- return [...distances].filter((distance3) => distance3 >= -EPS8 && distance3 <= traceLength + EPS8).sort((a, b) => a - b);
6842
+ return [...distances].filter((distance3) => distance3 >= -EPS9 && distance3 <= traceLength + EPS9).sort((a, b) => a - b);
6623
6843
  };
6624
6844
  var getOrientationsForPoint = (params) => {
6625
6845
  const { inputProblem, label, point, orientationConstraint } = params;
@@ -6726,13 +6946,13 @@ var getOutwardOrientationOptions = (point, chipBounds) => {
6726
6946
  };
6727
6947
  var getOutwardAxisOption = (params) => {
6728
6948
  const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
6729
- if (value > max + EPS8) {
6949
+ if (value > max + EPS9) {
6730
6950
  return {
6731
6951
  orientation: positiveOrientation,
6732
6952
  outwardScore: 1 + value - max
6733
6953
  };
6734
6954
  }
6735
- if (value < min - EPS8) {
6955
+ if (value < min - EPS9) {
6736
6956
  return {
6737
6957
  orientation: negativeOrientation,
6738
6958
  outwardScore: 1 + min - value
@@ -6743,7 +6963,7 @@ var getOutwardAxisOption = (params) => {
6743
6963
  outwardScore: getNormalizedCenterDistance(value, center, max - min)
6744
6964
  };
6745
6965
  };
6746
- var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS8, span / 2);
6966
+ var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS9, span / 2);
6747
6967
  var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
6748
6968
  var getFlippedOrientation = (orientation) => {
6749
6969
  switch (orientation) {
@@ -6789,11 +7009,22 @@ var getChipBounds = (inputProblem) => {
6789
7009
  };
6790
7010
  };
6791
7011
  var getNetLabelWidth = (inputProblem, label) => {
6792
- const configuredWidth = inputProblem.netConnections.find(
7012
+ const ncWidthByNetId = inputProblem.netConnections.find(
6793
7013
  (connection) => connection.netId === label.netId
6794
7014
  )?.netLabelWidth;
6795
- if (configuredWidth !== void 0) return configuredWidth;
6796
- return label.orientation === "y+" || label.orientation === "y-" ? label.height : label.width;
7015
+ if (ncWidthByNetId !== void 0) return ncWidthByNetId;
7016
+ const dcWidth = inputProblem.directConnections.find(
7017
+ (dc) => dc.pinIds.some((pid) => label.pinIds.includes(pid))
7018
+ )?.netLabelWidth;
7019
+ if (dcWidth !== void 0) return dcWidth;
7020
+ const ncWidthByPinId = inputProblem.netConnections.find(
7021
+ (nc) => nc.pinIds.some((pid) => label.pinIds.includes(pid))
7022
+ )?.netLabelWidth;
7023
+ if (ncWidthByPinId !== void 0) return ncWidthByPinId;
7024
+ if (label.orientation === "y+" || label.orientation === "y-") {
7025
+ return label.height;
7026
+ }
7027
+ return label.width;
6797
7028
  };
6798
7029
  var roundDistance = (distance3) => Number(distance3.toFixed(6));
6799
7030
  var dedupeOrientations = (orientations) => [
@@ -6818,7 +7049,7 @@ var normalizeFacingDirection = (value) => {
6818
7049
  var dedupeStrings = (values) => [
6819
7050
  ...new Set(values.filter((value) => value !== void 0))
6820
7051
  ];
6821
- var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS8 && Math.abs(point.y - label.anchorPoint.y) <= EPS8 && orientation === label.orientation;
7052
+ var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS9 && Math.abs(point.y - label.anchorPoint.y) <= EPS9 && orientation === label.orientation;
6822
7053
 
6823
7054
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
6824
7055
  var CANDIDATE_SELECTED_COLOR4 = "blue";