@tscircuit/schematic-trace-solver 0.0.47 → 0.0.49

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 (164) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +51 -1
  3. package/dist/index.js +750 -49
  4. package/lib/solvers/Example28Solver/Example28Solver.ts +182 -0
  5. package/lib/solvers/Example28Solver/geometry.ts +246 -0
  6. package/lib/solvers/Example28Solver/labelMovement.ts +82 -0
  7. package/lib/solvers/Example28Solver/reroute.ts +368 -0
  8. package/lib/solvers/Example28Solver/types.ts +47 -0
  9. package/lib/solvers/Example28Solver/visualize.ts +96 -0
  10. package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +2 -0
  11. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts +12 -0
  12. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +16 -0
  13. package/package.json +1 -1
  14. package/site/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.page.tsx +2 -0
  15. package/site/TraceCleanupSolver/TraceCleanupSolver.page.tsx +23 -0
  16. package/site/TraceLabelOverlapAvoidanceSolver/MergedNetLabelObstacles.page.tsx +18 -0
  17. package/site/TraceLabelOverlapAvoidanceSolver/OverlapAvoidanceStepSolver.page.tsx +24 -0
  18. package/site/TraceLabelOverlapAvoidanceSolver/SingleOverlapSolver.page.tsx +10 -0
  19. package/site/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.page.tsx +146 -0
  20. package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView01.page.tsx +32 -0
  21. package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView02.page.tsx +32 -0
  22. package/site/TraceLabelOverlapAvoidanceSolver/renderComparisonView03.page.tsx +35 -0
  23. package/site/examples/example01.page.tsx +6 -0
  24. package/site/examples/example02.page.tsx +4 -180
  25. package/site/examples/example03.page.tsx +3 -212
  26. package/site/examples/example04.page.tsx +6 -0
  27. package/site/examples/example05.page.tsx +3 -179
  28. package/site/examples/example06.page.tsx +3 -57
  29. package/site/examples/example07.page.tsx +3 -132
  30. package/site/examples/example08.page.tsx +3 -91
  31. package/site/examples/example09.page.tsx +3 -414
  32. package/site/examples/example10.page.tsx +3 -75
  33. package/site/examples/example11.page.tsx +3 -116
  34. package/site/examples/example12.page.tsx +3 -94
  35. package/site/examples/example13.page.tsx +3 -216
  36. package/site/examples/example14.page.tsx +3 -207
  37. package/site/examples/example15.page.tsx +6 -0
  38. package/site/examples/example16.page.tsx +6 -0
  39. package/site/examples/example17.page.tsx +6 -0
  40. package/site/examples/example18.page.tsx +3 -178
  41. package/site/examples/example19.page.tsx +3 -166
  42. package/site/examples/example20.page.tsx +3 -100
  43. package/site/examples/example21.page.tsx +3 -174
  44. package/site/examples/example22.page.tsx +3 -105
  45. package/site/examples/example23.page.tsx +3 -134
  46. package/site/examples/example24.page.tsx +3 -125
  47. package/site/examples/example25.page.tsx +2 -0
  48. package/site/examples/example26.page.tsx +2 -0
  49. package/site/examples/example27.page.tsx +2 -0
  50. package/site/examples/example28.page.tsx +6 -0
  51. package/site/examples/example29.page.tsx +6 -0
  52. package/site/examples/example30.page.tsx +6 -0
  53. package/site/examples/example31.page.tsx +4 -0
  54. package/tests/assets/example01.json +109 -0
  55. package/tests/assets/example02.json +178 -0
  56. package/tests/assets/example03.json +210 -0
  57. package/tests/assets/example04.json +41 -0
  58. package/tests/assets/example05.json +175 -0
  59. package/tests/assets/example06.json +55 -0
  60. package/tests/assets/example07.json +130 -0
  61. package/tests/assets/example08.json +89 -0
  62. package/tests/assets/example09.json +412 -0
  63. package/tests/assets/example10.json +73 -0
  64. package/tests/assets/example11.json +114 -0
  65. package/tests/assets/example12.json +92 -0
  66. package/tests/assets/example13.json +214 -0
  67. package/tests/assets/example14.json +205 -0
  68. package/tests/assets/example15.json +618 -0
  69. package/tests/assets/example16.json +102 -0
  70. package/tests/assets/example17.json +160 -0
  71. package/tests/assets/example18.json +176 -0
  72. package/tests/assets/example19.json +164 -0
  73. package/tests/assets/example20.json +98 -0
  74. package/tests/assets/example21.json +172 -0
  75. package/tests/assets/example22.json +103 -0
  76. package/tests/assets/example23.json +132 -0
  77. package/tests/assets/example24.json +123 -0
  78. package/tests/assets/example25.json +146 -33
  79. package/tests/assets/example26.json +51 -1134
  80. package/tests/assets/example27.json +151 -91
  81. package/tests/assets/example28.json +56 -0
  82. package/tests/assets/example29.json +1206 -0
  83. package/tests/assets/example30.json +168 -0
  84. package/tests/assets/example31.json +46 -0
  85. package/tests/examples/__snapshots__/example01.snap.svg +12 -6
  86. package/tests/examples/__snapshots__/example02.snap.svg +16 -8
  87. package/tests/examples/__snapshots__/example03.snap.svg +36 -18
  88. package/tests/examples/__snapshots__/example04.snap.svg +8 -4
  89. package/tests/examples/__snapshots__/example05.snap.svg +4 -2
  90. package/tests/examples/__snapshots__/example06.snap.svg +4 -2
  91. package/tests/examples/__snapshots__/example07.snap.svg +20 -10
  92. package/tests/examples/__snapshots__/example08.snap.svg +16 -8
  93. package/tests/examples/__snapshots__/example09.snap.svg +80 -40
  94. package/tests/examples/__snapshots__/example10.snap.svg +16 -8
  95. package/tests/examples/__snapshots__/example11.snap.svg +24 -12
  96. package/tests/examples/__snapshots__/example12.snap.svg +16 -8
  97. package/tests/examples/__snapshots__/example13.snap.svg +32 -16
  98. package/tests/examples/__snapshots__/example14.snap.svg +115 -48
  99. package/tests/examples/__snapshots__/example15.snap.svg +36 -18
  100. package/tests/examples/__snapshots__/example16.snap.svg +16 -8
  101. package/tests/examples/__snapshots__/example17.snap.svg +16 -8
  102. package/tests/examples/__snapshots__/example18.snap.svg +20 -10
  103. package/tests/examples/__snapshots__/example19.snap.svg +16 -8
  104. package/tests/examples/__snapshots__/example20.snap.svg +43 -135
  105. package/tests/examples/__snapshots__/example21.snap.svg +34 -18
  106. package/tests/examples/__snapshots__/example22.snap.svg +37 -23
  107. package/tests/examples/__snapshots__/example23.snap.svg +62 -26
  108. package/tests/examples/__snapshots__/example24.snap.svg +50 -28
  109. package/tests/examples/__snapshots__/example25.snap.svg +175 -49
  110. package/tests/examples/__snapshots__/example26.snap.svg +16 -8
  111. package/tests/examples/__snapshots__/example27.snap.svg +24 -12
  112. package/tests/examples/__snapshots__/example28.snap.svg +29 -189
  113. package/tests/examples/__snapshots__/example29.snap.svg +184 -92
  114. package/tests/examples/__snapshots__/example30.snap.svg +48 -24
  115. package/tests/examples/__snapshots__/example31.snap.svg +88 -0
  116. package/tests/examples/example01.test.ts +2 -2
  117. package/tests/examples/example02.test.ts +2 -2
  118. package/tests/examples/example03.test.ts +2 -2
  119. package/tests/examples/example04.test.ts +2 -2
  120. package/tests/examples/example05.test.ts +2 -2
  121. package/tests/examples/example06.test.ts +2 -2
  122. package/tests/examples/example07.test.ts +2 -2
  123. package/tests/examples/example08.test.ts +2 -2
  124. package/tests/examples/example09.test.ts +2 -2
  125. package/tests/examples/example10.test.ts +2 -2
  126. package/tests/examples/{example11.test.tsx → example11.test.ts} +2 -2
  127. package/tests/examples/{example12.test.tsx → example12.test.ts} +2 -2
  128. package/tests/examples/{example13.test.tsx → example13.test.ts} +2 -2
  129. package/tests/examples/example14.test.ts +12 -0
  130. package/tests/examples/example15.test.ts +12 -0
  131. package/tests/examples/example16.test.ts +12 -0
  132. package/tests/examples/example17.test.ts +12 -0
  133. package/tests/examples/example18.test.ts +12 -0
  134. package/tests/examples/example19.test.ts +12 -0
  135. package/tests/examples/example20.test.ts +12 -0
  136. package/tests/examples/example21.test.ts +12 -0
  137. package/tests/examples/example22.test.ts +12 -0
  138. package/tests/examples/example23.test.ts +12 -0
  139. package/tests/examples/example24.test.ts +12 -0
  140. package/tests/examples/example25.test.ts +12 -0
  141. package/tests/examples/example26.test.ts +12 -0
  142. package/tests/examples/example27.test.ts +3 -232
  143. package/tests/examples/example28.test.ts +1 -2
  144. package/tests/examples/example29.test.ts +1 -2
  145. package/tests/examples/example30.test.ts +2 -2
  146. package/tests/examples/example31.test.ts +12 -0
  147. package/site/examples/example01-basic.page.tsx +0 -105
  148. package/site/examples/example04-single-symbol.page.tsx +0 -46
  149. package/site/examples/example15-rp2040-caps.page.tsx +0 -623
  150. package/site/examples/example16-core-repro51.page.tsx +0 -107
  151. package/site/examples/example17-straight-line-trace.page.tsx +0 -165
  152. package/tests/examples/example14.test.tsx +0 -11
  153. package/tests/examples/example15.test.tsx +0 -629
  154. package/tests/examples/example16.test.tsx +0 -113
  155. package/tests/examples/example17.test.tsx +0 -171
  156. package/tests/examples/example18.test.tsx +0 -187
  157. package/tests/examples/example19.test.tsx +0 -175
  158. package/tests/examples/example20.test.tsx +0 -190
  159. package/tests/examples/example21.test.tsx +0 -183
  160. package/tests/examples/example22.test.tsx +0 -109
  161. package/tests/examples/example23.test.tsx +0 -109
  162. package/tests/examples/example24.test.tsx +0 -114
  163. package/tests/examples/example25.test.tsx +0 -143
  164. package/tests/examples/example26.test.tsx +0 -134
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 EPS5 = 1e-9;
274
+ const EPS6 = 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) < -EPS5) return true;
278
+ if ((a.x - rcl.x) * (b.x - rcl.x) < -EPS6) 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) < -EPS5) return true;
281
+ if ((a.y - rcl.y) * (b.y - rcl.y) < -EPS6) return true;
282
282
  }
283
283
  }
284
284
  return false;
285
285
  };
286
- if (Math.abs(p1.x - p2.x) < EPS5 || Math.abs(p1.y - p2.y) < EPS5) {
286
+ if (Math.abs(p1.x - p2.x) < EPS6 || Math.abs(p1.y - p2.y) < EPS6) {
287
287
  return crossesSegment({ x: p1.x, y: p1.y }, { x: p2.x, y: p2.y });
288
288
  }
289
289
  const elbowHV = { x: p2.x, y: p1.y };
@@ -810,8 +810,8 @@ var SchematicTraceSingleLineSolver2 = class extends BaseSolver {
810
810
  if (!collision) {
811
811
  const first = path[0];
812
812
  const last = path[path.length - 1];
813
- const EPS5 = 1e-9;
814
- const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS5 && Math.abs(p.y - q.y) < EPS5;
813
+ const EPS6 = 1e-9;
814
+ const samePoint = (p, q) => Math.abs(p.x - q.x) < EPS6 && Math.abs(p.y - q.y) < EPS6;
815
815
  if (samePoint(first, { x: PA.x, y: PA.y }) && samePoint(last, { x: PB.x, y: PB.y })) {
816
816
  this.solvedTracePath = path;
817
817
  this.solved = true;
@@ -1010,13 +1010,13 @@ var applyJogToTerminalSegment = ({
1010
1010
  segmentIndex: si,
1011
1011
  offset,
1012
1012
  JOG_SIZE,
1013
- EPS: EPS5 = 1e-6
1013
+ EPS: EPS6 = 1e-6
1014
1014
  }) => {
1015
1015
  if (si !== 0 && si !== pts.length - 2) return;
1016
1016
  const start = pts[si];
1017
1017
  const end = pts[si + 1];
1018
- const isVertical3 = Math.abs(start.x - end.x) < EPS5;
1019
- const isHorizontal2 = Math.abs(start.y - end.y) < EPS5;
1018
+ const isVertical3 = Math.abs(start.x - end.x) < EPS6;
1019
+ const isHorizontal2 = Math.abs(start.y - end.y) < EPS6;
1020
1020
  if (!isVertical3 && !isHorizontal2) return;
1021
1021
  const segDir = isVertical3 ? end.y > start.y ? 1 : -1 : end.x > start.x ? 1 : -1;
1022
1022
  if (si === 0) {
@@ -1083,13 +1083,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1083
1083
  }
1084
1084
  }
1085
1085
  _step() {
1086
- const EPS5 = 1e-6;
1086
+ const EPS6 = 1e-6;
1087
1087
  const offsets = this.overlappingTraceSegments.map((_, idx) => {
1088
1088
  const n = Math.floor(idx / 2) + 1;
1089
1089
  const signed = idx % 2 === 0 ? -n : n;
1090
1090
  return signed * this.SHIFT_DISTANCE;
1091
1091
  });
1092
- const eq = (a, b) => Math.abs(a - b) < EPS5;
1092
+ const eq = (a, b) => Math.abs(a - b) < EPS6;
1093
1093
  const samePoint = (p, q) => !!p && !!q && eq(p.x, q.x) && eq(p.y, q.y);
1094
1094
  this.overlappingTraceSegments.forEach((group, gidx) => {
1095
1095
  const offset = offsets[gidx];
@@ -1115,13 +1115,13 @@ var TraceOverlapIssueSolver = class extends BaseSolver {
1115
1115
  segmentIndex: si,
1116
1116
  offset,
1117
1117
  JOG_SIZE,
1118
- EPS: EPS5
1118
+ EPS: EPS6
1119
1119
  });
1120
1120
  } else {
1121
1121
  const start = pts[si];
1122
1122
  const end = pts[si + 1];
1123
- const isVertical3 = Math.abs(start.x - end.x) < EPS5;
1124
- const isHorizontal2 = Math.abs(start.y - end.y) < EPS5;
1123
+ const isVertical3 = Math.abs(start.x - end.x) < EPS6;
1124
+ const isHorizontal2 = Math.abs(start.y - end.y) < EPS6;
1125
1125
  if (!isVertical3 && !isHorizontal2) continue;
1126
1126
  if (isVertical3) {
1127
1127
  start.x += offset;
@@ -1218,7 +1218,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1218
1218
  return islands;
1219
1219
  }
1220
1220
  findNextOverlapIssue() {
1221
- const EPS5 = 2e-3;
1221
+ const EPS6 = 2e-3;
1222
1222
  const netIds = Object.keys(this.traceNetIslands);
1223
1223
  for (let i = 0; i < netIds.length; i++) {
1224
1224
  for (let j = i + 1; j < netIds.length; j++) {
@@ -1236,7 +1236,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1236
1236
  const minB = Math.min(b1, b2);
1237
1237
  const maxB = Math.max(b1, b2);
1238
1238
  const overlap = Math.min(maxA, maxB) - Math.max(minA, minB);
1239
- return overlap > EPS5;
1239
+ return overlap > EPS6;
1240
1240
  };
1241
1241
  for (let pa = 0; pa < pathsA.length; pa++) {
1242
1242
  const pathA = pathsA[pa];
@@ -1244,8 +1244,8 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1244
1244
  for (let sa = 0; sa < ptsA.length - 1; sa++) {
1245
1245
  const a1 = ptsA[sa];
1246
1246
  const a2 = ptsA[sa + 1];
1247
- const aVert = Math.abs(a1.x - a2.x) < EPS5;
1248
- const aHorz = Math.abs(a1.y - a2.y) < EPS5;
1247
+ const aVert = Math.abs(a1.x - a2.x) < EPS6;
1248
+ const aHorz = Math.abs(a1.y - a2.y) < EPS6;
1249
1249
  if (!aVert && !aHorz) continue;
1250
1250
  for (let pb = 0; pb < pathsB.length; pb++) {
1251
1251
  const pathB = pathsB[pb];
@@ -1253,11 +1253,11 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1253
1253
  for (let sb = 0; sb < ptsB.length - 1; sb++) {
1254
1254
  const b1 = ptsB[sb];
1255
1255
  const b2 = ptsB[sb + 1];
1256
- const bVert = Math.abs(b1.x - b2.x) < EPS5;
1257
- const bHorz = Math.abs(b1.y - b2.y) < EPS5;
1256
+ const bVert = Math.abs(b1.x - b2.x) < EPS6;
1257
+ const bHorz = Math.abs(b1.y - b2.y) < EPS6;
1258
1258
  if (!bVert && !bHorz) continue;
1259
1259
  if (aVert && bVert) {
1260
- if (Math.abs(a1.x - b1.x) < EPS5) {
1260
+ if (Math.abs(a1.x - b1.x) < EPS6) {
1261
1261
  if (overlaps1D(a1.y, a2.y, b1.y, b2.y)) {
1262
1262
  const keyA = `${pa}:${sa}`;
1263
1263
  const keyB = `${pb}:${sb}`;
@@ -1278,7 +1278,7 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1278
1278
  }
1279
1279
  }
1280
1280
  } else if (aHorz && bHorz) {
1281
- if (Math.abs(a1.y - b1.y) < EPS5) {
1281
+ if (Math.abs(a1.y - b1.y) < EPS6) {
1282
1282
  if (overlaps1D(a1.x, a2.x, b1.x, b2.x)) {
1283
1283
  const keyA = `${pa}:${sa}`;
1284
1284
  const keyB = `${pb}:${sb}`;
@@ -1316,14 +1316,14 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1316
1316
  return null;
1317
1317
  }
1318
1318
  findNextDiagonalSegment() {
1319
- const EPS5 = 2e-3;
1319
+ const EPS6 = 2e-3;
1320
1320
  for (const mspPairId in this.correctedTraceMap) {
1321
1321
  const tracePath = this.correctedTraceMap[mspPairId].tracePath;
1322
1322
  for (let i = 0; i < tracePath.length - 1; i++) {
1323
1323
  const p1 = tracePath[i];
1324
1324
  const p2 = tracePath[i + 1];
1325
- const isHorizontal2 = Math.abs(p1.y - p2.y) < EPS5;
1326
- const isVertical3 = Math.abs(p1.x - p2.x) < EPS5;
1325
+ const isHorizontal2 = Math.abs(p1.y - p2.y) < EPS6;
1326
+ const isVertical3 = Math.abs(p1.x - p2.x) < EPS6;
1327
1327
  if (!isHorizontal2 && !isVertical3) {
1328
1328
  return { mspPairId, tracePath, i, p1, p2 };
1329
1329
  }
@@ -1337,13 +1337,13 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
1337
1337
  return false;
1338
1338
  }
1339
1339
  const { mspPairId, tracePath, i, p1, p2 } = diagonalInfo;
1340
- const EPS5 = 2e-3;
1340
+ const EPS6 = 2e-3;
1341
1341
  const p0 = i > 0 ? tracePath[i - 1] : null;
1342
1342
  const p3 = i + 2 < tracePath.length ? tracePath[i + 2] : null;
1343
- const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS5 : false;
1344
- const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS5 : false;
1345
- const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS5 : false;
1346
- const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS5 : false;
1343
+ const prevIsVertical = p0 ? Math.abs(p0.x - p1.x) < EPS6 : false;
1344
+ const prevIsHorizontal = p0 ? Math.abs(p0.y - p1.y) < EPS6 : false;
1345
+ const nextIsVertical = p3 ? Math.abs(p2.x - p3.x) < EPS6 : false;
1346
+ const nextIsHorizontal = p3 ? Math.abs(p2.y - p3.y) < EPS6 : false;
1347
1347
  const elbow1 = { x: p1.x, y: p2.y };
1348
1348
  const elbow2 = { x: p2.x, y: p1.y };
1349
1349
  let score1 = 0;
@@ -1507,24 +1507,24 @@ function getRectBounds(center, w, h) {
1507
1507
  }
1508
1508
 
1509
1509
  // lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
1510
- function segmentIntersectsRect2(p1, p2, rect, EPS5 = 1e-9) {
1511
- const isVert = Math.abs(p1.x - p2.x) < EPS5;
1512
- const isHorz = Math.abs(p1.y - p2.y) < EPS5;
1510
+ function segmentIntersectsRect2(p1, p2, rect, EPS6 = 1e-9) {
1511
+ const isVert = Math.abs(p1.x - p2.x) < EPS6;
1512
+ const isHorz = Math.abs(p1.y - p2.y) < EPS6;
1513
1513
  if (!isVert && !isHorz) return false;
1514
1514
  if (isVert) {
1515
1515
  const x = p1.x;
1516
- if (x < rect.minX - EPS5 || x > rect.maxX + EPS5) return false;
1516
+ if (x < rect.minX - EPS6 || x > rect.maxX + EPS6) return false;
1517
1517
  const segMinY = Math.min(p1.y, p2.y);
1518
1518
  const segMaxY = Math.max(p1.y, p2.y);
1519
1519
  const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
1520
- return overlap > EPS5;
1520
+ return overlap > EPS6;
1521
1521
  } else {
1522
1522
  const y = p1.y;
1523
- if (y < rect.minY - EPS5 || y > rect.maxY + EPS5) return false;
1523
+ if (y < rect.minY - EPS6 || y > rect.maxY + EPS6) return false;
1524
1524
  const segMinX = Math.min(p1.x, p2.x);
1525
1525
  const segMaxX = Math.max(p1.x, p2.x);
1526
1526
  const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
1527
- return overlap > EPS5;
1527
+ return overlap > EPS6;
1528
1528
  }
1529
1529
  }
1530
1530
  function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
@@ -1767,6 +1767,7 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
1767
1767
  for (const c of solver.testedCandidates) {
1768
1768
  const fill = c.status === "ok" ? "rgba(0, 180, 0, 0.25)" : c.status === "chip-collision" ? "rgba(220, 0, 0, 0.25)" : c.status === "trace-collision" ? "rgba(220, 140, 0, 0.25)" : "rgba(120, 120, 120, 0.15)";
1769
1769
  const stroke = c.status === "ok" ? "green" : c.status === "chip-collision" ? "red" : c.status === "trace-collision" ? "orange" : "gray";
1770
+ const candidateLabel = c.status === "ok" ? "status: ok(valid net label candidate)" : c.status === "chip-collision" ? "status: chip-collision" : c.status === "trace-collision" ? "status: trace-collision" : "status: parallel-to-segment";
1770
1771
  graphics.rects.push({
1771
1772
  center: {
1772
1773
  x: (c.bounds.minX + c.bounds.maxX) / 2,
@@ -1775,12 +1776,16 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
1775
1776
  width: c.width,
1776
1777
  height: c.height,
1777
1778
  fill,
1778
- strokeColor: stroke
1779
+ strokeColor: stroke,
1780
+ label: `${candidateLabel}
1781
+ orientation: ${c.orientation}`
1779
1782
  });
1780
1783
  graphics.points.push({
1781
1784
  x: c.anchor.x,
1782
1785
  y: c.anchor.y,
1783
- color: stroke
1786
+ color: stroke,
1787
+ label: `anchor
1788
+ orientation: ${c.orientation}`
1784
1789
  });
1785
1790
  }
1786
1791
  if (solver.netLabelPlacement) {
@@ -1790,12 +1795,16 @@ function visualizeSingleNetLabelPlacementSolver(solver) {
1790
1795
  width: p.width,
1791
1796
  height: p.height,
1792
1797
  fill: "rgba(0, 128, 255, 0.35)",
1793
- strokeColor: "blue"
1798
+ strokeColor: "blue",
1799
+ label: `netId: ${p.netId}
1800
+ globalConnNetId: ${p.globalConnNetId}`
1794
1801
  });
1795
1802
  graphics.points.push({
1796
1803
  x: p.anchorPoint.x,
1797
1804
  y: p.anchorPoint.y,
1798
- color: "blue"
1805
+ color: "blue",
1806
+ label: `anchor
1807
+ orientation: ${p.orientation}`
1799
1808
  });
1800
1809
  }
1801
1810
  return graphics;
@@ -1893,14 +1902,14 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
1893
1902
  };
1894
1903
  let bestCandidate = null;
1895
1904
  let bestScore = -Infinity;
1896
- const EPS5 = 1e-6;
1905
+ const EPS6 = 1e-6;
1897
1906
  for (const curr of tracesToScan) {
1898
1907
  const pts = curr.tracePath.slice();
1899
1908
  for (let si = 0; si < pts.length - 1; si++) {
1900
1909
  const a = pts[si];
1901
1910
  const b = pts[si + 1];
1902
- const isH = Math.abs(a.y - b.y) < EPS5;
1903
- const isV = Math.abs(a.x - b.x) < EPS5;
1911
+ const isH = Math.abs(a.y - b.y) < EPS6;
1912
+ const isV = Math.abs(a.x - b.x) < EPS6;
1904
1913
  if (!isH && !isV) continue;
1905
1914
  const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
1906
1915
  const candidateOrients = orientations.filter(
@@ -2248,12 +2257,16 @@ var NetLabelPlacementSolver = class extends BaseSolver {
2248
2257
  width: p.width,
2249
2258
  height: p.height,
2250
2259
  fill: getColorFromString(p.globalConnNetId, 0.35),
2251
- strokeColor: getColorFromString(p.globalConnNetId, 0.9)
2260
+ strokeColor: getColorFromString(p.globalConnNetId, 0.9),
2261
+ label: `netId: ${p.netId}
2262
+ globalConnNetId: ${p.globalConnNetId}`
2252
2263
  });
2253
2264
  graphics.points.push({
2254
2265
  x: p.anchorPoint.x,
2255
2266
  y: p.anchorPoint.y,
2256
- color: getColorFromString(p.globalConnNetId, 0.9)
2267
+ color: getColorFromString(p.globalConnNetId, 0.9),
2268
+ label: `anchorPoint
2269
+ orientation: ${p.orientation}`
2257
2270
  });
2258
2271
  }
2259
2272
  return graphics;
@@ -2843,7 +2856,7 @@ var SingleOverlapSolver = class extends BaseSolver {
2843
2856
  paddingBuffer: effectivePadding
2844
2857
  // Use the calculated, larger padding
2845
2858
  });
2846
- const getPathLength = (pts) => {
2859
+ const getPathLength2 = (pts) => {
2847
2860
  let len = 0;
2848
2861
  for (let i = 0; i < pts.length - 1; i++) {
2849
2862
  const dx = pts[i + 1].x - pts[i].x;
@@ -2853,7 +2866,7 @@ var SingleOverlapSolver = class extends BaseSolver {
2853
2866
  return len;
2854
2867
  };
2855
2868
  this.queuedCandidatePaths = candidates.sort(
2856
- (a, b) => getPathLength(a) - getPathLength(b)
2869
+ (a, b) => getPathLength2(a) - getPathLength2(b)
2857
2870
  );
2858
2871
  this.obstacles = getObstacleRects(this.problem);
2859
2872
  }
@@ -3420,6 +3433,7 @@ var LongDistancePairSolver = class extends BaseSolver {
3420
3433
  }
3421
3434
  this.queuedCandidatePairs = candidatePairs;
3422
3435
  }
3436
+ params;
3423
3437
  solvedLongDistanceTraces = [];
3424
3438
  queuedCandidatePairs = [];
3425
3439
  currentCandidatePair = null;
@@ -4655,6 +4669,682 @@ var TraceCleanupSolver = class extends BaseSolver {
4655
4669
  }
4656
4670
  };
4657
4671
 
4672
+ // lib/solvers/Example28Solver/types.ts
4673
+ var EPS5 = 1e-9;
4674
+
4675
+ // lib/solvers/Example28Solver/geometry.ts
4676
+ var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
4677
+ var getPathLength = (path) => {
4678
+ let length = 0;
4679
+ for (let i = 0; i < path.length - 1; i++) {
4680
+ length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
4681
+ }
4682
+ return length;
4683
+ };
4684
+ var getDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
4685
+ var isAxisAlignedSegment = (start, end) => Math.abs(start.x - end.x) < EPS5 || Math.abs(start.y - end.y) < EPS5;
4686
+ var getSegmentOrientation = (start, end) => Math.abs(start.y - end.y) < EPS5 ? "horizontal" : "vertical";
4687
+ var projectPointToSegment = (point, start, end) => {
4688
+ if (Math.abs(start.x - end.x) < EPS5) {
4689
+ return {
4690
+ x: start.x,
4691
+ y: Math.min(
4692
+ Math.max(point.y, Math.min(start.y, end.y)),
4693
+ Math.max(start.y, end.y)
4694
+ )
4695
+ };
4696
+ }
4697
+ if (Math.abs(start.y - end.y) < EPS5) {
4698
+ return {
4699
+ x: Math.min(
4700
+ Math.max(point.x, Math.min(start.x, end.x)),
4701
+ Math.max(start.x, end.x)
4702
+ ),
4703
+ y: start.y
4704
+ };
4705
+ }
4706
+ return start;
4707
+ };
4708
+ var getPointToSegmentDistance = (point, start, end) => getDistance(point, projectPointToSegment(point, start, end));
4709
+ var getSegments = (path) => {
4710
+ const segments = [];
4711
+ for (let i = 0; i < path.length - 1; i++) {
4712
+ const start = path[i];
4713
+ const end = path[i + 1];
4714
+ if (!isAxisAlignedSegment(start, end)) continue;
4715
+ segments.push({
4716
+ start,
4717
+ end,
4718
+ orientation: getSegmentOrientation(start, end)
4719
+ });
4720
+ }
4721
+ return segments;
4722
+ };
4723
+ var findSegmentContainingPoint = (path, point) => {
4724
+ for (let i = 0; i < path.length - 1; i++) {
4725
+ const start = path[i];
4726
+ const end = path[i + 1];
4727
+ if (!isAxisAlignedSegment(start, end)) continue;
4728
+ if (getPointToSegmentDistance(point, start, end) > 1e-6) continue;
4729
+ return {
4730
+ index: i,
4731
+ start,
4732
+ end,
4733
+ orientation: getSegmentOrientation(start, end)
4734
+ };
4735
+ }
4736
+ return null;
4737
+ };
4738
+ var isPointWithinSegmentPrimaryRange = (point, segment) => {
4739
+ if (segment.orientation === "horizontal") {
4740
+ return point.x >= Math.min(segment.start.x, segment.end.x) - EPS5 && point.x <= Math.max(segment.start.x, segment.end.x) + EPS5;
4741
+ }
4742
+ return point.y >= Math.min(segment.start.y, segment.end.y) - EPS5 && point.y <= Math.max(segment.start.y, segment.end.y) + EPS5;
4743
+ };
4744
+ var findPreferredReroutedSegment = (path, originalSegmentIndex, originalSegmentCount, orientation, anchorPoint) => {
4745
+ const matchingSegments = getSegments(path).filter(
4746
+ (segment) => segment.orientation === orientation
4747
+ );
4748
+ if (matchingSegments.length === 0) return null;
4749
+ const containingSegments = matchingSegments.filter(
4750
+ (segment) => isPointWithinSegmentPrimaryRange(anchorPoint, segment)
4751
+ );
4752
+ const candidateSegments = containingSegments.length > 0 ? containingSegments : matchingSegments;
4753
+ const isNearStart = originalSegmentIndex < originalSegmentCount / 2;
4754
+ return isNearStart ? candidateSegments[0] : candidateSegments[candidateSegments.length - 1];
4755
+ };
4756
+ var projectPointToPath = (point, path) => {
4757
+ let bestPoint = null;
4758
+ let bestDistance = Number.POSITIVE_INFINITY;
4759
+ for (let i = 0; i < path.length - 1; i++) {
4760
+ const projectedPoint = projectPointToSegment(point, path[i], path[i + 1]);
4761
+ const distance3 = getDistance(point, projectedPoint);
4762
+ if (distance3 < bestDistance) {
4763
+ bestPoint = projectedPoint;
4764
+ bestDistance = distance3;
4765
+ }
4766
+ }
4767
+ return bestPoint;
4768
+ };
4769
+ var segmentsIntersect = (a1, a2, b1, b2) => {
4770
+ const aVertical = Math.abs(a1.x - a2.x) < EPS5;
4771
+ const bVertical = Math.abs(b1.x - b2.x) < EPS5;
4772
+ const between = (value, p1, p2) => value >= Math.min(p1, p2) - EPS5 && value <= Math.max(p1, p2) + EPS5;
4773
+ if (aVertical && bVertical) {
4774
+ if (Math.abs(a1.x - b1.x) > EPS5) return false;
4775
+ 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));
4776
+ return overlap > EPS5;
4777
+ }
4778
+ if (!aVertical && !bVertical) {
4779
+ if (Math.abs(a1.y - b1.y) > EPS5) return false;
4780
+ 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));
4781
+ return overlap > EPS5;
4782
+ }
4783
+ const verticalA = aVertical ? a1 : b1;
4784
+ const verticalB = aVertical ? a2 : b2;
4785
+ const horizontalA = aVertical ? b1 : a1;
4786
+ const horizontalB = aVertical ? b2 : a2;
4787
+ return between(verticalA.x, horizontalA.x, horizontalB.x) && between(horizontalA.y, verticalA.y, verticalB.y);
4788
+ };
4789
+ var countPathIntersections = (pathA, pathB) => {
4790
+ let count = 0;
4791
+ for (let i = 0; i < pathA.length - 1; i++) {
4792
+ for (let j = 0; j < pathB.length - 1; j++) {
4793
+ if (segmentsIntersect(pathA[i], pathA[i + 1], pathB[j], pathB[j + 1])) {
4794
+ count++;
4795
+ }
4796
+ }
4797
+ }
4798
+ return count;
4799
+ };
4800
+ var isPathCollidingWithChipInterior = (path, chipObstacles) => {
4801
+ for (let i = 0; i < path.length - 1; i++) {
4802
+ const start = path[i];
4803
+ const end = path[i + 1];
4804
+ for (const obstacle of chipObstacles) {
4805
+ if (!segmentIntersectsRect(start, end, obstacle)) continue;
4806
+ if (segmentRunsAlongRectBoundary(start, end, obstacle)) continue;
4807
+ return true;
4808
+ }
4809
+ }
4810
+ return false;
4811
+ };
4812
+ var segmentRunsAlongRectBoundary = (start, end, rect) => {
4813
+ const isVertical3 = Math.abs(start.x - end.x) < EPS5;
4814
+ const isHorizontal2 = Math.abs(start.y - end.y) < EPS5;
4815
+ if (isVertical3) {
4816
+ return Math.abs(start.x - rect.minX) < EPS5 || Math.abs(start.x - rect.maxX) < EPS5;
4817
+ }
4818
+ if (isHorizontal2) {
4819
+ return Math.abs(start.y - rect.minY) < EPS5 || Math.abs(start.y - rect.maxY) < EPS5;
4820
+ }
4821
+ return false;
4822
+ };
4823
+
4824
+ // lib/solvers/Example28Solver/labelMovement.ts
4825
+ var moveAttachedLabelsToReroutedTrace = ({
4826
+ trace,
4827
+ originalTracePath,
4828
+ reroutedTracePath,
4829
+ netLabelPlacements
4830
+ }) => netLabelPlacements.map((label) => {
4831
+ if (!isLabelAttachedToTrace(label, trace)) return label;
4832
+ const movedAnchorPoint = getMovedAnchorPointForReroute(
4833
+ label.anchorPoint,
4834
+ originalTracePath,
4835
+ reroutedTracePath
4836
+ );
4837
+ if (!movedAnchorPoint) return label;
4838
+ const delta = {
4839
+ x: movedAnchorPoint.x - label.anchorPoint.x,
4840
+ y: movedAnchorPoint.y - label.anchorPoint.y
4841
+ };
4842
+ return {
4843
+ ...label,
4844
+ anchorPoint: movedAnchorPoint,
4845
+ center: {
4846
+ x: label.center.x + delta.x,
4847
+ y: label.center.y + delta.y
4848
+ }
4849
+ };
4850
+ });
4851
+ var isLabelAttachedToTrace = (label, trace) => label.globalConnNetId === trace.globalConnNetId || label.mspConnectionPairIds.includes(trace.mspPairId);
4852
+ var getMovedAnchorPointForReroute = (anchorPoint, originalTracePath, reroutedTracePath) => {
4853
+ const originalSegment = findSegmentContainingPoint(
4854
+ originalTracePath,
4855
+ anchorPoint
4856
+ );
4857
+ if (!originalSegment) return null;
4858
+ const preferredSegment = findPreferredReroutedSegment(
4859
+ reroutedTracePath,
4860
+ originalSegment.index,
4861
+ originalTracePath.length - 1,
4862
+ originalSegment.orientation,
4863
+ anchorPoint
4864
+ );
4865
+ if (!preferredSegment) {
4866
+ return projectPointToPath(anchorPoint, reroutedTracePath);
4867
+ }
4868
+ return projectPointToSegment(
4869
+ anchorPoint,
4870
+ preferredSegment.start,
4871
+ preferredSegment.end
4872
+ );
4873
+ };
4874
+
4875
+ // lib/utils/dir.ts
4876
+ var dir = (facingDirection) => {
4877
+ switch (facingDirection) {
4878
+ case "x+":
4879
+ return { x: 1, y: 0 };
4880
+ case "x-":
4881
+ return { x: -1, y: 0 };
4882
+ case "y+":
4883
+ return { x: 0, y: 1 };
4884
+ case "y-":
4885
+ return { x: 0, y: -1 };
4886
+ }
4887
+ throw new Error(`Invalid facing direction: ${facingDirection}`);
4888
+ };
4889
+
4890
+ // lib/solvers/Example28Solver/reroute.ts
4891
+ var LABEL_SIDE_CLEARANCE = 0.1;
4892
+ var LABEL_HUG_CLEARANCE = 1e-3;
4893
+ var findBestReroutePath = ({
4894
+ trace,
4895
+ obstacleLabel,
4896
+ inputProblem,
4897
+ outputTraces,
4898
+ outputNetLabelPlacements,
4899
+ chipObstacles
4900
+ }) => {
4901
+ const candidateResults = generateRerouteCandidateResults({
4902
+ trace,
4903
+ label: obstacleLabel,
4904
+ inputProblem,
4905
+ outputTraces,
4906
+ outputNetLabelPlacements,
4907
+ chipObstacles
4908
+ });
4909
+ const bestPath = selectBestReroutePath({
4910
+ trace,
4911
+ obstacleLabel,
4912
+ outputTraces,
4913
+ outputNetLabelPlacements,
4914
+ candidateResults
4915
+ });
4916
+ markSelectedCandidate(candidateResults, bestPath);
4917
+ return { bestPath, candidateResults };
4918
+ };
4919
+ var generateRerouteCandidateResults = ({
4920
+ trace,
4921
+ label,
4922
+ inputProblem,
4923
+ outputTraces,
4924
+ outputNetLabelPlacements,
4925
+ chipObstacles
4926
+ }) => {
4927
+ const rawCandidates = generateCandidatePaths(trace, label, inputProblem);
4928
+ const seen = /* @__PURE__ */ new Set();
4929
+ const candidateResults = [];
4930
+ for (const rawCandidate of rawCandidates) {
4931
+ candidateResults.push(
4932
+ createCandidateResult({
4933
+ trace,
4934
+ obstacleLabel: label,
4935
+ path: simplifyPath(rawCandidate),
4936
+ seen,
4937
+ outputTraces,
4938
+ outputNetLabelPlacements,
4939
+ chipObstacles
4940
+ })
4941
+ );
4942
+ }
4943
+ return candidateResults;
4944
+ };
4945
+ var generateCandidatePaths = (trace, label, inputProblem) => [
4946
+ ...generateLabelHugCandidates(trace, label),
4947
+ ...generateRerouteCandidates({
4948
+ trace,
4949
+ label,
4950
+ problem: inputProblem,
4951
+ paddingBuffer: LABEL_SIDE_CLEARANCE,
4952
+ detourCount: 0
4953
+ }),
4954
+ ...generateEndpointDetourCandidates(trace, label)
4955
+ ];
4956
+ var createCandidateResult = ({
4957
+ trace,
4958
+ obstacleLabel,
4959
+ path,
4960
+ seen,
4961
+ outputTraces,
4962
+ outputNetLabelPlacements,
4963
+ chipObstacles
4964
+ }) => {
4965
+ const key = getPathKey(path);
4966
+ if (seen.has(key)) {
4967
+ return {
4968
+ path,
4969
+ status: "duplicate",
4970
+ selected: false
4971
+ };
4972
+ }
4973
+ seen.add(key);
4974
+ if (isPathCollidingWithChipInterior(path, chipObstacles)) {
4975
+ return {
4976
+ path,
4977
+ status: "chip-collision",
4978
+ selected: false
4979
+ };
4980
+ }
4981
+ return {
4982
+ path,
4983
+ score: scoreTracePath({
4984
+ trace,
4985
+ tracePath: path,
4986
+ obstacleLabel,
4987
+ outputTraces,
4988
+ outputNetLabelPlacements
4989
+ }),
4990
+ status: "valid",
4991
+ selected: false
4992
+ };
4993
+ };
4994
+ var generateEndpointDetourCandidates = (trace, label) => {
4995
+ const start = trace.tracePath[0];
4996
+ const end = trace.tracePath[trace.tracePath.length - 1];
4997
+ if (!start || !end) return [];
4998
+ const bounds = getRectBounds(label.center, label.width, label.height);
4999
+ const padding = LABEL_SIDE_CLEARANCE;
5000
+ const labelDirection = dir(label.orientation);
5001
+ const labelSideX = labelDirection.x < 0 ? bounds.minX - padding : bounds.maxX + padding;
5002
+ const oppositeSideX = labelDirection.x < 0 ? bounds.maxX + padding : bounds.minX - padding;
5003
+ const topY = bounds.maxY + padding;
5004
+ const bottomY = bounds.minY - padding;
5005
+ const candidates = [];
5006
+ for (const sideX of [labelSideX, oppositeSideX]) {
5007
+ candidates.push(
5008
+ [
5009
+ start,
5010
+ { x: start.x, y: topY },
5011
+ { x: sideX, y: topY },
5012
+ { x: sideX, y: bottomY },
5013
+ { x: end.x, y: bottomY },
5014
+ end
5015
+ ],
5016
+ [
5017
+ start,
5018
+ { x: start.x, y: bottomY },
5019
+ { x: sideX, y: bottomY },
5020
+ { x: sideX, y: topY },
5021
+ { x: end.x, y: topY },
5022
+ end
5023
+ ]
5024
+ );
5025
+ }
5026
+ return candidates;
5027
+ };
5028
+ var generateLabelHugCandidates = (trace, label) => {
5029
+ const start = trace.tracePath[0];
5030
+ const end = trace.tracePath[trace.tracePath.length - 1];
5031
+ if (!start || !end) return [];
5032
+ const labelDirection = dir(label.orientation);
5033
+ if (labelDirection.x === 0) return [];
5034
+ const bounds = getRectBounds(label.center, label.width, label.height);
5035
+ const sideX = labelDirection.x < 0 ? bounds.minX - LABEL_SIDE_CLEARANCE : bounds.maxX + LABEL_SIDE_CLEARANCE;
5036
+ const topY = bounds.maxY + LABEL_HUG_CLEARANCE;
5037
+ const bottomY = bounds.minY - LABEL_HUG_CLEARANCE;
5038
+ const startsAboveEnd = start.y >= end.y;
5039
+ const firstY = startsAboveEnd ? topY : bottomY;
5040
+ const secondY = startsAboveEnd ? bottomY : topY;
5041
+ return [
5042
+ [
5043
+ start,
5044
+ { x: start.x, y: firstY },
5045
+ { x: sideX, y: firstY },
5046
+ { x: sideX, y: secondY },
5047
+ { x: end.x, y: secondY },
5048
+ end
5049
+ ],
5050
+ [
5051
+ start,
5052
+ { x: start.x, y: secondY },
5053
+ { x: sideX, y: secondY },
5054
+ { x: sideX, y: firstY },
5055
+ { x: end.x, y: firstY },
5056
+ end
5057
+ ]
5058
+ ];
5059
+ };
5060
+ var selectBestReroutePath = ({
5061
+ trace,
5062
+ obstacleLabel,
5063
+ outputTraces,
5064
+ outputNetLabelPlacements,
5065
+ candidateResults
5066
+ }) => {
5067
+ let bestPath = null;
5068
+ let bestScore = scoreTracePath({
5069
+ trace,
5070
+ tracePath: trace.tracePath,
5071
+ obstacleLabel,
5072
+ outputTraces,
5073
+ outputNetLabelPlacements
5074
+ });
5075
+ for (const candidate of candidateResults) {
5076
+ if (candidate.status !== "valid" || !candidate.score) continue;
5077
+ if (!isBetterScore(candidate.score, bestScore)) continue;
5078
+ bestScore = candidate.score;
5079
+ bestPath = candidate.path;
5080
+ }
5081
+ return bestPath;
5082
+ };
5083
+ var markSelectedCandidate = (candidateResults, bestPath) => {
5084
+ if (!bestPath) return;
5085
+ for (const candidate of candidateResults) {
5086
+ candidate.selected = candidate.path === bestPath;
5087
+ }
5088
+ };
5089
+ var scoreTracePath = ({
5090
+ trace,
5091
+ tracePath,
5092
+ obstacleLabel,
5093
+ outputTraces,
5094
+ outputNetLabelPlacements
5095
+ }) => {
5096
+ const candidateTrace = { ...trace, tracePath };
5097
+ return {
5098
+ labelIntersections: detectTraceLabelOverlap({
5099
+ traces: [candidateTrace],
5100
+ netLabels: outputNetLabelPlacements
5101
+ }).length,
5102
+ labelHugDistance: getLabelHugDistance(tracePath, obstacleLabel),
5103
+ traceIntersections: countTraceIntersections(candidateTrace, outputTraces),
5104
+ pathLength: getPathLength(tracePath)
5105
+ };
5106
+ };
5107
+ var countTraceIntersections = (trace, outputTraces) => {
5108
+ let count = 0;
5109
+ for (const otherTrace of outputTraces) {
5110
+ if (otherTrace.mspPairId === trace.mspPairId) continue;
5111
+ count += countPathIntersections(trace.tracePath, otherTrace.tracePath);
5112
+ }
5113
+ return count;
5114
+ };
5115
+ var isBetterScore = (score, bestScore) => {
5116
+ if (score.labelIntersections !== bestScore.labelIntersections) {
5117
+ return score.labelIntersections < bestScore.labelIntersections;
5118
+ }
5119
+ if (score.labelHugDistance !== bestScore.labelHugDistance) {
5120
+ return score.labelHugDistance < bestScore.labelHugDistance;
5121
+ }
5122
+ if (score.traceIntersections !== bestScore.traceIntersections) {
5123
+ return score.traceIntersections < bestScore.traceIntersections;
5124
+ }
5125
+ return score.pathLength < bestScore.pathLength;
5126
+ };
5127
+ var getLabelHugDistance = (tracePath, obstacleLabel) => {
5128
+ const bounds = getRectBounds(
5129
+ obstacleLabel.center,
5130
+ obstacleLabel.width,
5131
+ obstacleLabel.height
5132
+ );
5133
+ let distance3 = 0;
5134
+ for (const point of tracePath) {
5135
+ distance3 += getPointDistanceFromRect(point, bounds);
5136
+ }
5137
+ return distance3;
5138
+ };
5139
+ var getPointDistanceFromRect = (point, rect) => {
5140
+ const dx = Math.max(rect.minX - point.x, 0, point.x - rect.maxX);
5141
+ const dy = Math.max(rect.minY - point.y, 0, point.y - rect.maxY);
5142
+ return dx + dy;
5143
+ };
5144
+
5145
+ // lib/solvers/Example28Solver/visualize.ts
5146
+ var CANDIDATE_SELECTED_COLOR = "blue";
5147
+ var CANDIDATE_REJECTED_COLOR = "red";
5148
+ var visualizeExample28Solver = (state) => {
5149
+ const graphics = visualizeInputProblem(state.inputProblem);
5150
+ drawTraces(graphics, state);
5151
+ drawNetLabels(graphics, state);
5152
+ if (!state.solved) {
5153
+ drawCurrentCandidates(graphics, state.currentCandidateResults);
5154
+ drawCurrentOverlap(graphics, state);
5155
+ }
5156
+ return graphics;
5157
+ };
5158
+ var drawTraces = (graphics, state) => {
5159
+ for (const trace of state.outputTraces) {
5160
+ graphics.lines.push({
5161
+ points: trace.tracePath,
5162
+ strokeColor: "purple"
5163
+ });
5164
+ }
5165
+ };
5166
+ var drawNetLabels = (graphics, state) => {
5167
+ for (const label of state.outputNetLabelPlacements) {
5168
+ const color = getColorFromString(label.globalConnNetId, 0.35);
5169
+ graphics.rects.push({
5170
+ center: label.center,
5171
+ width: label.width,
5172
+ height: label.height,
5173
+ fill: color,
5174
+ strokeColor: getColorFromString(label.globalConnNetId, 0.9),
5175
+ label: `netId: ${label.netId}
5176
+ globalConnNetId: ${label.globalConnNetId}`
5177
+ });
5178
+ graphics.points.push({
5179
+ ...label.anchorPoint,
5180
+ color: getColorFromString(label.globalConnNetId, 0.9),
5181
+ label: `anchorPoint
5182
+ orientation: ${label.orientation}`
5183
+ });
5184
+ }
5185
+ };
5186
+ var drawCurrentCandidates = (graphics, candidateResults) => {
5187
+ for (const candidate of candidateResults) {
5188
+ const scoreLabel = candidate.score ? `
5189
+ label intersections: ${candidate.score.labelIntersections}
5190
+ trace intersections: ${candidate.score.traceIntersections}` : "";
5191
+ graphics.lines.push({
5192
+ points: candidate.path,
5193
+ strokeColor: candidate.selected ? CANDIDATE_SELECTED_COLOR : CANDIDATE_REJECTED_COLOR,
5194
+ strokeDash: candidate.selected ? void 0 : "4 2",
5195
+ label: `${candidate.selected ? "selected" : candidate.status} reroute${scoreLabel}`
5196
+ });
5197
+ }
5198
+ };
5199
+ var drawCurrentOverlap = (graphics, state) => {
5200
+ if (!state.currentOverlap) return;
5201
+ const label = state.currentOverlap.label;
5202
+ graphics.rects.push({
5203
+ center: label.center,
5204
+ width: label.width,
5205
+ height: label.height,
5206
+ fill: "rgba(255, 0, 0, 0.2)",
5207
+ strokeColor: CANDIDATE_REJECTED_COLOR,
5208
+ label: `overlap target
5209
+ ${label.netId ?? label.globalConnNetId}`
5210
+ });
5211
+ };
5212
+
5213
+ // lib/solvers/Example28Solver/Example28Solver.ts
5214
+ var Example28Solver = class extends BaseSolver {
5215
+ inputProblem;
5216
+ traces;
5217
+ netLabelPlacements;
5218
+ outputTraces;
5219
+ outputNetLabelPlacements;
5220
+ queuedOverlaps = [];
5221
+ currentOverlap = null;
5222
+ currentCandidateResults = [];
5223
+ chipObstacles;
5224
+ constructor(params) {
5225
+ super();
5226
+ this.inputProblem = params.inputProblem;
5227
+ this.traces = params.traces;
5228
+ this.netLabelPlacements = params.netLabelPlacements;
5229
+ this.outputTraces = [...params.traces];
5230
+ this.outputNetLabelPlacements = [...params.netLabelPlacements];
5231
+ this.chipObstacles = getObstacleRects(params.inputProblem);
5232
+ this.initializeQueuedOverlaps();
5233
+ this.currentOverlap = this.queuedOverlaps[0] ?? null;
5234
+ }
5235
+ getConstructorParams() {
5236
+ return {
5237
+ inputProblem: this.inputProblem,
5238
+ traces: this.traces,
5239
+ netLabelPlacements: this.netLabelPlacements
5240
+ };
5241
+ }
5242
+ _step() {
5243
+ const overlap = this.queuedOverlaps.shift();
5244
+ if (!overlap) {
5245
+ this.finish();
5246
+ return;
5247
+ }
5248
+ if (!this.isOverlapStillPresent(overlap)) {
5249
+ this.currentOverlap = overlap;
5250
+ this.currentCandidateResults = [];
5251
+ return;
5252
+ }
5253
+ this.rerouteOverlappingTrace(overlap);
5254
+ }
5255
+ finish() {
5256
+ this.currentOverlap = null;
5257
+ this.currentCandidateResults = [];
5258
+ this.solved = true;
5259
+ }
5260
+ initializeQueuedOverlaps() {
5261
+ const queuedOverlaps = [];
5262
+ const seenPairs = /* @__PURE__ */ new Set();
5263
+ for (const overlap of this.getTraceLabelOverlaps()) {
5264
+ if (!this.shouldRerouteOverlap(overlap)) continue;
5265
+ const key = this.getOverlapKey(overlap);
5266
+ if (seenPairs.has(key)) continue;
5267
+ seenPairs.add(key);
5268
+ queuedOverlaps.push(overlap);
5269
+ }
5270
+ this.queuedOverlaps = queuedOverlaps;
5271
+ }
5272
+ getTraceLabelOverlaps() {
5273
+ return detectTraceLabelOverlap({
5274
+ traces: this.outputTraces,
5275
+ netLabels: this.outputNetLabelPlacements
5276
+ });
5277
+ }
5278
+ shouldRerouteOverlap(overlap) {
5279
+ return this.isXFacingLabel(overlap.label) && !this.hasExplicitOrientationConstraint(overlap.label);
5280
+ }
5281
+ isXFacingLabel(label) {
5282
+ return label.orientation === "x+" || label.orientation === "x-";
5283
+ }
5284
+ hasExplicitOrientationConstraint(label) {
5285
+ const effectiveNetId = label.netId ?? label.globalConnNetId;
5286
+ return Object.hasOwn(
5287
+ this.inputProblem.availableNetLabelOrientations,
5288
+ effectiveNetId
5289
+ );
5290
+ }
5291
+ getOverlapKey(overlap) {
5292
+ const labelNetId = overlap.label.netId ?? overlap.label.globalConnNetId;
5293
+ return `${overlap.trace.mspPairId}:${labelNetId}:${overlap.label.anchorPoint.x}:${overlap.label.anchorPoint.y}`;
5294
+ }
5295
+ isOverlapStillPresent(overlap) {
5296
+ const currentTrace = this.getCurrentTrace(overlap.trace);
5297
+ if (!currentTrace) return false;
5298
+ return detectTraceLabelOverlap({
5299
+ traces: [currentTrace],
5300
+ netLabels: [overlap.label]
5301
+ }).length > 0;
5302
+ }
5303
+ getCurrentTrace(trace) {
5304
+ return this.outputTraces.find((t) => t.mspPairId === trace.mspPairId);
5305
+ }
5306
+ rerouteOverlappingTrace(overlap) {
5307
+ this.currentOverlap = overlap;
5308
+ this.currentCandidateResults = [];
5309
+ const traceIndex = this.outputTraces.findIndex(
5310
+ (trace) => trace.mspPairId === overlap.trace.mspPairId
5311
+ );
5312
+ if (traceIndex === -1) return;
5313
+ const currentTrace = this.outputTraces[traceIndex];
5314
+ const rerouteResult = findBestReroutePath({
5315
+ trace: currentTrace,
5316
+ obstacleLabel: overlap.label,
5317
+ inputProblem: this.inputProblem,
5318
+ outputTraces: this.outputTraces,
5319
+ outputNetLabelPlacements: this.outputNetLabelPlacements,
5320
+ chipObstacles: this.chipObstacles
5321
+ });
5322
+ this.currentCandidateResults = rerouteResult.candidateResults;
5323
+ if (!rerouteResult.bestPath) return;
5324
+ const originalTracePath = currentTrace.tracePath;
5325
+ this.outputTraces[traceIndex] = {
5326
+ ...currentTrace,
5327
+ tracePath: rerouteResult.bestPath
5328
+ };
5329
+ this.outputNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
5330
+ trace: currentTrace,
5331
+ originalTracePath,
5332
+ reroutedTracePath: rerouteResult.bestPath,
5333
+ netLabelPlacements: this.outputNetLabelPlacements
5334
+ });
5335
+ }
5336
+ visualize() {
5337
+ return visualizeExample28Solver({
5338
+ inputProblem: this.inputProblem,
5339
+ outputTraces: this.outputTraces,
5340
+ outputNetLabelPlacements: this.outputNetLabelPlacements,
5341
+ solved: this.solved,
5342
+ currentOverlap: this.currentOverlap,
5343
+ currentCandidateResults: this.currentCandidateResults
5344
+ });
5345
+ }
5346
+ };
5347
+
4658
5348
  // lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
4659
5349
  function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
4660
5350
  return {
@@ -4675,6 +5365,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
4675
5365
  labelMergingSolver;
4676
5366
  traceLabelOverlapAvoidanceSolver;
4677
5367
  traceCleanupSolver;
5368
+ example28Solver;
4678
5369
  startTimeOfPhase;
4679
5370
  endTimeOfPhase;
4680
5371
  timeSpentOnPhase;
@@ -4810,7 +5501,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
4810
5501
  }
4811
5502
  ];
4812
5503
  }
4813
- )
5504
+ ),
5505
+ definePipelineStep("example28Solver", Example28Solver, (instance) => {
5506
+ const traces = instance.traceCleanupSolver?.getOutput().traces ?? instance.traceLabelOverlapAvoidanceSolver.getOutput().traces;
5507
+ return [
5508
+ {
5509
+ inputProblem: instance.inputProblem,
5510
+ traces,
5511
+ netLabelPlacements: instance.netLabelPlacementSolver.netLabelPlacements
5512
+ }
5513
+ ];
5514
+ })
4814
5515
  ];
4815
5516
  constructor(inputProblem) {
4816
5517
  super();