@tscircuit/schematic-trace-solver 0.0.64 → 0.0.65

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.d.ts CHANGED
@@ -204,13 +204,17 @@ interface OverlappingTraceSegmentLocator {
204
204
  declare class TraceOverlapIssueSolver extends BaseSolver {
205
205
  overlappingTraceSegments: OverlappingTraceSegmentLocator[];
206
206
  traceNetIslands: Record<ConnNetId$1, Array<SolvedTracePath>>;
207
+ obstacleRects: ReturnType<typeof getObstacleRects>;
207
208
  SHIFT_DISTANCE: number;
208
209
  correctedTraceMap: Record<MspConnectionPairId, SolvedTracePath>;
209
210
  constructor(params: {
211
+ inputProblem: InputProblem;
210
212
  overlappingTraceSegments: OverlappingTraceSegmentLocator[];
211
213
  traceNetIslands: Record<ConnNetId$1, Array<SolvedTracePath>>;
212
214
  });
213
215
  _step(): void;
216
+ private getObstacleAwareOffset;
217
+ private getBlockingCollisions;
214
218
  visualize(): GraphicsObject;
215
219
  }
216
220
 
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 EPS10 = 1e-9;
274
+ const EPS11 = 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) < -EPS10) return true;
278
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS11) 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) < -EPS10) return true;
281
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS11) return true;
282
282
  }
283
283
  }
284
284
  return false;
285
285
  };
286
- if (Math.abs(p1.x - p2.x) < EPS10 || Math.abs(p1.y - p2.y) < EPS10) {
286
+ if (Math.abs(p1.x - p2.x) < EPS11 || Math.abs(p1.y - p2.y) < EPS11) {
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 EPS10 = 1e-9;
854
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS10 && Math.abs(p.y - q.y) < EPS10;
853
+ const EPS11 = 1e-9;
854
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS11 && Math.abs(p.y - q.y) < EPS11;
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: EPS10 = 1e-6
1053
+ EPS: EPS11 = 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) < EPS10;
1059
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS10;
1058
+ const isVertical4 = Math.abs(start.x - end.x) < EPS11;
1059
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS11;
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) {
@@ -1103,15 +1103,18 @@ var applyJogToTerminalSegment = ({
1103
1103
  };
1104
1104
 
1105
1105
  // lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts
1106
+ var EPS4 = 1e-6;
1106
1107
  var TraceOverlapIssueSolver = class extends BaseSolver {
1107
1108
  overlappingTraceSegments;
1108
1109
  traceNetIslands;
1110
+ obstacleRects;
1109
1111
  SHIFT_DISTANCE = 0.1;
1110
1112
  correctedTraceMap = {};
1111
1113
  constructor(params) {
1112
1114
  super();
1113
1115
  this.overlappingTraceSegments = params.overlappingTraceSegments;
1114
1116
  this.traceNetIslands = params.traceNetIslands;
1117
+ this.obstacleRects = getObstacleRects(params.inputProblem);
1115
1118
  for (const { connNetId, pathsWithOverlap } of this.overlappingTraceSegments) {
1116
1119
  for (const {
1117
1120
  solvedTracePathIndex,
@@ -1123,13 +1126,15 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1123
1126
  }
1124
1127
  }
1125
1128
  _step() {
1126
- const EPS10 = 1e-6;
1127
- const offsets = this.overlappingTraceSegments.map((_, idx) => {
1129
+ const offsets = this.overlappingTraceSegments.map((group, idx) => {
1128
1130
  const n = Math.floor(idx / 2) + 1;
1129
1131
  const signed = idx % 2 === 0 ? -n : n;
1130
- return signed * this.SHIFT_DISTANCE;
1132
+ return this.getObstacleAwareOffset({
1133
+ group,
1134
+ offset: signed * this.SHIFT_DISTANCE
1135
+ });
1131
1136
  });
1132
- const eq = (a, b) => Math.abs(a - b) < EPS10;
1137
+ const eq = (a, b) => Math.abs(a - b) < EPS4;
1133
1138
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
1134
1139
  this.overlappingTraceSegments.forEach((group, gidx) => {
1135
1140
  const offset = offsets[gidx];
@@ -1144,7 +1149,6 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1144
1149
  const original = this.traceNetIslands[group.connNetId][pathIdx];
1145
1150
  const current = this.correctedTraceMap[original.mspPairId] ?? original;
1146
1151
  const pts = current.tracePath.map((p) => ({ ...p }));
1147
- const segIdxs = Array.from(segIdxSet).sort((a, b) => a - b);
1148
1152
  const segIdxsRev = Array.from(segIdxSet).sort((a, b) => a - b).reverse();
1149
1153
  const JOG_SIZE = this.SHIFT_DISTANCE;
1150
1154
  for (const si of segIdxsRev) {
@@ -1155,13 +1159,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1155
1159
  segmentIndex: si,
1156
1160
  offset,
1157
1161
  JOG_SIZE,
1158
- EPS: EPS10
1162
+ EPS: EPS4
1159
1163
  });
1160
1164
  } else {
1161
1165
  const start = pts[si];
1162
1166
  const end = pts[si + 1];
1163
- const isVertical4 = Math.abs(start.x - end.x) < EPS10;
1164
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS10;
1167
+ const isVertical4 = Math.abs(start.x - end.x) < EPS4;
1168
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS4;
1165
1169
  if (!isVertical4 && !isHorizontal3) continue;
1166
1170
  if (isVertical4) {
1167
1171
  start.x += offset;
@@ -1186,6 +1190,53 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1186
1190
  });
1187
1191
  this.solved = true;
1188
1192
  }
1193
+ getObstacleAwareOffset({
1194
+ group,
1195
+ offset
1196
+ }) {
1197
+ const blockingCollisions = this.getBlockingCollisions({ group, offset });
1198
+ if (blockingCollisions.length === 0) return offset;
1199
+ return blockingCollisions.flatMap(({ start, isVertical: isVertical4, obstacle }) => [
1200
+ (isVertical4 ? obstacle.minX - start.x : obstacle.minY - start.y) - this.SHIFT_DISTANCE,
1201
+ (isVertical4 ? obstacle.maxX - start.x : obstacle.maxY - start.y) + this.SHIFT_DISTANCE
1202
+ ]).filter(
1203
+ (candidateOffset) => this.getBlockingCollisions({ group, offset: candidateOffset }).length === 0
1204
+ ).sort((a, b) => Math.abs(a - offset) - Math.abs(b - offset))[0] ?? offset;
1205
+ }
1206
+ getBlockingCollisions({
1207
+ group,
1208
+ offset
1209
+ }) {
1210
+ const blockingCollisions = [];
1211
+ for (const {
1212
+ solvedTracePathIndex,
1213
+ traceSegmentIndex
1214
+ } of group.pathsWithOverlap) {
1215
+ const trace = this.traceNetIslands[group.connNetId][solvedTracePathIndex];
1216
+ const start = trace.tracePath[traceSegmentIndex];
1217
+ const end = trace.tracePath[traceSegmentIndex + 1];
1218
+ if (!start || !end) continue;
1219
+ const isVertical4 = Math.abs(start.x - end.x) < EPS4;
1220
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS4;
1221
+ if (!isVertical4 && !isHorizontal3) continue;
1222
+ const shiftedSegment = isVertical4 ? [
1223
+ { ...start, x: start.x + offset },
1224
+ { ...end, x: end.x + offset }
1225
+ ] : [
1226
+ { ...start, y: start.y + offset },
1227
+ { ...end, y: end.y + offset }
1228
+ ];
1229
+ const collision = findFirstCollision(shiftedSegment, this.obstacleRects);
1230
+ if (collision) {
1231
+ blockingCollisions.push({
1232
+ start,
1233
+ isVertical: isVertical4,
1234
+ obstacle: collision.rect
1235
+ });
1236
+ }
1237
+ }
1238
+ return blockingCollisions;
1239
+ }
1189
1240
  visualize() {
1190
1241
  const graphics = {
1191
1242
  lines: [],
@@ -1258,7 +1309,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1258
1309
  return islands;
1259
1310
  }
1260
1311
  findNextOverlapIssue() {
1261
- const EPS10 = 2e-3;
1312
+ const EPS11 = 2e-3;
1262
1313
  const netIds = Object.keys(this.traceNetIslands);
1263
1314
  for (let i = 0; i < netIds.length; i++) {
1264
1315
  for (let j = i + 1; j < netIds.length; j++) {
@@ -1276,7 +1327,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1276
1327
  const minB = Math.min(b1, b2);
1277
1328
  const maxB = Math.max(b1, b2);
1278
1329
  const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
1279
- return overlap > EPS10;
1330
+ return overlap > EPS11;
1280
1331
  };
1281
1332
  for (let pa = 0; pa < pathsA.length; pa++) {
1282
1333
  const pathA = pathsA[pa];
@@ -1284,8 +1335,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1284
1335
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
1285
1336
  const a1 = ptsA[sa];
1286
1337
  const a2 = ptsA[sa + 1];
1287
- const aVert = Math.abs(a1.x - a2.x) < EPS10;
1288
- const aHorz = Math.abs(a1.y - a2.y) < EPS10;
1338
+ const aVert = Math.abs(a1.x - a2.x) < EPS11;
1339
+ const aHorz = Math.abs(a1.y - a2.y) < EPS11;
1289
1340
  if (!aVert && !aHorz) continue;
1290
1341
  for (let pb = 0; pb < pathsB.length; pb++) {
1291
1342
  const pathB = pathsB[pb];
@@ -1293,11 +1344,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1293
1344
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
1294
1345
  const b1 = ptsB[sb];
1295
1346
  const b2 = ptsB[sb + 1];
1296
- const bVert = Math.abs(b1.x - b2.x) < EPS10;
1297
- const bHorz = Math.abs(b1.y - b2.y) < EPS10;
1347
+ const bVert = Math.abs(b1.x - b2.x) < EPS11;
1348
+ const bHorz = Math.abs(b1.y - b2.y) < EPS11;
1298
1349
  if (!bVert && !bHorz) continue;
1299
1350
  if (aVert && bVert) {
1300
- if (Math.abs(a1.x - b1.x) < EPS10) {
1351
+ if (Math.abs(a1.x - b1.x) < EPS11) {
1301
1352
  if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
1302
1353
  const keyA = `${pa}:${sa}`;
1303
1354
  const keyB = `${pb}:${sb}`;
@@ -1318,7 +1369,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1318
1369
  }
1319
1370
  }
1320
1371
  } else if (aHorz && bHorz) {
1321
- if (Math.abs(a1.y - b1.y) < EPS10) {
1372
+ if (Math.abs(a1.y - b1.y) < EPS11) {
1322
1373
  if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
1323
1374
  const keyA = `${pa}:${sa}`;
1324
1375
  const keyB = `${pb}:${sb}`;
@@ -1356,14 +1407,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1356
1407
  return null;
1357
1408
  }
1358
1409
  findNextDiagonalSegment() {
1359
- const EPS10 = 2e-3;
1410
+ const EPS11 = 2e-3;
1360
1411
  for (const mspPairId in this.correctedTraceMap) {
1361
1412
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
1362
1413
  for (let i = 0; i < tracePath.length - 1; i++) {
1363
1414
  const p1 = tracePath[i];
1364
1415
  const p2 = tracePath[i + 1];
1365
- const isHorizontal3 = Math.abs(p1.y - p2.y) < EPS10;
1366
- const isVertical4 = Math.abs(p1.x - p2.x) < EPS10;
1416
+ const isHorizontal3 = Math.abs(p1.y - p2.y) < EPS11;
1417
+ const isVertical4 = Math.abs(p1.x - p2.x) < EPS11;
1367
1418
  if (!isHorizontal3 && !isVertical4) {
1368
1419
  return { mspPairId, tracePath, i, p1, p2 };
1369
1420
  }
@@ -1377,13 +1428,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1377
1428
  return false;
1378
1429
  }
1379
1430
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
1380
- const EPS10 = 2e-3;
1431
+ const EPS11 = 2e-3;
1381
1432
  const p0 = i > 0 ? tracePath[i - 1] : null;
1382
1433
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
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;
1434
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS11 : false;
1435
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS11 : false;
1436
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS11 : false;
1437
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS11 : false;
1387
1438
  const elbow1 = { x: p1.x, y: p2.y };
1388
1439
  const elbow2 = { x: p2.x, y: p1.y };
1389
1440
  let score1 = 0;
@@ -1428,6 +1479,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1428
1479
  }
1429
1480
  const { overlappingTraceSegments } = overlapIssue;
1430
1481
  this.activeSubSolver = new TraceOverlapIssueSolver({
1482
+ inputProblem: this.inputProblem,
1431
1483
  overlappingTraceSegments,
1432
1484
  traceNetIslands: this.traceNetIslands
1433
1485
  });
@@ -1548,24 +1600,24 @@ function getRectBounds(center, w, h) {
1548
1600
  }
1549
1601
 
1550
1602
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
1551
- function segmentIntersectsRect2(p1, p2, rect, EPS10 = 1e-9) {
1552
- const isVert = Math.abs(p1.x - p2.x) < EPS10;
1553
- const isHorz = Math.abs(p1.y - p2.y) < EPS10;
1603
+ function segmentIntersectsRect2(p1, p2, rect, EPS11 = 1e-9) {
1604
+ const isVert = Math.abs(p1.x - p2.x) < EPS11;
1605
+ const isHorz = Math.abs(p1.y - p2.y) < EPS11;
1554
1606
  if (!isVert && !isHorz) return false;
1555
1607
  if (isVert) {
1556
1608
  const x = p1.x;
1557
- if (x < rect.minX - EPS10 || x > rect.maxX + EPS10) return false;
1609
+ if (x < rect.minX - EPS11 || x > rect.maxX + EPS11) return false;
1558
1610
  const segMinY = Math.min(p1.y, p2.y);
1559
1611
  const segMaxY = Math.max(p1.y, p2.y);
1560
1612
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
1561
- return overlap > EPS10;
1613
+ return overlap > EPS11;
1562
1614
  } else {
1563
1615
  const y = p1.y;
1564
- if (y < rect.minY - EPS10 || y > rect.maxY + EPS10) return false;
1616
+ if (y < rect.minY - EPS11 || y > rect.maxY + EPS11) return false;
1565
1617
  const segMinX = Math.min(p1.x, p2.x);
1566
1618
  const segMaxX = Math.max(p1.x, p2.x);
1567
1619
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
1568
- return overlap > EPS10;
1620
+ return overlap > EPS11;
1569
1621
  }
1570
1622
  }
1571
1623
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -1949,14 +2001,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1949
2001
  };
1950
2002
  let bestCandidate = null;
1951
2003
  let bestScore = -Infinity;
1952
- const EPS10 = 1e-6;
2004
+ const EPS11 = 1e-6;
1953
2005
  for (const curr of tracesToScan) {
1954
2006
  const pts = curr.tracePath.slice();
1955
2007
  for (let si = 0; si < pts.length - 1; si++) {
1956
2008
  const a = pts[si];
1957
2009
  const b = pts[si + 1];
1958
- const isH = Math.abs(a.y - b.y) < EPS10;
1959
- const isV = Math.abs(a.x - b.x) < EPS10;
2010
+ const isH = Math.abs(a.y - b.y) < EPS11;
2011
+ const isV = Math.abs(a.x - b.x) < EPS11;
1960
2012
  if (!isH && !isV) continue;
1961
2013
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
1962
2014
  const candidateOrients = orientations.filter(
@@ -4172,8 +4224,8 @@ var findIntersectionsWithObstacles = (p1, p2, obstacles) => {
4172
4224
  };
4173
4225
 
4174
4226
  // lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts
4175
- var EPS4 = 1e-6;
4176
- var isVertical2 = (a, b, eps = EPS4) => Math.abs(a.x - b.x) < eps;
4227
+ var EPS5 = 1e-6;
4228
+ var isVertical2 = (a, b, eps = EPS5) => Math.abs(a.x - b.x) < eps;
4177
4229
  var generateLShapeRerouteCandidates = ({
4178
4230
  lShape,
4179
4231
  rectangle,
@@ -4186,7 +4238,7 @@ var generateLShapeRerouteCandidates = ({
4186
4238
  let c2;
4187
4239
  let i1_padded = interactionPoint1;
4188
4240
  let i2_padded = interactionPoint2;
4189
- if (Math.abs(p2.x - x) < EPS4 && Math.abs(p2.y - (y + height)) < EPS4) {
4241
+ if (Math.abs(p2.x - x) < EPS5 && Math.abs(p2.y - (y + height)) < EPS5) {
4190
4242
  c2 = { x: x + width + padding, y: y - padding };
4191
4243
  if (isVertical2(p1, p2)) {
4192
4244
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -4198,7 +4250,7 @@ var generateLShapeRerouteCandidates = ({
4198
4250
  } else {
4199
4251
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
4200
4252
  }
4201
- } else if (Math.abs(p2.x - (x + width)) < EPS4 && Math.abs(p2.y - (y + height)) < EPS4) {
4253
+ } else if (Math.abs(p2.x - (x + width)) < EPS5 && Math.abs(p2.y - (y + height)) < EPS5) {
4202
4254
  c2 = { x: x - padding, y: y - padding };
4203
4255
  if (isVertical2(p1, p2)) {
4204
4256
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y - padding };
@@ -4210,7 +4262,7 @@ var generateLShapeRerouteCandidates = ({
4210
4262
  } else {
4211
4263
  i2_padded = { x: interactionPoint2.x - padding, y: interactionPoint2.y };
4212
4264
  }
4213
- } else if (Math.abs(p2.x - x) < EPS4 && Math.abs(p2.y - y) < EPS4) {
4265
+ } else if (Math.abs(p2.x - x) < EPS5 && Math.abs(p2.y - y) < EPS5) {
4214
4266
  c2 = { x: x + width + padding, y: y + height + padding };
4215
4267
  if (isVertical2(p1, p2)) {
4216
4268
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -4222,7 +4274,7 @@ var generateLShapeRerouteCandidates = ({
4222
4274
  } else {
4223
4275
  i2_padded = { x: interactionPoint2.x + padding, y: interactionPoint2.y };
4224
4276
  }
4225
- } else if (Math.abs(p2.x - (x + width)) < EPS4 && Math.abs(p2.y - y) < EPS4) {
4277
+ } else if (Math.abs(p2.x - (x + width)) < EPS5 && Math.abs(p2.y - y) < EPS5) {
4226
4278
  c2 = { x: x - padding, y: y + height + padding };
4227
4279
  if (isVertical2(p1, p2)) {
4228
4280
  i1_padded = { x: interactionPoint1.x, y: interactionPoint1.y + padding };
@@ -4667,9 +4719,9 @@ var UntangleTraceSubsolver = class extends BaseSolver {
4667
4719
  };
4668
4720
 
4669
4721
  // lib/solvers/TraceCleanupSolver/is4PointRectangle.ts
4670
- var EPS5 = 1e-6;
4671
- var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS5;
4672
- var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS5;
4722
+ var EPS6 = 1e-6;
4723
+ var sameX = (a, b) => Math.abs(a.x - b.x) <= EPS6;
4724
+ var sameY = (a, b) => Math.abs(a.y - b.y) <= EPS6;
4673
4725
  var is4PointRectangle = (path) => {
4674
4726
  if (path.length !== 4) return false;
4675
4727
  const [p0, p1, p2, p3] = path;
@@ -4803,7 +4855,7 @@ var TraceCleanupSolver = class extends BaseSolver {
4803
4855
  };
4804
4856
 
4805
4857
  // lib/solvers/Example28Solver/types.ts
4806
- var EPS6 = 1e-9;
4858
+ var EPS7 = 1e-9;
4807
4859
 
4808
4860
  // lib/solvers/Example28Solver/geometry.ts
4809
4861
  var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
@@ -4815,10 +4867,10 @@ var getPathLength = (path) => {
4815
4867
  return length;
4816
4868
  };
4817
4869
  var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
4818
- var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS6 || Math.abs(start.y - end.y) < EPS6;
4819
- var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS6 ? "horizontal" : "vertical";
4870
+ var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS7 || Math.abs(start.y - end.y) < EPS7;
4871
+ var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS7 ? "horizontal" : "vertical";
4820
4872
  var projectPointToSegment = (point, start, end) => {
4821
- if (Math.abs(start.x - end.x) < EPS6) {
4873
+ if (Math.abs(start.x - end.x) < EPS7) {
4822
4874
  return {
4823
4875
  x: start.x,
4824
4876
  y: Math.min(
@@ -4827,7 +4879,7 @@ var projectPointToSegment = (point, start, end) => {
4827
4879
  )
4828
4880
  };
4829
4881
  }
4830
- if (Math.abs(start.y - end.y) < EPS6) {
4882
+ if (Math.abs(start.y - end.y) < EPS7) {
4831
4883
  return {
4832
4884
  x: Math.min(
4833
4885
  Math.max(point.x, Math.min(start.x, end.x)),
@@ -4870,9 +4922,9 @@ var findSegmentContainingPoint = (path, point) => {
4870
4922
  };
4871
4923
  var isPointWithinSegmentPrimaryRange = (point, segment) => {
4872
4924
  if (segment.orientation === "horizontal") {
4873
- return point.x >= Math.min(segment.start.x, segment.end.x) - EPS6 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS6;
4925
+ return point.x >= Math.min(segment.start.x, segment.end.x) - EPS7 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS7;
4874
4926
  }
4875
- return point.y >= Math.min(segment.start.y, segment.end.y) - EPS6 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS6;
4927
+ return point.y >= Math.min(segment.start.y, segment.end.y) - EPS7 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS7;
4876
4928
  };
4877
4929
  var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
4878
4930
  const matchingSegments = getSegments(path).filter(
@@ -4900,18 +4952,18 @@ var projectPointToPath = (point, path) => {
4900
4952
  return bestPoint;
4901
4953
  };
4902
4954
  var segmentsIntersect = (a1, a2, b1, b2) => {
4903
- const aVertical = Math.abs(a1.x - a2.x) < EPS6;
4904
- const bVertical = Math.abs(b1.x - b2.x) < EPS6;
4905
- const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS6 && value <= Math.max(p1, p2) + EPS6;
4955
+ const aVertical = Math.abs(a1.x - a2.x) < EPS7;
4956
+ const bVertical = Math.abs(b1.x - b2.x) < EPS7;
4957
+ const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS7 && value <= Math.max(p1, p2) + EPS7;
4906
4958
  if (aVertical && bVertical) {
4907
- if (Math.abs(a1.x - b1.x) > EPS6) return false;
4959
+ if (Math.abs(a1.x - b1.x) > EPS7) return false;
4908
4960
  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));
4909
- return overlap > EPS6;
4961
+ return overlap > EPS7;
4910
4962
  }
4911
4963
  if (!aVertical && !bVertical) {
4912
- if (Math.abs(a1.y - b1.y) > EPS6) return false;
4964
+ if (Math.abs(a1.y - b1.y) > EPS7) return false;
4913
4965
  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));
4914
- return overlap > EPS6;
4966
+ return overlap > EPS7;
4915
4967
  }
4916
4968
  const verticalA = aVertical ? a1 : b1;
4917
4969
  const verticalB = aVertical ? a2 : b2;
@@ -4943,13 +4995,13 @@ var isPathCollidingWithChipInterior = (path, chipObstacles) => {
4943
4995
  return false;
4944
4996
  };
4945
4997
  var segmentRunsAlongRectBoundary = (start, end, rect) => {
4946
- const isVertical4 = Math.abs(start.x - end.x) < EPS6;
4947
- const isHorizontal3 = Math.abs(start.y - end.y) < EPS6;
4998
+ const isVertical4 = Math.abs(start.x - end.x) < EPS7;
4999
+ const isHorizontal3 = Math.abs(start.y - end.y) < EPS7;
4948
5000
  if (isVertical4) {
4949
- return Math.abs(start.x - rect.minX) < EPS6 || Math.abs(start.x - rect.maxX) < EPS6;
5001
+ return Math.abs(start.x - rect.minX) < EPS7 || Math.abs(start.x - rect.maxX) < EPS7;
4950
5002
  }
4951
5003
  if (isHorizontal3) {
4952
- return Math.abs(start.y - rect.minY) < EPS6 || Math.abs(start.y - rect.maxY) < EPS6;
5004
+ return Math.abs(start.y - rect.minY) < EPS7 || Math.abs(start.y - rect.maxY) < EPS7;
4953
5005
  }
4954
5006
  return false;
4955
5007
  };
@@ -5573,16 +5625,16 @@ var Example28Solver = class extends BaseSolver {
5573
5625
  // lib/solvers/AvailableNetOrientationSolver/constants.ts
5574
5626
  var LABEL_SEARCH_STEP = 0.05;
5575
5627
  var WICK_CLEARANCE = 1e-3;
5576
- var EPS7 = 1e-9;
5577
- var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS7;
5628
+ var EPS8 = 1e-9;
5629
+ var TRACE_BOUNDARY_TOLERANCE = WICK_CLEARANCE + EPS8;
5578
5630
  var CANDIDATE_SELECTED_COLOR2 = "blue";
5579
5631
  var CANDIDATE_REJECTED_COLOR2 = "red";
5580
5632
 
5581
5633
  // lib/solvers/AvailableNetOrientationSolver/geometry.ts
5582
5634
  var isYOrientation = (orientation) => orientation === "y+" || orientation === "y-";
5583
5635
  var isXOrientation = (orientation) => orientation === "x+" || orientation === "x-";
5584
- 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;
5585
- var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS7;
5636
+ var rectsOverlap = (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;
5637
+ var rangesOverlap = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS8;
5586
5638
  var traceCrossesBoundsInterior = (bounds, traceMap) => {
5587
5639
  for (const trace of Object.values(traceMap)) {
5588
5640
  const points = trace.tracePath;
@@ -5605,7 +5657,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
5605
5657
  return false;
5606
5658
  }
5607
5659
  if (sameX2(p1, p2)) {
5608
- if (p1.x <= interiorBounds.minX + EPS7 || p1.x >= interiorBounds.maxX - EPS7) {
5660
+ if (p1.x <= interiorBounds.minX + EPS8 || p1.x >= interiorBounds.maxX - EPS8) {
5609
5661
  return false;
5610
5662
  }
5611
5663
  return rangesOverlap(
@@ -5616,7 +5668,7 @@ var segmentCrossesBoundsInterior = (p1, p2, bounds) => {
5616
5668
  );
5617
5669
  }
5618
5670
  if (sameY2(p1, p2)) {
5619
- if (p1.y <= interiorBounds.minY + EPS7 || p1.y >= interiorBounds.maxY - EPS7) {
5671
+ if (p1.y <= interiorBounds.minY + EPS8 || p1.y >= interiorBounds.maxY - EPS8) {
5620
5672
  return false;
5621
5673
  }
5622
5674
  return rangesOverlap(
@@ -5664,10 +5716,10 @@ var tracePathCrossesAnyTrace = (tracePath, traceMap) => {
5664
5716
  };
5665
5717
  var segmentsStrictlyCross = (a1, a2, b1, b2) => {
5666
5718
  if (sameX2(a1, a2) && sameY2(b1, b2)) {
5667
- 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;
5719
+ return a1.x > Math.min(b1.x, b2.x) + EPS8 && a1.x < Math.max(b1.x, b2.x) - EPS8 && b1.y > Math.min(a1.y, a2.y) + EPS8 && b1.y < Math.max(a1.y, a2.y) - EPS8;
5668
5720
  }
5669
5721
  if (sameY2(a1, a2) && sameX2(b1, b2)) {
5670
- 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;
5722
+ return b1.x > Math.min(a1.x, a2.x) + EPS8 && b1.x < Math.max(a1.x, a2.x) - EPS8 && a1.y > Math.min(b1.y, b2.y) + EPS8 && a1.y < Math.max(b1.y, b2.y) - EPS8;
5671
5723
  }
5672
5724
  return false;
5673
5725
  };
@@ -5699,8 +5751,8 @@ var simplifyOrthogonalPath = (path) => {
5699
5751
  return simplified;
5700
5752
  };
5701
5753
  var pointsEqual = (a, b) => sameX2(a, b) && sameY2(a, b);
5702
- var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS7;
5703
- var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS7;
5754
+ var sameX2 = (a, b) => Math.abs(a.x - b.x) <= EPS8;
5755
+ var sameY2 = (a, b) => Math.abs(a.y - b.y) <= EPS8;
5704
5756
  var getMaxSearchDistance = (inputProblem) => {
5705
5757
  const maxChipWidth = Math.max(
5706
5758
  ...inputProblem.chips.map((chip) => chip.width),
@@ -5988,7 +6040,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
5988
6040
  );
5989
6041
  const maxSearchDistance = this.getSearchDistanceLimit(label, orientation);
5990
6042
  const maxOutwardDistance = outwardDirection.x === 0 && outwardDirection.y === 0 ? 0 : this.maxSearchDistance;
5991
- for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS7; outwardDistance += LABEL_SEARCH_STEP) {
6043
+ for (let outwardDistance = 0; outwardDistance <= maxOutwardDistance + EPS8; outwardDistance += LABEL_SEARCH_STEP) {
5992
6044
  const baseAnchor = {
5993
6045
  x: initialBaseAnchor.x + outwardDirection.x * outwardDistance,
5994
6046
  y: initialBaseAnchor.y + outwardDirection.y * outwardDistance
@@ -6061,7 +6113,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6061
6113
  outwardDistance,
6062
6114
  phase = "shift"
6063
6115
  } = params;
6064
- for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS7; distance3 += LABEL_SEARCH_STEP) {
6116
+ for (let distance3 = LABEL_SEARCH_STEP; distance3 <= maxSearchDistance + EPS8; distance3 += LABEL_SEARCH_STEP) {
6065
6117
  const anchorPoint = {
6066
6118
  x: baseAnchor.x + direction.x * distance3,
6067
6119
  y: baseAnchor.y + direction.y * distance3
@@ -6299,8 +6351,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6299
6351
  sharesChipBoundary(bounds) {
6300
6352
  for (const chip of this.chipObstacleSpatialIndex.chips) {
6301
6353
  const chipBounds = chip.bounds;
6302
- const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS7 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS7;
6303
- const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS7 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS7;
6354
+ const adjacentToVerticalSide = Math.abs(bounds.minX - chipBounds.maxX) <= WICK_CLEARANCE + EPS8 || Math.abs(bounds.maxX - chipBounds.minX) <= WICK_CLEARANCE + EPS8;
6355
+ const adjacentToHorizontalSide = Math.abs(bounds.minY - chipBounds.maxY) <= WICK_CLEARANCE + EPS8 || Math.abs(bounds.maxY - chipBounds.minY) <= WICK_CLEARANCE + EPS8;
6304
6356
  if (adjacentToVerticalSide && rangesOverlap(
6305
6357
  bounds.minY,
6306
6358
  bounds.maxY,
@@ -6348,7 +6400,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6348
6400
  let nearestDistance = Number.POSITIVE_INFINITY;
6349
6401
  for (const chip of this.chipObstacleSpatialIndex.chips) {
6350
6402
  const bounds = chip.bounds;
6351
- if (point.x < bounds.minX - EPS7 || point.x > bounds.maxX + EPS7 || point.y < bounds.minY - EPS7 || point.y > bounds.maxY + EPS7) {
6403
+ if (point.x < bounds.minX - EPS8 || point.x > bounds.maxX + EPS8 || point.y < bounds.minY - EPS8 || point.y > bounds.maxY + EPS8) {
6352
6404
  continue;
6353
6405
  }
6354
6406
  for (const [side, distance3] of getSideDistances(point, bounds)) {
@@ -6365,8 +6417,8 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6365
6417
  let nearestDistanceSq = Number.POSITIVE_INFINITY;
6366
6418
  for (const chip of this.chipObstacleSpatialIndex.chips) {
6367
6419
  const bounds = chip.bounds;
6368
- const dx = point.x < bounds.minX - EPS7 ? bounds.minX - point.x : point.x > bounds.maxX + EPS7 ? point.x - bounds.maxX : 0;
6369
- const dy = point.y < bounds.minY - EPS7 ? bounds.minY - point.y : point.y > bounds.maxY + EPS7 ? point.y - bounds.maxY : 0;
6420
+ const dx = point.x < bounds.minX - EPS8 ? bounds.minX - point.x : point.x > bounds.maxX + EPS8 ? point.x - bounds.maxX : 0;
6421
+ const dy = point.y < bounds.minY - EPS8 ? bounds.minY - point.y : point.y > bounds.maxY + EPS8 ? point.y - bounds.maxY : 0;
6370
6422
  if (dx === 0 && dy === 0) continue;
6371
6423
  const distanceSq = dx ** 2 + dy ** 2;
6372
6424
  if (distanceSq >= nearestDistanceSq) continue;
@@ -6405,9 +6457,9 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
6405
6457
  };
6406
6458
 
6407
6459
  // lib/solvers/VccNetLabelCornerPlacementSolver/geometry.ts
6408
- var EPS8 = 1e-6;
6460
+ var EPS9 = 1e-6;
6409
6461
  var isTraceLine = (trace) => getUniquePinCount(trace.pinIds) >= 2;
6410
- 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;
6462
+ var rectsOverlap2 = (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;
6411
6463
  var getTraceCorners = (path) => {
6412
6464
  const corners = [];
6413
6465
  for (let i = 1; i < path.length - 1; i++) {
@@ -6431,11 +6483,11 @@ var getUniquePinCount = (pinIds) => new Set(pinIds).size;
6431
6483
  var isTraceCorner = (a, b, c) => getSegmentOrientation2(a, b) !== getSegmentOrientation2(b, c);
6432
6484
  var isPointOnSegment = (point, start, end) => {
6433
6485
  if (getSegmentOrientation2(start, end) === "horizontal") {
6434
- 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;
6486
+ 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;
6435
6487
  }
6436
- 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;
6488
+ 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;
6437
6489
  };
6438
- var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS8 ? "horizontal" : "vertical";
6490
+ var getSegmentOrientation2 = (a, b) => Math.abs(a.y - b.y) <= EPS9 ? "horizontal" : "vertical";
6439
6491
 
6440
6492
  // lib/solvers/VccNetLabelCornerPlacementSolver/visualize.ts
6441
6493
  var CANDIDATE_SELECTED_COLOR3 = "blue";
@@ -6713,11 +6765,11 @@ var VccNetLabelCornerPlacementSolver = class extends BaseSolver {
6713
6765
  };
6714
6766
 
6715
6767
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/geometry.ts
6716
- var EPS9 = 1e-6;
6768
+ var EPS10 = 1e-6;
6717
6769
  var TRACE_BOUNDARY_TOLERANCE2 = 1e-6;
6718
6770
  var getLabelBounds = (label) => getRectBounds(label.center, label.width, label.height);
6719
- 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;
6720
- 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;
6771
+ var rectsOverlap3 = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) > EPS10 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) > EPS10;
6772
+ var rectsTouchOrOverlap = (a, b) => Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) >= -EPS10 && Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) >= -EPS10;
6721
6773
  var traceCrossesBoundsInterior2 = (bounds, traces) => {
6722
6774
  for (const trace of traces) {
6723
6775
  const points = trace.tracePath;
@@ -6763,7 +6815,7 @@ var getPointAtTraceDistance = (trace, distance3) => {
6763
6815
  const end = trace.tracePath[i + 1];
6764
6816
  const segmentLength = getManhattanDistance(start, end);
6765
6817
  const nextDistance = pathDistance + segmentLength;
6766
- if (distance3 <= nextDistance + EPS9) {
6818
+ if (distance3 <= nextDistance + EPS10) {
6767
6819
  const offset = Math.max(
6768
6820
  0,
6769
6821
  Math.min(segmentLength, distance3 - pathDistance)
@@ -6802,7 +6854,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
6802
6854
  return false;
6803
6855
  }
6804
6856
  if (isVertical3(start, end)) {
6805
- if (start.x <= interior.minX + EPS9 || start.x >= interior.maxX - EPS9) {
6857
+ if (start.x <= interior.minX + EPS10 || start.x >= interior.maxX - EPS10) {
6806
6858
  return false;
6807
6859
  }
6808
6860
  return rangesOverlap2(
@@ -6813,7 +6865,7 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
6813
6865
  );
6814
6866
  }
6815
6867
  if (isHorizontal2(start, end)) {
6816
- if (start.y <= interior.minY + EPS9 || start.y >= interior.maxY - EPS9) {
6868
+ if (start.y <= interior.minY + EPS10 || start.y >= interior.maxY - EPS10) {
6817
6869
  return false;
6818
6870
  }
6819
6871
  return rangesOverlap2(
@@ -6827,10 +6879,10 @@ var segmentCrossesBoundsInterior2 = (start, end, bounds) => {
6827
6879
  };
6828
6880
  var isPointOnSegment2 = (point, start, end) => {
6829
6881
  if (isHorizontal2(start, end)) {
6830
- 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;
6882
+ return Math.abs(point.y - start.y) <= EPS10 && point.x >= Math.min(start.x, end.x) - EPS10 && point.x <= Math.max(start.x, end.x) + EPS10;
6831
6883
  }
6832
6884
  if (isVertical3(start, end)) {
6833
- 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;
6885
+ return Math.abs(point.x - start.x) <= EPS10 && point.y >= Math.min(start.y, end.y) - EPS10 && point.y <= Math.max(start.y, end.y) + EPS10;
6834
6886
  }
6835
6887
  return false;
6836
6888
  };
@@ -6838,9 +6890,9 @@ var getSegmentDirection = (start, end) => ({
6838
6890
  x: isVertical3(start, end) ? 0 : Math.sign(end.x - start.x),
6839
6891
  y: isHorizontal2(start, end) ? 0 : Math.sign(end.y - start.y)
6840
6892
  });
6841
- var isHorizontal2 = (start, end) => Math.abs(start.y - end.y) <= EPS9;
6842
- var isVertical3 = (start, end) => Math.abs(start.x - end.x) <= EPS9;
6843
- var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS9;
6893
+ var isHorizontal2 = (start, end) => Math.abs(start.y - end.y) <= EPS10;
6894
+ var isVertical3 = (start, end) => Math.abs(start.x - end.x) <= EPS10;
6895
+ var rangesOverlap2 = (minA, maxA, minB, maxB) => Math.min(maxA, maxB) - Math.max(minA, minB) > EPS10;
6844
6896
  var getUniquePinCount2 = (pinIds) => new Set(pinIds).size;
6845
6897
 
6846
6898
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts
@@ -6897,7 +6949,7 @@ var getCandidateDistances = (traceLength, vertexDistances) => {
6897
6949
  for (const distance3 of vertexDistances) {
6898
6950
  distances.add(roundDistance(distance3));
6899
6951
  }
6900
- return [...distances].filter((distance3) => distance3 >= -EPS9 && distance3 <= traceLength + EPS9).sort((a, b) => a - b);
6952
+ return [...distances].filter((distance3) => distance3 >= -EPS10 && distance3 <= traceLength + EPS10).sort((a, b) => a - b);
6901
6953
  };
6902
6954
  var getOrientationsForPoint = (params) => {
6903
6955
  const { inputProblem, label, point, orientationConstraint } = params;
@@ -7006,13 +7058,13 @@ var getOutwardOrientationOptions = (point, chipBounds) => {
7006
7058
  };
7007
7059
  var getOutwardAxisOption = (params) => {
7008
7060
  const { value, min, max, center, positiveOrientation, negativeOrientation } = params;
7009
- if (value > max + EPS9) {
7061
+ if (value > max + EPS10) {
7010
7062
  return {
7011
7063
  orientation: positiveOrientation,
7012
7064
  outwardScore: 1 + value - max
7013
7065
  };
7014
7066
  }
7015
- if (value < min - EPS9) {
7067
+ if (value < min - EPS10) {
7016
7068
  return {
7017
7069
  orientation: negativeOrientation,
7018
7070
  outwardScore: 1 + min - value
@@ -7023,7 +7075,7 @@ var getOutwardAxisOption = (params) => {
7023
7075
  outwardScore: getNormalizedCenterDistance(value, center, max - min)
7024
7076
  };
7025
7077
  };
7026
- var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS9, span / 2);
7078
+ var getNormalizedCenterDistance = (value, center, span) => Math.abs(value - center) / Math.max(EPS10, span / 2);
7027
7079
  var ALL_ORIENTATIONS = ["x+", "x-", "y+", "y-"];
7028
7080
  var getFlippedOrientation = (orientation) => {
7029
7081
  switch (orientation) {
@@ -7118,7 +7170,7 @@ var normalizeFacingDirection = (value) => {
7118
7170
  var dedupeStrings = (values) => [
7119
7171
  ...new Set(values.filter((value) => value !== void 0))
7120
7172
  ];
7121
- var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS9 && Math.abs(point.y - label.anchorPoint.y) <= EPS9 && orientation === label.orientation;
7173
+ var isSamePlacement = (label, point, orientation) => Math.abs(point.x - label.anchorPoint.x) <= EPS10 && Math.abs(point.y - label.anchorPoint.y) <= EPS10 && orientation === label.orientation;
7122
7174
 
7123
7175
  // lib/solvers/TraceAnchoredNetLabelOverlapSolver/visualize.ts
7124
7176
  var CANDIDATE_SELECTED_COLOR4 = "blue";
@@ -1,11 +1,16 @@
1
1
  import type { GraphicsObject } from "graphics-debug"
2
2
  import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
3
3
  import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
4
+ import { findFirstCollision } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
5
+ import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
4
6
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
7
+ import type { InputProblem } from "lib/types/InputProblem"
5
8
  import { applyJogToTerminalSegment } from "./applyJogToTrace"
6
9
 
7
10
  type ConnNetId = string
8
11
 
12
+ const EPS = 1e-6
13
+
9
14
  export interface OverlappingTraceSegmentLocator {
10
15
  connNetId: string
11
16
  pathsWithOverlap: Array<{
@@ -17,18 +22,21 @@ export interface OverlappingTraceSegmentLocator {
17
22
  export class TraceOverlapIssueSolver extends BaseSolver {
18
23
  overlappingTraceSegments: OverlappingTraceSegmentLocator[]
19
24
  traceNetIslands: Record<ConnNetId, Array<SolvedTracePath>>
25
+ obstacleRects: ReturnType<typeof getObstacleRects>
20
26
 
21
27
  SHIFT_DISTANCE = 0.1
22
28
 
23
29
  correctedTraceMap: Record<MspConnectionPairId, SolvedTracePath> = {}
24
30
 
25
31
  constructor(params: {
32
+ inputProblem: InputProblem
26
33
  overlappingTraceSegments: OverlappingTraceSegmentLocator[]
27
34
  traceNetIslands: Record<ConnNetId, Array<SolvedTracePath>>
28
35
  }) {
29
36
  super()
30
37
  this.overlappingTraceSegments = params.overlappingTraceSegments
31
38
  this.traceNetIslands = params.traceNetIslands
39
+ this.obstacleRects = getObstacleRects(params.inputProblem)
32
40
 
33
41
  // Only add the relevant traces to the correctedTraceMap
34
42
  for (const { connNetId, pathsWithOverlap } of this
@@ -49,13 +57,14 @@ export class TraceOverlapIssueSolver extends BaseSolver {
49
57
  // Shift only the overlapping segments, and move the shared endpoints
50
58
  // (the last point of the previous segment and the first point of the next
51
59
  // segment) so the polyline remains orthogonal without self-overlap.
52
- const EPS = 1e-6
53
-
54
60
  // Compute offsets for each island involved: alternate directions
55
- const offsets = this.overlappingTraceSegments.map((_, idx) => {
61
+ const offsets = this.overlappingTraceSegments.map((group, idx) => {
56
62
  const n = Math.floor(idx / 2) + 1
57
63
  const signed = idx % 2 === 0 ? -n : n
58
- return signed * this.SHIFT_DISTANCE
64
+ return this.getObstacleAwareOffset({
65
+ group,
66
+ offset: signed * this.SHIFT_DISTANCE,
67
+ })
59
68
  })
60
69
 
61
70
  const eq = (a: number, b: number) => Math.abs(a - b) < EPS
@@ -82,8 +91,6 @@ export class TraceOverlapIssueSolver extends BaseSolver {
82
91
  const current = this.correctedTraceMap[original.mspPairId] ?? original
83
92
  const pts = current.tracePath.map((p) => ({ ...p }))
84
93
 
85
- const segIdxs = Array.from(segIdxSet).sort((a, b) => a - b)
86
-
87
94
  const segIdxsRev = Array.from(segIdxSet)
88
95
  .sort((a, b) => a - b)
89
96
  .reverse()
@@ -142,6 +149,82 @@ export class TraceOverlapIssueSolver extends BaseSolver {
142
149
  this.solved = true
143
150
  }
144
151
 
152
+ private getObstacleAwareOffset({
153
+ group,
154
+ offset,
155
+ }: {
156
+ group: OverlappingTraceSegmentLocator
157
+ offset: number
158
+ }) {
159
+ const blockingCollisions = this.getBlockingCollisions({ group, offset })
160
+ if (blockingCollisions.length === 0) return offset
161
+
162
+ return (
163
+ blockingCollisions
164
+ .flatMap(({ start, isVertical, obstacle }) => [
165
+ (isVertical ? obstacle.minX - start.x : obstacle.minY - start.y) -
166
+ this.SHIFT_DISTANCE,
167
+ (isVertical ? obstacle.maxX - start.x : obstacle.maxY - start.y) +
168
+ this.SHIFT_DISTANCE,
169
+ ])
170
+ .filter(
171
+ (candidateOffset) =>
172
+ this.getBlockingCollisions({ group, offset: candidateOffset })
173
+ .length === 0,
174
+ )
175
+ .sort((a, b) => Math.abs(a - offset) - Math.abs(b - offset))[0] ??
176
+ offset
177
+ )
178
+ }
179
+
180
+ private getBlockingCollisions({
181
+ group,
182
+ offset,
183
+ }: {
184
+ group: OverlappingTraceSegmentLocator
185
+ offset: number
186
+ }) {
187
+ const blockingCollisions: Array<{
188
+ start: SolvedTracePath["tracePath"][number]
189
+ isVertical: boolean
190
+ obstacle: ReturnType<typeof getObstacleRects>[number]
191
+ }> = []
192
+
193
+ for (const {
194
+ solvedTracePathIndex,
195
+ traceSegmentIndex,
196
+ } of group.pathsWithOverlap) {
197
+ const trace = this.traceNetIslands[group.connNetId][solvedTracePathIndex]!
198
+ const start = trace.tracePath[traceSegmentIndex]
199
+ const end = trace.tracePath[traceSegmentIndex + 1]
200
+ if (!start || !end) continue
201
+
202
+ const isVertical = Math.abs(start.x - end.x) < EPS
203
+ const isHorizontal = Math.abs(start.y - end.y) < EPS
204
+ if (!isVertical && !isHorizontal) continue
205
+
206
+ const shiftedSegment = isVertical
207
+ ? [
208
+ { ...start, x: start.x + offset },
209
+ { ...end, x: end.x + offset },
210
+ ]
211
+ : [
212
+ { ...start, y: start.y + offset },
213
+ { ...end, y: end.y + offset },
214
+ ]
215
+ const collision = findFirstCollision(shiftedSegment, this.obstacleRects)
216
+ if (collision) {
217
+ blockingCollisions.push({
218
+ start,
219
+ isVertical,
220
+ obstacle: collision.rect,
221
+ })
222
+ }
223
+ }
224
+
225
+ return blockingCollisions
226
+ }
227
+
145
228
  override visualize(): GraphicsObject {
146
229
  // Visualize overlapped segments and proposed corrections
147
230
  const graphics: GraphicsObject = {
@@ -309,6 +309,7 @@ export class TraceOverlapShiftSolver extends BaseSolver {
309
309
  const { overlappingTraceSegments } = overlapIssue
310
310
 
311
311
  this.activeSubSolver = new TraceOverlapIssueSolver({
312
+ inputProblem: this.inputProblem,
312
313
  overlappingTraceSegments,
313
314
  traceNetIslands: this.traceNetIslands,
314
315
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.64",
4
+ "version": "0.0.65",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -18,7 +18,7 @@ y-" data-x="2.25" data-y="-2.6500000000000004" cx="500.642827694859" cy="536.849
18
18
  </g>
19
19
  <g>
20
20
  <circle data-type="point" data-label="anchorPoint
21
- orientation: y-" data-x="2.125" data-y="-2.8500000000000005" cx="489.034204145937" cy="555.4228855721392" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
21
+ orientation: y-" data-x="1.9902723250000003" data-y="-2.8500000000000005" cx="476.52218126036485" cy="555.4228855721392" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
22
22
  </g>
23
23
  <g>
24
24
  <circle data-type="point" data-label="anchorPoint
@@ -34,7 +34,7 @@ orientation: y+" data-x="1.9249999999999998" data-y="-0.32499999999999996" cx="4
34
34
  <polyline data-points="1.7999999999999998,-0.32499999999999996 1.9249999999999998,-0.32499999999999996 1.9249999999999998,-1.1 2.25,-1.1 2.25,-0.9" data-type="line" data-label="" points="458.8517829187396,320.9286898839137 470.46040646766164,320.9286898839137 470.46040646766164,392.9021558872305 500.642827694859,392.9021558872305 500.642827694859,374.3283582089552" fill="none" stroke="purple" stroke-width="1" />
35
35
  </g>
36
36
  <g>
37
- <polyline data-points="1.7999999999999998,-0.975 2.125,-0.975 2.125,-2.8500000000000005 2.25,-2.8500000000000005 2.25,-2.6500000000000004" data-type="line" data-label="" points="458.8517829187396,381.2935323383084 489.034204145937,381.2935323383084 489.034204145937,555.4228855721392 500.642827694859,555.4228855721392 500.642827694859,536.849087893864" fill="none" stroke="purple" stroke-width="1" />
37
+ <polyline data-points="1.7999999999999998,-0.975 1.9902723250000003,-0.975 1.9902723250000003,-2.8500000000000005 2.25,-2.8500000000000005 2.25,-2.6500000000000004" data-type="line" data-label="" points="458.8517829187396,381.2935323383084 476.52218126036485,381.2935323383084 476.52218126036485,555.4228855721392 500.642827694859,555.4228855721392 500.642827694859,536.849087893864" fill="none" stroke="purple" stroke-width="1" />
38
38
  </g>
39
39
  <g>
40
40
  <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="124.52342470978448" y="40" width="334.32835820895514" height="501.49253731343276" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.010767857142857145" />
@@ -47,7 +47,7 @@ orientation: y+" data-x="1.9249999999999998" data-y="-0.32499999999999996" cx="4
47
47
  </g>
48
48
  <g>
49
49
  <rect data-type="rect" data-label="netId: GND
50
- globalConnNetId: connectivity_net1" data-x="2.125" data-y="-3.0900000000000007" x="479.7473053067993" y="555.4228855721392" width="18.573797678275298" height="44.577114427860806" fill="#00000066" stroke="#000000" stroke-width="0.010767857142857145" />
50
+ globalConnNetId: connectivity_net1" data-x="1.9902723250000003" data-y="-3.0900000000000007" x="467.2352824212272" y="555.4228855721392" width="18.573797678275298" height="44.577114427860806" fill="#00000066" stroke="#000000" stroke-width="0.010767857142857145" />
51
51
  </g>
52
52
  <g>
53
53
  <rect data-type="rect" data-label="netId: U1.FB to R1.pin2