@tscircuit/hypergraph 0.0.53 → 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.
- package/dist/index.js +389 -65
- 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
|
|
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:
|
|
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((
|
|
14746
|
+
(region) => region.flatMap((point4, i) => {
|
|
14737
14747
|
const next = region[(i + 1) % region.length];
|
|
14738
14748
|
if (!next) return [];
|
|
14739
|
-
return [{ points: [
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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) <
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
15720
|
-
y:
|
|
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
|
|
17553
|
-
const area =
|
|
17554
|
-
|
|
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
|
-
|
|
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 *
|
|
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 =
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
|
|
17718
|
-
|
|
17719
|
-
|
|
17720
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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",
|