@tscircuit/hypergraph 0.0.46 → 0.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1017,6 +1017,7 @@ declare class ViaGraphSolver extends HyperGraphSolver<JRegion, JPort> {
1017
1017
  estimateCostToEnd(port: JPort): number;
1018
1018
  getPortUsagePenalty(port: JPort): number;
1019
1019
  computeIncreasedRegionCostIfPortsAreUsed(region: JRegion, port1: JPort, port2: JPort): number;
1020
+ isRipRequiredForPortUsage(region: JRegion, _port1: JPort, _port2: JPort): boolean;
1020
1021
  getRipsRequiredForPortUsage(region: JRegion, port1: JPort, port2: JPort): RegionPortAssignment[];
1021
1022
  routeSolvedHook(solvedRoute: SolvedRoute): void;
1022
1023
  getSolvedRoutePoints(solvedRoute: SolvedRoute): Array<{
package/dist/index.js CHANGED
@@ -5815,6 +5815,34 @@ function lineSegmentsIntersect(p1, p2, p3, p4, eps = 1e-9) {
5815
5815
  if (d1 * d2 < 0 && d3 * d4 < 0) {
5816
5816
  return true;
5817
5817
  }
5818
+ const pointOnSegment = (point2, segStart, segEnd, crossProduct) => {
5819
+ if (Math.abs(crossProduct) > eps) return false;
5820
+ const minX = Math.min(segStart.x, segEnd.x) - eps;
5821
+ const maxX = Math.max(segStart.x, segEnd.x) + eps;
5822
+ const minY = Math.min(segStart.y, segEnd.y) - eps;
5823
+ const maxY = Math.max(segStart.y, segEnd.y) + eps;
5824
+ return point2.x >= minX && point2.x <= maxX && point2.y >= minY && point2.y <= maxY;
5825
+ };
5826
+ if (pointOnSegment(p1, p3, p4, d1)) {
5827
+ if (!pointsCoincident(p1, p3) && !pointsCoincident(p1, p4)) {
5828
+ return true;
5829
+ }
5830
+ }
5831
+ if (pointOnSegment(p2, p3, p4, d2)) {
5832
+ if (!pointsCoincident(p2, p3) && !pointsCoincident(p2, p4)) {
5833
+ return true;
5834
+ }
5835
+ }
5836
+ if (pointOnSegment(p3, p1, p2, d3)) {
5837
+ if (!pointsCoincident(p3, p1) && !pointsCoincident(p3, p2)) {
5838
+ return true;
5839
+ }
5840
+ }
5841
+ if (pointOnSegment(p4, p1, p2, d4)) {
5842
+ if (!pointsCoincident(p4, p1) && !pointsCoincident(p4, p2)) {
5843
+ return true;
5844
+ }
5845
+ }
5818
5846
  return false;
5819
5847
  }
5820
5848
  function chordsIntersect(newChord, existingChord, perimeter, newPort1, newPort2, existingPort1, existingPort2) {
@@ -6300,6 +6328,15 @@ var ViaGraphSolver = class extends HyperGraphSolver {
6300
6328
  );
6301
6329
  return crossings * this.crossingPenalty + crossings * this.crossingPenaltySq;
6302
6330
  }
6331
+ isRipRequiredForPortUsage(region, _port1, _port2) {
6332
+ if (region.d.isViaRegion) {
6333
+ const assignments = region.assignments ?? [];
6334
+ return assignments.some(
6335
+ (a) => a.connection.mutuallyConnectedNetworkId !== this.currentConnection.mutuallyConnectedNetworkId
6336
+ );
6337
+ }
6338
+ return false;
6339
+ }
6303
6340
  getRipsRequiredForPortUsage(region, port1, port2) {
6304
6341
  if (region.d.isViaRegion) {
6305
6342
  const assignments = region.assignments ?? [];
@@ -6356,13 +6393,16 @@ function closestPointOnPolygonEdge(point2, polygon2) {
6356
6393
  }
6357
6394
  return { ...bestPoint, dist: bestDist };
6358
6395
  }
6359
- var findBoundaryRegionForPolygons = (x, y, regions) => {
6396
+ var findBoundaryRegionForPolygons = (params) => {
6397
+ const { x, y, regions } = params;
6398
+ const hasFillerRegions = regions.some((r) => r.regionId.startsWith("filler:"));
6360
6399
  let closestRegion = null;
6361
6400
  let closestDistance = Infinity;
6362
6401
  let closestPortPosition = { x, y };
6363
6402
  for (const region of regions) {
6364
6403
  if (region.d.isPad || region.d.isThroughJumper || region.d.isConnectionRegion)
6365
6404
  continue;
6405
+ if (hasFillerRegions && !region.regionId.startsWith("filler:")) continue;
6366
6406
  const polygon2 = region.d.polygon;
6367
6407
  if (!polygon2 || polygon2.length < 3) continue;
6368
6408
  const result = closestPointOnPolygonEdge({ x, y }, polygon2);
@@ -6397,11 +6437,11 @@ var createViaGraphWithConnections = (baseGraph, xyConnections) => {
6397
6437
  end.y
6398
6438
  );
6399
6439
  regions.push(endRegion);
6400
- const startBoundary = findBoundaryRegionForPolygons(
6401
- start.x,
6402
- start.y,
6403
- baseGraph.regions
6404
- );
6440
+ const startBoundary = findBoundaryRegionForPolygons({
6441
+ x: start.x,
6442
+ y: start.y,
6443
+ regions: baseGraph.regions
6444
+ });
6405
6445
  if (startBoundary) {
6406
6446
  const startPort = createConnectionPort(
6407
6447
  `conn:${connectionId}:start-port`,
@@ -6411,11 +6451,11 @@ var createViaGraphWithConnections = (baseGraph, xyConnections) => {
6411
6451
  );
6412
6452
  ports.push(startPort);
6413
6453
  }
6414
- const endBoundary = findBoundaryRegionForPolygons(
6415
- end.x,
6416
- end.y,
6417
- baseGraph.regions
6418
- );
6454
+ const endBoundary = findBoundaryRegionForPolygons({
6455
+ x: end.x,
6456
+ y: end.y,
6457
+ regions: baseGraph.regions
6458
+ });
6419
6459
  if (endBoundary) {
6420
6460
  const endPort = createConnectionPort(
6421
6461
  `conn:${connectionId}:end-port`,
@@ -15916,6 +15956,34 @@ function rectPolygonFromBounds(b) {
15916
15956
  { x: b.minX, y: b.maxY }
15917
15957
  ];
15918
15958
  }
15959
+ function findViaRegionSidePorts(polygon2) {
15960
+ if (polygon2.length < 3) {
15961
+ return { top: null, bottom: null, left: null, right: null };
15962
+ }
15963
+ const bounds = boundsFromPolygon(polygon2);
15964
+ const tolerance = 1e-3;
15965
+ let topPort = null;
15966
+ let bottomPort = null;
15967
+ let leftPort = null;
15968
+ let rightPort = null;
15969
+ for (let i = 0; i < polygon2.length; i++) {
15970
+ const p1 = polygon2[i];
15971
+ const p2 = polygon2[(i + 1) % polygon2.length];
15972
+ if (Math.abs(p1.y - bounds.maxY) < tolerance && Math.abs(p2.y - bounds.maxY) < tolerance) {
15973
+ topPort = { x: (p1.x + p2.x) / 2, y: bounds.maxY };
15974
+ }
15975
+ if (Math.abs(p1.y - bounds.minY) < tolerance && Math.abs(p2.y - bounds.minY) < tolerance) {
15976
+ bottomPort = { x: (p1.x + p2.x) / 2, y: bounds.minY };
15977
+ }
15978
+ if (Math.abs(p1.x - bounds.minX) < tolerance && Math.abs(p2.x - bounds.minX) < tolerance) {
15979
+ leftPort = { x: bounds.minX, y: (p1.y + p2.y) / 2 };
15980
+ }
15981
+ if (Math.abs(p1.x - bounds.maxX) < tolerance && Math.abs(p2.x - bounds.maxX) < tolerance) {
15982
+ rightPort = { x: bounds.maxX, y: (p1.y + p2.y) / 2 };
15983
+ }
15984
+ }
15985
+ return { top: topPort, bottom: bottomPort, left: leftPort, right: rightPort };
15986
+ }
15919
15987
  function extendViaRegionToTileEdge(polygon2, tileBounds, threshold = 0.1) {
15920
15988
  if (polygon2.length === 0) return polygon2;
15921
15989
  const polyBounds = boundsFromPolygon(polygon2);
@@ -16391,24 +16459,120 @@ function generateConvexViaTopologyRegions(opts) {
16391
16459
  }
16392
16460
  }
16393
16461
  }
16462
+ const fillerPortPositions = /* @__PURE__ */ new Map();
16394
16463
  for (const fillerRegion of fillerRegions) {
16464
+ fillerPortPositions.set(fillerRegion.regionId, []);
16465
+ const fillerBounds = fillerRegion.d.bounds;
16466
+ const stripWidth = fillerBounds.maxX - fillerBounds.minX;
16467
+ const stripHeight = fillerBounds.maxY - fillerBounds.minY;
16468
+ const isHorizontalStrip = stripWidth > stripHeight;
16469
+ const stripSize = isHorizontalStrip ? stripWidth : stripHeight;
16470
+ const numPorts = Math.max(1, Math.floor(stripSize / portPitch));
16471
+ const actualPitch = stripSize / numPorts;
16472
+ const adjacencyTolerance = clearance * 2;
16473
+ const isTopFiller = fillerRegion.regionId.startsWith("filler:top:");
16474
+ const isBottomFiller = fillerRegion.regionId.startsWith("filler:bottom:");
16475
+ const isLeftFiller = fillerRegion.regionId.startsWith("filler:left:");
16476
+ const isRightFiller = fillerRegion.regionId.startsWith("filler:right:");
16395
16477
  const tileRegions = [...convexRegions, ...viaRegions];
16396
16478
  for (const tileRegion of tileRegions) {
16397
- const sharedEdges = findSharedEdges(
16398
- tileRegion.d.polygon,
16399
- fillerRegion.d.polygon,
16400
- clearance * 2
16401
- );
16402
- for (const edge of sharedEdges) {
16403
- const portPositions = createPortsAlongEdge(edge, portPitch);
16404
- for (const pos of portPositions) {
16405
- createPort(
16406
- `filler:${tileRegion.regionId}-${fillerRegion.regionId}:${portIdCounter++}`,
16407
- tileRegion,
16408
- fillerRegion,
16409
- pos
16479
+ const tileBounds = tileRegion.d.bounds;
16480
+ const eps = 1e-3;
16481
+ let isAdjacent = false;
16482
+ let edgeX = null;
16483
+ let edgeY = null;
16484
+ if (isHorizontalStrip) {
16485
+ const overlapMinX = Math.max(fillerBounds.minX, tileBounds.minX);
16486
+ const overlapMaxX = Math.min(fillerBounds.maxX, tileBounds.maxX);
16487
+ const hasXOverlap = overlapMaxX > overlapMinX + eps;
16488
+ if (hasXOverlap) {
16489
+ if (isTopFiller) {
16490
+ isAdjacent = Math.abs(tileBounds.maxY - fillerBounds.minY) < adjacencyTolerance;
16491
+ edgeY = fillerBounds.minY;
16492
+ } else if (isBottomFiller) {
16493
+ isAdjacent = Math.abs(tileBounds.minY - fillerBounds.maxY) < adjacencyTolerance;
16494
+ edgeY = fillerBounds.maxY;
16495
+ }
16496
+ }
16497
+ } else {
16498
+ const overlapMinY = Math.max(fillerBounds.minY, tileBounds.minY);
16499
+ const overlapMaxY = Math.min(fillerBounds.maxY, tileBounds.maxY);
16500
+ const hasYOverlap = overlapMaxY > overlapMinY + eps;
16501
+ if (hasYOverlap) {
16502
+ if (isLeftFiller) {
16503
+ isAdjacent = Math.abs(tileBounds.minX - fillerBounds.maxX) < adjacencyTolerance;
16504
+ edgeX = fillerBounds.maxX;
16505
+ } else if (isRightFiller) {
16506
+ isAdjacent = Math.abs(tileBounds.maxX - fillerBounds.minX) < adjacencyTolerance;
16507
+ edgeX = fillerBounds.minX;
16508
+ }
16509
+ }
16510
+ }
16511
+ if (!isAdjacent) continue;
16512
+ let overlapMin;
16513
+ let overlapMax;
16514
+ if (isHorizontalStrip) {
16515
+ overlapMin = Math.max(fillerBounds.minX, tileBounds.minX);
16516
+ overlapMax = Math.min(fillerBounds.maxX, tileBounds.maxX);
16517
+ } else {
16518
+ overlapMin = Math.max(fillerBounds.minY, tileBounds.minY);
16519
+ overlapMax = Math.min(fillerBounds.maxY, tileBounds.maxY);
16520
+ }
16521
+ const overlapSize = overlapMax - overlapMin;
16522
+ if (overlapSize < eps) continue;
16523
+ const overlapNumPorts = Math.max(1, Math.floor(overlapSize / portPitch));
16524
+ const overlapActualPitch = overlapSize / overlapNumPorts;
16525
+ for (let i = 0; i < overlapNumPorts; i++) {
16526
+ let pos;
16527
+ if (isHorizontalStrip) {
16528
+ const x = overlapMin + (i + 0.5) * overlapActualPitch;
16529
+ pos = { x, y: edgeY };
16530
+ } else {
16531
+ const y = overlapMin + (i + 0.5) * overlapActualPitch;
16532
+ pos = { x: edgeX, y };
16533
+ }
16534
+ if (tileRegion.d.polygon) {
16535
+ let testPoint;
16536
+ if (isTopFiller) {
16537
+ const gap = fillerBounds.minY - tileBounds.maxY;
16538
+ const testOffset = gap + 0.01;
16539
+ testPoint = { x: pos.x, y: pos.y - testOffset };
16540
+ } else if (isBottomFiller) {
16541
+ const gap = tileBounds.minY - fillerBounds.maxY;
16542
+ const testOffset = gap + 0.01;
16543
+ testPoint = { x: pos.x, y: pos.y + testOffset };
16544
+ } else if (isLeftFiller) {
16545
+ const gap = tileBounds.minX - fillerBounds.maxX;
16546
+ const testOffset = gap + 0.01;
16547
+ testPoint = { x: pos.x + testOffset, y: pos.y };
16548
+ } else {
16549
+ const gap = fillerBounds.minX - tileBounds.maxX;
16550
+ const testOffset = gap + 0.01;
16551
+ testPoint = { x: pos.x - testOffset, y: pos.y };
16552
+ }
16553
+ if (!pointInPolygon(testPoint, tileRegion.d.polygon)) {
16554
+ continue;
16555
+ }
16556
+ }
16557
+ const existingPositions = fillerPortPositions.get(
16558
+ fillerRegion.regionId
16559
+ );
16560
+ const tooClose = existingPositions.some((existing) => {
16561
+ const dist = Math.sqrt(
16562
+ (pos.x - existing.x) ** 2 + (pos.y - existing.y) ** 2
16410
16563
  );
16564
+ return dist < portPitch;
16565
+ });
16566
+ if (tooClose) {
16567
+ continue;
16411
16568
  }
16569
+ existingPositions.push(pos);
16570
+ createPort(
16571
+ `filler:${tileRegion.regionId}-${fillerRegion.regionId}:${portIdCounter++}`,
16572
+ tileRegion,
16573
+ fillerRegion,
16574
+ pos
16575
+ );
16412
16576
  }
16413
16577
  }
16414
16578
  }
@@ -16425,10 +16589,20 @@ function generateConvexViaTopologyRegions(opts) {
16425
16589
  const edgeLength = Math.sqrt(
16426
16590
  (edge.to.x - edge.from.x) ** 2 + (edge.to.y - edge.from.y) ** 2
16427
16591
  );
16428
- if (edgeLength < portPitch) {
16592
+ if (edgeLength < 0.01) {
16429
16593
  continue;
16430
16594
  }
16431
- const portPositions = createPortsAlongEdge(edge, portPitch);
16595
+ let portPositions;
16596
+ if (edgeLength < portPitch) {
16597
+ portPositions = [
16598
+ {
16599
+ x: (edge.from.x + edge.to.x) / 2,
16600
+ y: (edge.from.y + edge.to.y) / 2
16601
+ }
16602
+ ];
16603
+ } else {
16604
+ portPositions = createPortsAlongEdge(edge, portPitch);
16605
+ }
16432
16606
  for (const pos of portPositions) {
16433
16607
  createPort(
16434
16608
  `filler:${region1.regionId}-${region2.regionId}:${portIdCounter++}`,
@@ -16440,22 +16614,61 @@ function generateConvexViaTopologyRegions(opts) {
16440
16614
  }
16441
16615
  }
16442
16616
  }
16617
+ const nonViaRegions = allRegions.filter((r) => !r.d.isViaRegion);
16618
+ const createdViaSidePorts = /* @__PURE__ */ new Set();
16443
16619
  for (const viaRegion of viaRegions) {
16444
- for (const convexRegion of convexRegions) {
16445
- const sharedEdges = findSharedEdges(
16446
- viaRegion.d.polygon,
16447
- convexRegion.d.polygon,
16448
- clearance * 2
16449
- );
16450
- for (const edge of sharedEdges) {
16451
- const portPositions = createPortsAlongEdge(edge, portPitch);
16452
- for (const pos of portPositions) {
16620
+ const sidePorts = findViaRegionSidePorts(viaRegion.d.polygon);
16621
+ const viaBounds = viaRegion.d.bounds;
16622
+ const sides = ["top", "bottom", "left", "right"];
16623
+ for (const side of sides) {
16624
+ const portPos = sidePorts[side];
16625
+ if (!portPos) continue;
16626
+ const sideKey = `${viaRegion.regionId}:${side}`;
16627
+ if (createdViaSidePorts.has(sideKey)) continue;
16628
+ const adjacencyTolerance = 0.2;
16629
+ const testOffset = adjacencyTolerance;
16630
+ let testPoint;
16631
+ switch (side) {
16632
+ case "top":
16633
+ testPoint = { x: portPos.x, y: viaBounds.maxY + testOffset };
16634
+ break;
16635
+ case "bottom":
16636
+ testPoint = { x: portPos.x, y: viaBounds.minY - testOffset };
16637
+ break;
16638
+ case "left":
16639
+ testPoint = { x: viaBounds.minX - testOffset, y: portPos.y };
16640
+ break;
16641
+ case "right":
16642
+ testPoint = { x: viaBounds.maxX + testOffset, y: portPos.y };
16643
+ break;
16644
+ }
16645
+ for (const adjacentRegion of nonViaRegions) {
16646
+ const adjBounds = adjacentRegion.d.bounds;
16647
+ let boundsAdjacent = false;
16648
+ switch (side) {
16649
+ case "top":
16650
+ boundsAdjacent = Math.abs(adjBounds.minY - viaBounds.maxY) < adjacencyTolerance && adjBounds.minX < portPos.x && adjBounds.maxX > portPos.x;
16651
+ break;
16652
+ case "bottom":
16653
+ boundsAdjacent = Math.abs(adjBounds.maxY - viaBounds.minY) < adjacencyTolerance && adjBounds.minX < portPos.x && adjBounds.maxX > portPos.x;
16654
+ break;
16655
+ case "left":
16656
+ boundsAdjacent = Math.abs(adjBounds.maxX - viaBounds.minX) < adjacencyTolerance && adjBounds.minY < portPos.y && adjBounds.maxY > portPos.y;
16657
+ break;
16658
+ case "right":
16659
+ boundsAdjacent = Math.abs(adjBounds.minX - viaBounds.maxX) < adjacencyTolerance && adjBounds.minY < portPos.y && adjBounds.maxY > portPos.y;
16660
+ break;
16661
+ }
16662
+ if (!boundsAdjacent) continue;
16663
+ if (adjacentRegion.d.polygon && pointInPolygon(testPoint, adjacentRegion.d.polygon)) {
16453
16664
  createPort(
16454
- `via-convex:${viaRegion.regionId}-${convexRegion.regionId}:${portIdCounter++}`,
16665
+ `via-side:${viaRegion.regionId}:${side}:${portIdCounter++}`,
16455
16666
  viaRegion,
16456
- convexRegion,
16457
- pos
16667
+ adjacentRegion,
16668
+ portPos
16458
16669
  );
16670
+ createdViaSidePorts.add(sideKey);
16671
+ break;
16459
16672
  }
16460
16673
  }
16461
16674
  }
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.46",
4
+ "version": "0.0.48",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",