@tscircuit/hypergraph 0.0.54 → 0.0.55

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 (2) hide show
  1. package/dist/index.js +389 -65
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -14700,6 +14700,10 @@ var buildRegionsFromCells = (merged) => {
14700
14700
  );
14701
14701
  return { regions, hulls };
14702
14702
  };
14703
+ var clampPointToBounds = (point4, bounds) => ({
14704
+ x: Math.min(bounds.maxX, Math.max(bounds.minX, point4.x)),
14705
+ y: Math.min(bounds.maxY, Math.max(bounds.minY, point4.y))
14706
+ });
14703
14707
  var BuildRegionsSolver = class extends BaseSolver2 {
14704
14708
  input;
14705
14709
  output = null;
@@ -14708,9 +14712,15 @@ var BuildRegionsSolver = class extends BaseSolver2 {
14708
14712
  this.input = input;
14709
14713
  }
14710
14714
  _step() {
14711
- const { regions, hulls } = buildRegionsFromCells(this.input);
14715
+ const boundedPts = this.input.pts.map(
14716
+ (pt) => clampPointToBounds(pt, this.input.bounds)
14717
+ );
14718
+ const { regions, hulls } = buildRegionsFromCells({
14719
+ ...this.input,
14720
+ pts: boundedPts
14721
+ });
14712
14722
  this.output = {
14713
- pts: this.input.pts,
14723
+ pts: boundedPts,
14714
14724
  validTris: this.input.validTris,
14715
14725
  regions,
14716
14726
  hulls,
@@ -14733,10 +14743,10 @@ var BuildRegionsSolver = class extends BaseSolver2 {
14733
14743
  return {
14734
14744
  points: output.pts.map((pt) => ({ x: pt.x, y: pt.y, color: "#3b82f6" })),
14735
14745
  lines: output.regions.flatMap(
14736
- (region) => region.flatMap((point3, i) => {
14746
+ (region) => region.flatMap((point4, i) => {
14737
14747
  const next = region[(i + 1) % region.length];
14738
14748
  if (!next) return [];
14739
- return [{ points: [point3, next], strokeColor: "#0f766e" }];
14749
+ return [{ points: [point4, next], strokeColor: "#0f766e" }];
14740
14750
  })
14741
14751
  ),
14742
14752
  rects: [],
@@ -14964,6 +14974,63 @@ var resolveConstraintCrossings = (pts, constraintEdges, ringBoundaries) => {
14964
14974
  }
14965
14975
  return { pts: outPts, constraintEdges: outEdges, hadCrossings: true };
14966
14976
  };
14977
+ var ringToFlattenPoly = (ring) => {
14978
+ return new Polygon$1(ring.map((p) => point(p.x, p.y)));
14979
+ };
14980
+ var extractFaces = (poly) => {
14981
+ const result = [];
14982
+ for (const face of poly.faces) {
14983
+ const ring = [];
14984
+ for (const v of face.vertices) {
14985
+ ring.push({ x: v.x, y: v.y });
14986
+ }
14987
+ if (ring.length >= 3) {
14988
+ result.push(ring);
14989
+ }
14990
+ }
14991
+ return result;
14992
+ };
14993
+ var unionObstacleBoundaries = (rings) => {
14994
+ if (rings.length <= 1) return rings;
14995
+ try {
14996
+ let polys = rings.map(ringToFlattenPoly);
14997
+ for (let pass = 0; pass < 2; pass++) {
14998
+ const merged = [];
14999
+ const used = /* @__PURE__ */ new Set();
15000
+ for (let i = 0; i < polys.length; i++) {
15001
+ if (used.has(i)) continue;
15002
+ let current = polys[i];
15003
+ for (let j = i + 1; j < polys.length; j++) {
15004
+ if (used.has(j)) continue;
15005
+ const boxA = current.box;
15006
+ const boxB = polys[j].box;
15007
+ if (boxA.xmin > boxB.xmax + 1e-6 || boxA.xmax < boxB.xmin - 1e-6 || boxA.ymin > boxB.ymax + 1e-6 || boxA.ymax < boxB.ymin - 1e-6) {
15008
+ continue;
15009
+ }
15010
+ try {
15011
+ const unified = BooleanOperations.unify(current, polys[j]);
15012
+ if (unified.faces.size > 0) {
15013
+ current = unified;
15014
+ used.add(j);
15015
+ }
15016
+ } catch {
15017
+ }
15018
+ }
15019
+ merged.push(current);
15020
+ used.add(i);
15021
+ }
15022
+ polys = merged;
15023
+ }
15024
+ const result = [];
15025
+ for (const poly of polys) {
15026
+ const faces = extractFaces(poly);
15027
+ result.push(...faces);
15028
+ }
15029
+ return result.length > 0 ? result : rings;
15030
+ } catch {
15031
+ return rings;
15032
+ }
15033
+ };
14967
15034
  var addRing = (ringPts, allPts, constraintEdges) => {
14968
15035
  const startIdx = allPts.length;
14969
15036
  allPts.push(...ringPts);
@@ -15005,6 +15072,7 @@ var generateBoundaryPointsWithEdges = (params) => {
15005
15072
  }
15006
15073
  ringBoundaries.push(constraintEdges.length);
15007
15074
  addRing(boundsPts, allPts, constraintEdges);
15075
+ const obstacleRings = [];
15008
15076
  for (const via of vias) {
15009
15077
  const radius = via.diameter / 2 + clearance;
15010
15078
  const viaPts = [];
@@ -15015,8 +15083,7 @@ var generateBoundaryPointsWithEdges = (params) => {
15015
15083
  y: via.center.y + radius * Math.sin(angle)
15016
15084
  });
15017
15085
  }
15018
- ringBoundaries.push(constraintEdges.length);
15019
- addRing(viaPts, allPts, constraintEdges);
15086
+ obstacleRings.push(viaPts);
15020
15087
  }
15021
15088
  for (const rect of rects) {
15022
15089
  const halfWidth = rect.width / 2 + clearance;
@@ -15027,8 +15094,7 @@ var generateBoundaryPointsWithEdges = (params) => {
15027
15094
  rotatePoint({ localX: halfWidth, localY: halfHeight, rect }),
15028
15095
  rotatePoint({ localX: -halfWidth, localY: halfHeight, rect })
15029
15096
  ];
15030
- ringBoundaries.push(constraintEdges.length);
15031
- addRing(rectPts, allPts, constraintEdges);
15097
+ obstacleRings.push(rectPts);
15032
15098
  }
15033
15099
  for (const polygon2 of polygons) {
15034
15100
  if (polygon2.points.length < 3) continue;
@@ -15037,8 +15103,12 @@ var generateBoundaryPointsWithEdges = (params) => {
15037
15103
  clearance,
15038
15104
  verticesOnly: true
15039
15105
  });
15106
+ obstacleRings.push(offsetPoints);
15107
+ }
15108
+ const mergedRings = unionObstacleBoundaries(obstacleRings);
15109
+ for (const ring of mergedRings) {
15040
15110
  ringBoundaries.push(constraintEdges.length);
15041
- addRing(offsetPoints, allPts, constraintEdges);
15111
+ addRing(ring, allPts, constraintEdges);
15042
15112
  }
15043
15113
  const resolved = resolveConstraintCrossings(
15044
15114
  allPts,
@@ -15260,6 +15330,256 @@ var mergeCells = (params) => {
15260
15330
  const depths = cells.map((cell) => concavityDepth(cell, pts));
15261
15331
  return { cells, depths };
15262
15332
  };
15333
+ var edgeKey3 = (a, b) => a < b ? a * 1e5 + b : b * 1e5 + a;
15334
+ var mergeCellsPolyanya = (params) => {
15335
+ const { triangles, pts } = params;
15336
+ if (!triangles.length) return { cells: [], depths: [] };
15337
+ const n = triangles.length;
15338
+ const cells = triangles.map(([a, b, c]) => {
15339
+ const pa = pts[a];
15340
+ const pb = pts[b];
15341
+ const pc = pts[c];
15342
+ if (!pa || !pb || !pc) return [a, b, c];
15343
+ return cross({ o: pa, a: pb, b: pc }) < 0 ? [a, c, b] : [a, b, c];
15344
+ });
15345
+ const adj = new Array(n).fill(null);
15346
+ for (let i = 0; i < n; i++) adj[i] = /* @__PURE__ */ new Map();
15347
+ function rebuildAdj() {
15348
+ for (let i = 0; i < adj.length; i++) {
15349
+ if (adj[i]) adj[i].clear();
15350
+ }
15351
+ const edgeMap = /* @__PURE__ */ new Map();
15352
+ for (let ci = 0; ci < cells.length; ci++) {
15353
+ const ring = cells[ci];
15354
+ if (!ring) continue;
15355
+ for (let k = 0; k < ring.length; k++) {
15356
+ const a = ring[k];
15357
+ const b = ring[(k + 1) % ring.length];
15358
+ const key = edgeKey3(a, b);
15359
+ const existing = edgeMap.get(key);
15360
+ if (existing) {
15361
+ adj[ci].set(k, {
15362
+ neighbor: existing.cellIdx,
15363
+ neighborEdge: existing.edgePos
15364
+ });
15365
+ adj[existing.cellIdx].set(existing.edgePos, {
15366
+ neighbor: ci,
15367
+ neighborEdge: k
15368
+ });
15369
+ } else {
15370
+ edgeMap.set(key, { cellIdx: ci, edgePos: k });
15371
+ }
15372
+ }
15373
+ }
15374
+ }
15375
+ rebuildAdj();
15376
+ function cellArea(ring) {
15377
+ let a = 0;
15378
+ for (let i = 0; i < ring.length; i++) {
15379
+ const p = pts[ring[i]];
15380
+ const q = pts[ring[(i + 1) % ring.length]];
15381
+ a += p.x * q.y - q.x * p.y;
15382
+ }
15383
+ return Math.abs(a) / 2;
15384
+ }
15385
+ function findBoundary(xIdx, yIdx) {
15386
+ const xRing = cells[xIdx];
15387
+ const xAdj = adj[xIdx];
15388
+ const L = xRing.length;
15389
+ let seedK = -1;
15390
+ for (const [k, entry] of xAdj) {
15391
+ if (entry.neighbor === yIdx) {
15392
+ seedK = k;
15393
+ break;
15394
+ }
15395
+ }
15396
+ if (seedK === -1) return null;
15397
+ let firstK = seedK;
15398
+ while (true) {
15399
+ const prev = (firstK - 1 + L) % L;
15400
+ if (prev === seedK) break;
15401
+ const prevEntry = xAdj.get(prev);
15402
+ if (!prevEntry || prevEntry.neighbor !== yIdx) break;
15403
+ firstK = prev;
15404
+ }
15405
+ let lastK = seedK;
15406
+ while (true) {
15407
+ const next = (lastK + 1) % L;
15408
+ if (next === firstK) break;
15409
+ const nextEntry = xAdj.get(next);
15410
+ if (!nextEntry || nextEntry.neighbor !== yIdx) break;
15411
+ lastK = next;
15412
+ }
15413
+ const sharedCount = (lastK - firstK + L) % L + 1;
15414
+ return { firstK, lastK, sharedCount };
15415
+ }
15416
+ function canMerge(xIdx, yIdx) {
15417
+ const xRing = cells[xIdx];
15418
+ const yRing = cells[yIdx];
15419
+ const boundary = findBoundary(xIdx, yIdx);
15420
+ if (!boundary) return false;
15421
+ const { firstK, lastK, sharedCount } = boundary;
15422
+ const N = xRing.length;
15423
+ const M = yRing.length;
15424
+ const A = xRing[firstK];
15425
+ const B = xRing[(lastK + 1) % N];
15426
+ let mA = -1;
15427
+ for (let i = 0; i < M; i++) {
15428
+ if (yRing[i] === A) {
15429
+ mA = i;
15430
+ break;
15431
+ }
15432
+ }
15433
+ if (mA === -1) return false;
15434
+ const mB = (mA - sharedCount + M) % M;
15435
+ if (yRing[mB] !== B) return false;
15436
+ const prevX = pts[xRing[(firstK - 1 + N) % N]];
15437
+ const pA = pts[A];
15438
+ const nextY = pts[yRing[(mA + 1) % M]];
15439
+ if (cross({ o: prevX, a: pA, b: nextY }) < -1e-8) return false;
15440
+ const prevY = pts[yRing[(mB - 1 + M) % M]];
15441
+ const pB = pts[B];
15442
+ const nextX = pts[xRing[(lastK + 2) % N]];
15443
+ if (cross({ o: prevY, a: pB, b: nextX }) < -1e-8) return false;
15444
+ return true;
15445
+ }
15446
+ function doMerge(xIdx, yIdx) {
15447
+ const xRing = cells[xIdx];
15448
+ const yRing = cells[yIdx];
15449
+ const boundary = findBoundary(xIdx, yIdx);
15450
+ const { firstK, lastK, sharedCount } = boundary;
15451
+ const N = xRing.length;
15452
+ const M = yRing.length;
15453
+ const A = xRing[firstK];
15454
+ const B = xRing[(lastK + 1) % N];
15455
+ let mA = -1;
15456
+ for (let i = 0; i < M; i++) {
15457
+ if (yRing[i] === A) {
15458
+ mA = i;
15459
+ break;
15460
+ }
15461
+ }
15462
+ const newRing = [];
15463
+ newRing.push(A);
15464
+ const yNonShared = M - sharedCount - 1;
15465
+ for (let j = 1; j <= yNonShared; j++) {
15466
+ newRing.push(yRing[(mA + j) % M]);
15467
+ }
15468
+ newRing.push(B);
15469
+ const xNonShared = N - sharedCount - 1;
15470
+ for (let j = 2; j <= xNonShared + 1; j++) {
15471
+ newRing.push(xRing[(lastK + j) % N]);
15472
+ }
15473
+ cells[xIdx] = newRing;
15474
+ cells[yIdx] = null;
15475
+ }
15476
+ let changed = true;
15477
+ while (changed) {
15478
+ changed = false;
15479
+ rebuildAdj();
15480
+ for (let i = 0; i < cells.length; i++) {
15481
+ if (!cells[i]) continue;
15482
+ const iAdj = adj[i];
15483
+ const neighbors = /* @__PURE__ */ new Set();
15484
+ for (const [, entry] of iAdj) {
15485
+ if (cells[entry.neighbor]) neighbors.add(entry.neighbor);
15486
+ }
15487
+ if (neighbors.size === 1) {
15488
+ const nIdx = neighbors.values().next().value;
15489
+ if (canMerge(i, nIdx)) {
15490
+ doMerge(i, nIdx);
15491
+ changed = true;
15492
+ }
15493
+ }
15494
+ }
15495
+ }
15496
+ rebuildAdj();
15497
+ const areas = new Array(cells.length).fill(0);
15498
+ for (let i = 0; i < cells.length; i++) {
15499
+ if (cells[i]) areas[i] = cellArea(cells[i]);
15500
+ }
15501
+ const heap = [];
15502
+ function heapPush(e) {
15503
+ heap.push(e);
15504
+ let i = heap.length - 1;
15505
+ while (i > 0) {
15506
+ const p = i - 1 >> 1;
15507
+ if (heap[p].area >= heap[i].area) break;
15508
+ const t = heap[p];
15509
+ heap[p] = heap[i];
15510
+ heap[i] = t;
15511
+ i = p;
15512
+ }
15513
+ }
15514
+ function heapPop() {
15515
+ if (heap.length === 0) return void 0;
15516
+ const top = heap[0];
15517
+ const last = heap.pop();
15518
+ if (heap.length > 0) {
15519
+ heap[0] = last;
15520
+ let i = 0;
15521
+ while (true) {
15522
+ let big = i;
15523
+ const l = 2 * i + 1;
15524
+ const r = 2 * i + 2;
15525
+ if (l < heap.length && heap[l].area > heap[big].area) big = l;
15526
+ if (r < heap.length && heap[r].area > heap[big].area) big = r;
15527
+ if (big === i) break;
15528
+ const t = heap[i];
15529
+ heap[i] = heap[big];
15530
+ heap[big] = t;
15531
+ i = big;
15532
+ }
15533
+ }
15534
+ return top;
15535
+ }
15536
+ const seenPairs = /* @__PURE__ */ new Set();
15537
+ for (let i = 0; i < cells.length; i++) {
15538
+ if (!cells[i]) continue;
15539
+ const iAdj = adj[i];
15540
+ const neighbors = /* @__PURE__ */ new Set();
15541
+ for (const [, entry] of iAdj) {
15542
+ if (cells[entry.neighbor]) neighbors.add(entry.neighbor);
15543
+ }
15544
+ for (const nIdx of neighbors) {
15545
+ const pairKey = i < nIdx ? i * 1e5 + nIdx : nIdx * 1e5 + i;
15546
+ if (seenPairs.has(pairKey)) continue;
15547
+ seenPairs.add(pairKey);
15548
+ if (canMerge(i, nIdx)) {
15549
+ heapPush({ area: areas[i] + areas[nIdx], xIdx: i, yIdx: nIdx });
15550
+ }
15551
+ }
15552
+ }
15553
+ while (heap.length > 0) {
15554
+ const entry = heapPop();
15555
+ const { xIdx, yIdx } = entry;
15556
+ if (!cells[xIdx] || !cells[yIdx]) continue;
15557
+ const currentArea = areas[xIdx] + areas[yIdx];
15558
+ if (Math.abs(currentArea - entry.area) > 1e-10) {
15559
+ if (canMerge(xIdx, yIdx)) {
15560
+ heapPush({ area: currentArea, xIdx, yIdx });
15561
+ }
15562
+ continue;
15563
+ }
15564
+ if (!canMerge(xIdx, yIdx)) continue;
15565
+ doMerge(xIdx, yIdx);
15566
+ areas[xIdx] = cellArea(cells[xIdx]);
15567
+ rebuildAdj();
15568
+ const xAdj = adj[xIdx];
15569
+ const neighbors = /* @__PURE__ */ new Set();
15570
+ for (const [, e] of xAdj) {
15571
+ if (cells[e.neighbor]) neighbors.add(e.neighbor);
15572
+ }
15573
+ for (const nIdx of neighbors) {
15574
+ if (canMerge(xIdx, nIdx)) {
15575
+ heapPush({ area: areas[xIdx] + areas[nIdx], xIdx, yIdx: nIdx });
15576
+ }
15577
+ }
15578
+ }
15579
+ const liveCells = cells.filter((c) => c !== null);
15580
+ const depths = liveCells.map(() => 0);
15581
+ return { cells: liveCells, depths };
15582
+ };
15263
15583
  var MergeCellsSolver = class extends BaseSolver2 {
15264
15584
  input;
15265
15585
  output = null;
@@ -15268,13 +15588,17 @@ var MergeCellsSolver = class extends BaseSolver2 {
15268
15588
  this.input = input;
15269
15589
  }
15270
15590
  _step() {
15271
- const merged = mergeCells({
15591
+ const merged = this.input.usePolyanyaMerge !== false ? mergeCellsPolyanya({
15592
+ triangles: this.input.validTris,
15593
+ pts: this.input.pts
15594
+ }) : mergeCells({
15272
15595
  triangles: this.input.validTris,
15273
15596
  pts: this.input.pts,
15274
15597
  concavityTolerance: this.input.concavityTolerance
15275
15598
  });
15276
15599
  this.output = {
15277
15600
  pts: this.input.pts,
15601
+ bounds: this.input.bounds,
15278
15602
  validTris: this.input.validTris,
15279
15603
  cells: merged.cells,
15280
15604
  depths: merged.depths
@@ -15430,25 +15754,30 @@ var isPointInOrNearPolygon = (params) => {
15430
15754
  var inFreeSpace = (params) => {
15431
15755
  const { px, py, bounds, vias, clearance, rects, polygons } = params;
15432
15756
  const { minX, maxX, minY, maxY } = bounds;
15433
- if (px < minX - 0.1 || px > maxX + 0.1 || py < minY - 0.1 || py > maxY + 0.1) {
15757
+ const maxDim = Math.max(maxX - minX, maxY - minY, 1);
15758
+ const eps = maxDim * 1e-6;
15759
+ if (px < minX - eps || px > maxX + eps || py < minY - eps || py > maxY + eps) {
15434
15760
  return false;
15435
15761
  }
15436
15762
  for (const via of vias) {
15437
15763
  const radius = via.diameter / 2 + clearance;
15438
- if ((px - via.center.x) ** 2 + (py - via.center.y) ** 2 < radius * radius - 0.1) {
15764
+ const effectiveRadius = Math.max(0, radius - eps);
15765
+ if ((px - via.center.x) ** 2 + (py - via.center.y) ** 2 < effectiveRadius * effectiveRadius) {
15439
15766
  return false;
15440
15767
  }
15441
15768
  }
15442
15769
  for (const rect of rects) {
15443
15770
  const halfWidth = rect.width / 2 + clearance;
15444
15771
  const halfHeight = rect.height / 2 + clearance;
15772
+ const effectiveHalfWidth = Math.max(0, halfWidth - eps);
15773
+ const effectiveHalfHeight = Math.max(0, halfHeight - eps);
15445
15774
  const dx = px - rect.center.x;
15446
15775
  const dy = py - rect.center.y;
15447
15776
  const cosTheta = Math.cos(rect.ccwRotation);
15448
15777
  const sinTheta = Math.sin(rect.ccwRotation);
15449
15778
  const localX = dx * cosTheta + dy * sinTheta;
15450
15779
  const localY = -dx * sinTheta + dy * cosTheta;
15451
- if (Math.abs(localX) < halfWidth - 0.1 && Math.abs(localY) < halfHeight - 0.1) {
15780
+ if (Math.abs(localX) < effectiveHalfWidth && Math.abs(localY) < effectiveHalfHeight) {
15452
15781
  return false;
15453
15782
  }
15454
15783
  }
@@ -15526,7 +15855,8 @@ var TriangulateSolver = class extends BaseSolver2 {
15526
15855
  this.input.pts,
15527
15856
  this.input.constraintEdges
15528
15857
  );
15529
- validTris = this.input.hadCrossings ? filterTris({
15858
+ const hasObstacles = vias.length > 0 || rects.length > 0 || polygons.length > 0;
15859
+ validTris = hasObstacles ? filterTris({
15530
15860
  triangles: cdtTris,
15531
15861
  pts: this.input.pts,
15532
15862
  bounds: this.input.bounds,
@@ -15549,6 +15879,7 @@ var TriangulateSolver = class extends BaseSolver2 {
15549
15879
  }
15550
15880
  this.output = {
15551
15881
  pts: this.input.pts,
15882
+ bounds: this.input.bounds,
15552
15883
  validTris
15553
15884
  };
15554
15885
  this.stats = {
@@ -15622,8 +15953,10 @@ var ConvexRegionsSolver = class extends BasePipelineSolver {
15622
15953
  return [
15623
15954
  {
15624
15955
  pts: triangulated.pts,
15956
+ bounds: triangulated.bounds,
15625
15957
  validTris: triangulated.validTris,
15626
- concavityTolerance: instance.inputProblem.concavityTolerance
15958
+ concavityTolerance: instance.inputProblem.concavityTolerance,
15959
+ usePolyanyaMerge: instance.inputProblem.usePolyanyaMerge
15627
15960
  }
15628
15961
  ];
15629
15962
  }),
@@ -15644,16 +15977,24 @@ var ConvexRegionsSolver = class extends BasePipelineSolver {
15644
15977
  if (!result) {
15645
15978
  return { points: [], lines: [], rects: [], circles: [], texts: [] };
15646
15979
  }
15980
+ const boundsWidth = this.inputProblem.bounds.maxX - this.inputProblem.bounds.minX;
15981
+ const boundsHeight = this.inputProblem.bounds.maxY - this.inputProblem.bounds.minY;
15982
+ const maxDim = Math.max(boundsWidth, boundsHeight, Number.EPSILON);
15983
+ const targetVisualSize = 400;
15984
+ const displayScale = Math.max(1, targetVisualSize / maxDim);
15985
+ const displayOffsetX = -this.inputProblem.bounds.minX * displayScale;
15986
+ const displayOffsetY = -this.inputProblem.bounds.minY * displayScale;
15987
+ const toDisplayPoint = (point4) => ({
15988
+ x: point4.x * displayScale + displayOffsetX,
15989
+ y: point4.y * displayScale + displayOffsetY
15990
+ });
15647
15991
  const polygonLines = (this.inputProblem.polygons ?? []).flatMap(
15648
15992
  (polygon2) => {
15649
15993
  const pts = polygon2.points;
15650
15994
  return pts.map((p, i) => {
15651
15995
  const next = pts[(i + 1) % pts.length];
15652
15996
  return {
15653
- points: [
15654
- { x: p.x, y: p.y },
15655
- { x: next.x, y: next.y }
15656
- ],
15997
+ points: [toDisplayPoint(p), toDisplayPoint(next)],
15657
15998
  strokeColor: "#ff9f43",
15658
15999
  strokeWidth: 2
15659
16000
  };
@@ -15677,10 +16018,7 @@ var ConvexRegionsSolver = class extends BasePipelineSolver {
15677
16018
  return corners.map((p, i) => {
15678
16019
  const next = corners[(i + 1) % corners.length];
15679
16020
  return {
15680
- points: [
15681
- { x: p.x, y: p.y },
15682
- { x: next.x, y: next.y }
15683
- ],
16021
+ points: [toDisplayPoint(p), toDisplayPoint(next)],
15684
16022
  strokeColor: "#ff6b6b",
15685
16023
  strokeWidth: 2
15686
16024
  };
@@ -15688,8 +16026,7 @@ var ConvexRegionsSolver = class extends BasePipelineSolver {
15688
16026
  });
15689
16027
  return {
15690
16028
  points: result.pts.map((pt) => ({
15691
- x: pt.x,
15692
- y: pt.y,
16029
+ ...toDisplayPoint(pt),
15693
16030
  color: "#38b6ff"
15694
16031
  })),
15695
16032
  lines: [
@@ -15697,10 +16034,7 @@ var ConvexRegionsSolver = class extends BasePipelineSolver {
15697
16034
  (region) => region.map((p, i) => {
15698
16035
  const next = region[(i + 1) % region.length] ?? p;
15699
16036
  return {
15700
- points: [
15701
- { x: p.x, y: p.y },
15702
- { x: next.x, y: next.y }
15703
- ],
16037
+ points: [toDisplayPoint(p), toDisplayPoint(next)],
15704
16038
  strokeColor: "#4ecb82"
15705
16039
  };
15706
16040
  })
@@ -15710,14 +16044,14 @@ var ConvexRegionsSolver = class extends BasePipelineSolver {
15710
16044
  ],
15711
16045
  rects: [],
15712
16046
  circles: (this.inputProblem.vias ?? []).map((via) => ({
15713
- center: { x: via.center.x, y: via.center.y },
15714
- radius: via.diameter / 2,
16047
+ center: toDisplayPoint(via.center),
16048
+ radius: via.diameter / 2 * displayScale,
15715
16049
  stroke: "#ff6b6b"
15716
16050
  })),
15717
16051
  texts: [
15718
16052
  {
15719
- x: this.inputProblem.bounds.minX + 8,
15720
- y: this.inputProblem.bounds.minY + 16,
16053
+ x: 8,
16054
+ y: 16,
15721
16055
  text: `regions=${result.regions.length}`,
15722
16056
  color: "#ffffff"
15723
16057
  }
@@ -17549,25 +17883,18 @@ var extractXYConnectionsFromSample = (sample) => {
17549
17883
  }
17550
17884
  return xyConnections;
17551
17885
  };
17552
- var predictViaRegionFromGraphPattern = (input) => {
17553
- const area = Math.max(input.graphWidthMm * input.graphHeightMm, 0.01);
17554
- const density = input.connectionCount / area;
17555
- if (density <= 0.032343248633284985) {
17556
- if (input.connectionCount <= 11.5) {
17557
- if (input.connectionCount === 8 && input.intersectionCount >= 17 && input.graphWidthMm <= 15 && input.graphHeightMm >= 20) {
17558
- return "via-tile-5-regions";
17559
- }
17560
- return "via-tile-6-regions";
17561
- }
17562
- return "via-tile-3-regions";
17563
- }
17564
- if (input.graphHeightMm <= 12.600000000000001) {
17565
- return "via-tile-3-regions";
17566
- }
17567
- if (input.graphWidthMm <= 13.41) {
17886
+ var predictViaRegionFromSolveMatrix = (input) => {
17887
+ const area = input.graphWidthMm * input.graphHeightMm;
17888
+ if (input.graphWidthMm > 22) {
17568
17889
  return "via-tile-4-regions";
17569
17890
  }
17570
- return "via-tile-5-regions";
17891
+ if (input.intersectionCount <= 20) {
17892
+ if (input.intersectionCount <= 17) {
17893
+ return "via-tile-5-regions";
17894
+ }
17895
+ return input.graphWidthMm <= 12.6 ? "via-tile-3-regions" : "via-tile-4-regions";
17896
+ }
17897
+ return area <= 246.01 ? "via-tile-3-regions" : "via-tile-5-regions";
17571
17898
  };
17572
17899
  var getViaTileBounds = (viaTile) => {
17573
17900
  const points = [];
@@ -17605,7 +17932,7 @@ var scoreViaTileForProblem = (problem, candidate) => {
17605
17932
  const crossingPerConnection = problem.intersectionCount / Math.max(problem.connectionCount, 1);
17606
17933
  const density = problem.connectionCount / graphArea;
17607
17934
  const requiredCapacityScore = problem.connectionCount * 1.2 + problem.intersectionCount * 1.5 + crossingPerConnection * 5 + density * 40;
17608
- const capacityScore = viaCount * 3.1 + routeSegmentCount * 2.4 + netCount * 4 + 1 / tileArea * 4.5;
17935
+ const capacityScore = viaCount * 3.1 + routeSegmentCount * 2.4 + netCount * 0.5 + 1 / tileArea * 4.5;
17609
17936
  const predictedReliability = clamp4(
17610
17937
  sigmoid((capacityScore - requiredCapacityScore) / 3.2),
17611
17938
  0,
@@ -17711,24 +18038,21 @@ var recommendViaTileFromGraphInput = (problemInput, fallbackXYConnections = [],
17711
18038
  }
17712
18039
  return a.estimatedIterationCost - b.estimatedIterationCost;
17713
18040
  });
17714
- const predictedViaRegionName = predictViaRegionFromGraphPattern(normalizedInput);
17715
- if (predictedViaRegionName) {
17716
- const predictedCandidate = scored.find(
17717
- (candidate) => candidate.viaRegionName === predictedViaRegionName
17718
- );
17719
- if (predictedCandidate) {
17720
- const reordered = [
18041
+ const predictedViaRegionName = predictViaRegionFromSolveMatrix(normalizedInput);
18042
+ const predictedCandidate = scored.find(
18043
+ (candidate) => candidate.viaRegionName === predictedViaRegionName
18044
+ );
18045
+ if (predictedCandidate) {
18046
+ return {
18047
+ recommendedViaRegionName: predictedCandidate.viaRegionName,
18048
+ inputFeatures: normalizedInput,
18049
+ candidates: [
17721
18050
  predictedCandidate,
17722
18051
  ...scored.filter(
17723
18052
  (candidate) => candidate.viaRegionName !== predictedViaRegionName
17724
18053
  )
17725
- ];
17726
- return {
17727
- recommendedViaRegionName: reordered[0].viaRegionName,
17728
- inputFeatures: normalizedInput,
17729
- candidates: reordered
17730
- };
17731
- }
18054
+ ]
18055
+ };
17732
18056
  }
17733
18057
  return {
17734
18058
  recommendedViaRegionName: scored[0].viaRegionName,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/hypergraph",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.54",
4
+ "version": "0.0.55",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -16,7 +16,7 @@
16
16
  "repository": "https://github.com/tscircuit/hypergraph",
17
17
  "devDependencies": {
18
18
  "@biomejs/biome": "^2.3.11",
19
- "@tscircuit/find-convex-regions": "^0.0.9",
19
+ "@tscircuit/find-convex-regions": "^0.1.3",
20
20
  "@tscircuit/jumper-topology-generator": "^0.0.4",
21
21
  "@tscircuit/math-utils": "^0.0.29",
22
22
  "@types/bun": "latest",