@tscircuit/hypergraph 0.0.44 → 0.0.46

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 (3) hide show
  1. package/dist/index.d.ts +129 -102
  2. package/dist/index.js +644 -237
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5237,88 +5237,88 @@ var via_tile_4_regions_default = {
5237
5237
  net1: [
5238
5238
  {
5239
5239
  viaId: "14ee4bf1-e7e7-4351-ad16-ba1a8aac0af3",
5240
- diameter: 0.6,
5240
+ diameter: 0.3,
5241
5241
  position: {
5242
- x: 1.187494,
5243
- y: 0.226901
5242
+ x: 0.815244,
5243
+ y: -0.055081
5244
5244
  }
5245
5245
  },
5246
5246
  {
5247
5247
  viaId: "8a898812-db2b-4b81-899d-f953b3222f9a",
5248
- diameter: 0.6,
5248
+ diameter: 0.3,
5249
5249
  position: {
5250
- x: -0.890462,
5251
- y: 1.024371
5250
+ x: -0.528763,
5251
+ y: 0.676055
5252
5252
  }
5253
5253
  }
5254
5254
  ],
5255
5255
  net3: [
5256
5256
  {
5257
5257
  viaId: "13cd75da-d582-4eed-bc2c-fee6fa78bc6a",
5258
- diameter: 0.6,
5258
+ diameter: 0.3,
5259
5259
  position: {
5260
- x: -1.229928,
5261
- y: -1.159683
5260
+ x: -0.808222,
5261
+ y: -0.726499
5262
5262
  }
5263
5263
  },
5264
5264
  {
5265
5265
  viaId: "9f188f9d-bce8-46c7-b9e8-542b8599d48d",
5266
- diameter: 0.6,
5266
+ diameter: 0.3,
5267
5267
  position: {
5268
- x: 1.364053,
5269
- y: 1.083575
5268
+ x: 0.901143,
5269
+ y: 0.437488
5270
5270
  }
5271
5271
  }
5272
5272
  ],
5273
5273
  net4: [
5274
5274
  {
5275
5275
  viaId: "2ef3b96b-58c4-4150-b5d1-e90f74b92b25",
5276
- diameter: 0.6,
5276
+ diameter: 0.3,
5277
5277
  position: {
5278
- x: -0.134894,
5279
- y: 1.042149
5278
+ x: -0.029,
5279
+ y: 0.691542
5280
5280
  }
5281
5281
  },
5282
5282
  {
5283
5283
  viaId: "37a70467-7553-415f-8ad7-8b88a5e56ca8",
5284
- diameter: 0.6,
5284
+ diameter: 0.3,
5285
5285
  position: {
5286
- x: -1.195947,
5287
- y: 0.339403
5286
+ x: -0.824388,
5287
+ y: 0.272808
5288
5288
  }
5289
5289
  },
5290
5290
  {
5291
5291
  viaId: "6d182b7d-a260-427b-8c43-a09409422252",
5292
- diameter: 0.6,
5292
+ diameter: 0.3,
5293
5293
  position: {
5294
- x: -0.141929,
5295
- y: -1.050896
5294
+ x: 0.083422,
5295
+ y: -0.705274
5296
5296
  }
5297
5297
  }
5298
5298
  ],
5299
5299
  net5: [
5300
5300
  {
5301
5301
  viaId: "346c301a-f2b4-44d7-b975-5438ddaa0bde",
5302
- diameter: 0.6,
5302
+ diameter: 0.3,
5303
5303
  position: {
5304
- x: 0.587494,
5305
- y: -0.438741
5304
+ x: 0.517145,
5305
+ y: -0.456503
5306
5306
  }
5307
5307
  },
5308
5308
  {
5309
5309
  viaId: "c54b4b94-90e0-4240-97ef-883cf097d79d",
5310
- diameter: 0.6,
5310
+ diameter: 0.3,
5311
5311
  position: {
5312
- x: 0.614052,
5313
- y: 1.081959
5312
+ x: 0.471002,
5313
+ y: 0.692404
5314
5314
  }
5315
5315
  },
5316
5316
  {
5317
5317
  viaId: "f3cb916e-b0fd-4c5e-b14b-3779f4f9bd01",
5318
- diameter: 0.6,
5318
+ diameter: 0.3,
5319
5319
  position: {
5320
- x: -1.192929,
5321
- y: -0.410594
5320
+ x: -0.833162,
5321
+ y: -0.227118
5322
5322
  }
5323
5323
  }
5324
5324
  ]
@@ -5331,36 +5331,40 @@ var via_tile_4_regions_default = {
5331
5331
  layer: "bottom",
5332
5332
  segments: [
5333
5333
  {
5334
- x: 1.187494,
5335
- y: 0.226901
5334
+ x: 0.815244,
5335
+ y: -0.055081
5336
5336
  },
5337
5337
  {
5338
- x: 1.187494,
5339
- y: -0.582618
5338
+ x: 1.01282,
5339
+ y: -0.252657
5340
5340
  },
5341
5341
  {
5342
- x: 0.193216,
5343
- y: -1.576896
5342
+ x: 1.01282,
5343
+ y: -0.563283
5344
5344
  },
5345
5345
  {
5346
- x: -0.359806,
5347
- y: -1.576896
5346
+ x: 0.444829,
5347
+ y: -1.131274
5348
5348
  },
5349
5349
  {
5350
- x: -0.667929,
5351
- y: -1.268773
5350
+ x: -0.093033,
5351
+ y: -1.131274
5352
5352
  },
5353
5353
  {
5354
- x: -0.667929,
5355
- y: 0.561982
5354
+ x: -0.382222,
5355
+ y: -0.842085
5356
5356
  },
5357
5357
  {
5358
- x: -0.890462,
5359
- y: 0.784515
5358
+ x: -0.382222,
5359
+ y: 0.433097
5360
5360
  },
5361
5361
  {
5362
- x: -0.890462,
5363
- y: 1.024371
5362
+ x: -0.528763,
5363
+ y: 0.579638
5364
+ },
5365
+ {
5366
+ x: -0.528763,
5367
+ y: 0.676055
5364
5368
  }
5365
5369
  ]
5366
5370
  },
@@ -5371,28 +5375,36 @@ var via_tile_4_regions_default = {
5371
5375
  layer: "bottom",
5372
5376
  segments: [
5373
5377
  {
5374
- x: -1.229928,
5375
- y: -1.159683
5378
+ x: -0.808222,
5379
+ y: -0.726499
5380
+ },
5381
+ {
5382
+ x: -0.808222,
5383
+ y: -0.912474
5376
5384
  },
5377
5385
  {
5378
- x: -0.511715,
5379
- y: -1.877896
5386
+ x: -0.238422,
5387
+ y: -1.482274
5380
5388
  },
5381
5389
  {
5382
- x: 0.317894,
5383
- y: -1.877896
5390
+ x: 0.590217,
5391
+ y: -1.482274
5384
5392
  },
5385
5393
  {
5386
- x: 1.713494,
5387
- y: -0.482296
5394
+ x: 1.36382,
5395
+ y: -0.708672
5388
5396
  },
5389
5397
  {
5390
- x: 1.713494,
5391
- y: 0.734134
5398
+ x: 1.36382,
5399
+ y: -1202e-6
5392
5400
  },
5393
5401
  {
5394
- x: 1.364053,
5395
- y: 1.083575
5402
+ x: 0.92513,
5403
+ y: 0.437488
5404
+ },
5405
+ {
5406
+ x: 0.901143,
5407
+ y: 0.437488
5396
5408
  }
5397
5409
  ]
5398
5410
  },
@@ -5403,16 +5415,16 @@ var via_tile_4_regions_default = {
5403
5415
  layer: "bottom",
5404
5416
  segments: [
5405
5417
  {
5406
- x: -0.134894,
5407
- y: 1.042149
5418
+ x: -0.029,
5419
+ y: 0.691542
5408
5420
  },
5409
5421
  {
5410
- x: -0.141929,
5411
- y: 1.035114
5422
+ x: -0.029,
5423
+ y: -0.592852
5412
5424
  },
5413
5425
  {
5414
- x: -0.141929,
5415
- y: -1.050896
5426
+ x: 0.083422,
5427
+ y: -0.705274
5416
5428
  }
5417
5429
  ]
5418
5430
  },
@@ -5423,28 +5435,32 @@ var via_tile_4_regions_default = {
5423
5435
  layer: "bottom",
5424
5436
  segments: [
5425
5437
  {
5426
- x: -0.134894,
5427
- y: 1.042149
5438
+ x: -0.029,
5439
+ y: 0.691542
5440
+ },
5441
+ {
5442
+ x: -0.029,
5443
+ y: 0.778747
5428
5444
  },
5429
5445
  {
5430
- x: -0.643116,
5431
- y: 1.550371
5446
+ x: -0.352308,
5447
+ y: 1.102055
5432
5448
  },
5433
5449
  {
5434
- x: -1.108339,
5435
- y: 1.550371
5450
+ x: -0.705218,
5451
+ y: 1.102055
5436
5452
  },
5437
5453
  {
5438
- x: -1.416462,
5439
- y: 1.242248
5454
+ x: -0.954763,
5455
+ y: 0.85251
5440
5456
  },
5441
5457
  {
5442
- x: -1.416462,
5443
- y: 0.559918
5458
+ x: -0.954763,
5459
+ y: 0.403183
5444
5460
  },
5445
5461
  {
5446
- x: -1.195947,
5447
- y: 0.339403
5462
+ x: -0.824388,
5463
+ y: 0.272808
5448
5464
  }
5449
5465
  ]
5450
5466
  },
@@ -5455,20 +5471,20 @@ var via_tile_4_regions_default = {
5455
5471
  layer: "bottom",
5456
5472
  segments: [
5457
5473
  {
5458
- x: 0.587494,
5459
- y: -0.438741
5474
+ x: 0.517145,
5475
+ y: -0.456503
5460
5476
  },
5461
5477
  {
5462
- x: 0.587494,
5463
- y: 1.080547
5478
+ x: 0.389244,
5479
+ y: -0.328602
5464
5480
  },
5465
5481
  {
5466
- x: 0.588906,
5467
- y: 1.081959
5482
+ x: 0.389244,
5483
+ y: 0.610646
5468
5484
  },
5469
5485
  {
5470
- x: 0.614052,
5471
- y: 1.081959
5486
+ x: 0.471002,
5487
+ y: 0.692404
5472
5488
  }
5473
5489
  ]
5474
5490
  },
@@ -5479,34 +5495,42 @@ var via_tile_4_regions_default = {
5479
5495
  layer: "bottom",
5480
5496
  segments: [
5481
5497
  {
5482
- x: 0.614052,
5483
- y: 1.081959
5498
+ x: 0.471002,
5499
+ y: 0.692404
5500
+ },
5501
+ {
5502
+ x: 0.471002,
5503
+ y: 1.111132
5484
5504
  },
5485
5505
  {
5486
- x: -0.15536,
5487
- y: 1.851371
5506
+ x: 0.129079,
5507
+ y: 1.453055
5488
5508
  },
5489
5509
  {
5490
- x: -1.233017,
5491
- y: 1.851371
5510
+ x: -0.850606,
5511
+ y: 1.453055
5492
5512
  },
5493
5513
  {
5494
- x: -1.721947,
5495
- y: 1.362441
5514
+ x: -1.305763,
5515
+ y: 0.997899
5496
5516
  },
5497
5517
  {
5498
- x: -1.721947,
5499
- y: 0.118424
5518
+ x: -1.305763,
5519
+ y: 0.151728
5500
5520
  },
5501
5521
  {
5502
- x: -1.192929,
5503
- y: -0.410594
5522
+ x: -0.926917,
5523
+ y: -0.227118
5524
+ },
5525
+ {
5526
+ x: -0.833162,
5527
+ y: -0.227118
5504
5528
  }
5505
5529
  ]
5506
5530
  }
5507
5531
  ],
5508
- tileWidth: 3.735441,
5509
- tileHeight: 4.029267
5532
+ tileWidth: 2.9695829999999996,
5533
+ tileHeight: 3.2353289999999997
5510
5534
  };
5511
5535
 
5512
5536
  // lib/ViaGraphSolver/via-graph-generator/generateViaTopologyRegions.ts
@@ -5778,6 +5802,32 @@ function generateDefaultViaTopologyRegions(opts) {
5778
5802
  }
5779
5803
 
5780
5804
  // lib/ViaGraphSolver/polygonPerimeterUtils.ts
5805
+ function lineSegmentsIntersect(p1, p2, p3, p4, eps = 1e-9) {
5806
+ const pointsCoincident = (a, b) => Math.abs(a.x - b.x) < eps && Math.abs(a.y - b.y) < eps;
5807
+ if (pointsCoincident(p1, p3) || pointsCoincident(p1, p4) || pointsCoincident(p2, p3) || pointsCoincident(p2, p4)) {
5808
+ return false;
5809
+ }
5810
+ const cross2 = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
5811
+ const d1 = cross2(p3, p4, p1);
5812
+ const d2 = cross2(p3, p4, p2);
5813
+ const d3 = cross2(p1, p2, p3);
5814
+ const d4 = cross2(p1, p2, p4);
5815
+ if (d1 * d2 < 0 && d3 * d4 < 0) {
5816
+ return true;
5817
+ }
5818
+ return false;
5819
+ }
5820
+ function chordsIntersect(newChord, existingChord, perimeter, newPort1, newPort2, existingPort1, existingPort2) {
5821
+ if (chordsCross(newChord, existingChord, perimeter)) {
5822
+ return true;
5823
+ }
5824
+ return lineSegmentsIntersect(
5825
+ newPort1.d,
5826
+ newPort2.d,
5827
+ existingPort1.d,
5828
+ existingPort2.d
5829
+ );
5830
+ }
5781
5831
  function computeDifferentNetCrossingsForPolygon(region, port1, port2) {
5782
5832
  const polygon2 = region.d.polygon;
5783
5833
  if (!polygon2 || polygon2.length < 3) {
@@ -5790,16 +5840,20 @@ function computeDifferentNetCrossingsForPolygon(region, port1, port2) {
5790
5840
  let crossings = 0;
5791
5841
  const assignments = region.assignments ?? [];
5792
5842
  for (const assignment of assignments) {
5793
- const existingT1 = getPortPerimeterTInRegion(
5794
- assignment.regionPort1,
5795
- region
5796
- );
5797
- const existingT2 = getPortPerimeterTInRegion(
5798
- assignment.regionPort2,
5799
- region
5800
- );
5843
+ const existingPort1 = assignment.regionPort1;
5844
+ const existingPort2 = assignment.regionPort2;
5845
+ const existingT1 = getPortPerimeterTInRegion(existingPort1, region);
5846
+ const existingT2 = getPortPerimeterTInRegion(existingPort2, region);
5801
5847
  const existingChord = [existingT1, existingT2];
5802
- if (chordsCross(newChord, existingChord, perimeter)) {
5848
+ if (chordsIntersect(
5849
+ newChord,
5850
+ existingChord,
5851
+ perimeter,
5852
+ port1,
5853
+ port2,
5854
+ existingPort1,
5855
+ existingPort2
5856
+ )) {
5803
5857
  crossings++;
5804
5858
  }
5805
5859
  }
@@ -5817,16 +5871,20 @@ function computeCrossingAssignmentsForPolygon(region, port1, port2) {
5817
5871
  const crossingAssignments = [];
5818
5872
  const assignments = region.assignments ?? [];
5819
5873
  for (const assignment of assignments) {
5820
- const existingT1 = getPortPerimeterTInRegion(
5821
- assignment.regionPort1,
5822
- region
5823
- );
5824
- const existingT2 = getPortPerimeterTInRegion(
5825
- assignment.regionPort2,
5826
- region
5827
- );
5874
+ const existingPort1 = assignment.regionPort1;
5875
+ const existingPort2 = assignment.regionPort2;
5876
+ const existingT1 = getPortPerimeterTInRegion(existingPort1, region);
5877
+ const existingT2 = getPortPerimeterTInRegion(existingPort2, region);
5828
5878
  const existingChord = [existingT1, existingT2];
5829
- if (chordsCross(newChord, existingChord, perimeter)) {
5879
+ if (chordsIntersect(
5880
+ newChord,
5881
+ existingChord,
5882
+ perimeter,
5883
+ port1,
5884
+ port2,
5885
+ existingPort1,
5886
+ existingPort2
5887
+ )) {
5830
5888
  crossingAssignments.push(assignment);
5831
5889
  }
5832
5890
  }
@@ -15697,10 +15755,7 @@ function findSharedEdges(polygon1, polygon2, tolerance = 0.01) {
15697
15755
  x: a1.x + overlap.to * dx,
15698
15756
  y: a1.y + overlap.to * dy
15699
15757
  };
15700
- const edgeLen = Math.sqrt((to.x - from.x) ** 2 + (to.y - from.y) ** 2);
15701
- if (edgeLen > tolerance) {
15702
- sharedEdges.push({ from, to });
15703
- }
15758
+ sharedEdges.push({ from, to });
15704
15759
  }
15705
15760
  }
15706
15761
  return sharedEdges;
@@ -15768,35 +15823,6 @@ function centroid(points) {
15768
15823
  }
15769
15824
  return { x: cx / points.length, y: cy / points.length };
15770
15825
  }
15771
- function classifySideFromBounds(point2, bounds) {
15772
- const distances = {
15773
- left: Math.abs(point2.x - bounds.minX),
15774
- right: Math.abs(point2.x - bounds.maxX),
15775
- bottom: Math.abs(point2.y - bounds.minY),
15776
- top: Math.abs(point2.y - bounds.maxY)
15777
- };
15778
- let bestSide = "left";
15779
- let bestDistance = distances.left;
15780
- for (const side of ["right", "bottom", "top"]) {
15781
- if (distances[side] < bestDistance) {
15782
- bestSide = side;
15783
- bestDistance = distances[side];
15784
- }
15785
- }
15786
- return bestSide;
15787
- }
15788
- function toCandidateKey(regionId, point2) {
15789
- return `${regionId}:${point2.x.toFixed(6)},${point2.y.toFixed(6)}`;
15790
- }
15791
- function compareCandidateQuality(a, b) {
15792
- if (Math.abs(a.primaryDistance - b.primaryDistance) > 1e-6) {
15793
- return b.primaryDistance - a.primaryDistance;
15794
- }
15795
- if (Math.abs(a.orthDistance - b.orthDistance) > 1e-6) {
15796
- return a.orthDistance - b.orthDistance;
15797
- }
15798
- return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
15799
- }
15800
15826
  function createRegionFromPolygon(regionId, polygon2, opts) {
15801
15827
  const bounds = boundsFromPolygon(polygon2);
15802
15828
  return {
@@ -15879,6 +15905,124 @@ function translateRouteSegments(routeSegments, dx, dy, prefix) {
15879
15905
  }))
15880
15906
  }));
15881
15907
  }
15908
+ function translatePolygon(polygon2, dx, dy) {
15909
+ return polygon2.map((p) => ({ x: p.x + dx, y: p.y + dy }));
15910
+ }
15911
+ function rectPolygonFromBounds(b) {
15912
+ return [
15913
+ { x: b.minX, y: b.minY },
15914
+ { x: b.maxX, y: b.minY },
15915
+ { x: b.maxX, y: b.maxY },
15916
+ { x: b.minX, y: b.maxY }
15917
+ ];
15918
+ }
15919
+ function extendViaRegionToTileEdge(polygon2, tileBounds, threshold = 0.1) {
15920
+ if (polygon2.length === 0) return polygon2;
15921
+ const polyBounds = boundsFromPolygon(polygon2);
15922
+ const distToLeft = polyBounds.minX - tileBounds.minX;
15923
+ const distToRight = tileBounds.maxX - polyBounds.maxX;
15924
+ const distToBottom = polyBounds.minY - tileBounds.minY;
15925
+ const distToTop = tileBounds.maxY - polyBounds.maxY;
15926
+ const extendLeft = distToLeft > 0 && distToLeft < threshold;
15927
+ const extendRight = distToRight > 0 && distToRight < threshold;
15928
+ const extendBottom = distToBottom > 0 && distToBottom < threshold;
15929
+ const extendTop = distToTop > 0 && distToTop < threshold;
15930
+ if (!extendLeft && !extendRight && !extendBottom && !extendTop) {
15931
+ return polygon2;
15932
+ }
15933
+ const result = polygon2.map((p) => {
15934
+ let x = p.x;
15935
+ let y = p.y;
15936
+ if (extendLeft && Math.abs(p.x - polyBounds.minX) < 1e-3) {
15937
+ x = tileBounds.minX;
15938
+ }
15939
+ if (extendRight && Math.abs(p.x - polyBounds.maxX) < 1e-3) {
15940
+ x = tileBounds.maxX;
15941
+ }
15942
+ if (extendBottom && Math.abs(p.y - polyBounds.minY) < 1e-3) {
15943
+ y = tileBounds.minY;
15944
+ }
15945
+ if (extendTop && Math.abs(p.y - polyBounds.maxY) < 1e-3) {
15946
+ y = tileBounds.maxY;
15947
+ }
15948
+ return { x, y };
15949
+ });
15950
+ return deduplicateConsecutivePoints(result);
15951
+ }
15952
+ function pointInPolygon(point2, polygon2) {
15953
+ let inside2 = false;
15954
+ const n = polygon2.length;
15955
+ for (let i = 0, j = n - 1; i < n; j = i++) {
15956
+ const xi = polygon2[i].x;
15957
+ const yi = polygon2[i].y;
15958
+ const xj = polygon2[j].x;
15959
+ const yj = polygon2[j].y;
15960
+ const onEdge = Math.abs((point2.y - yi) * (xj - xi) - (point2.x - xi) * (yj - yi)) < 1e-3 && point2.x >= Math.min(xi, xj) - 1e-3 && point2.x <= Math.max(xi, xj) + 1e-3 && point2.y >= Math.min(yi, yj) - 1e-3 && point2.y <= Math.max(yi, yj) + 1e-3;
15961
+ if (onEdge) return true;
15962
+ if (yi > point2.y !== yj > point2.y) {
15963
+ const intersectX = (xj - xi) * (point2.y - yi) / (yj - yi) + xi;
15964
+ if (point2.x < intersectX) {
15965
+ inside2 = !inside2;
15966
+ }
15967
+ }
15968
+ }
15969
+ return inside2;
15970
+ }
15971
+ function findRegionContainingPoint(point2, regions) {
15972
+ for (const region of regions) {
15973
+ if (region.d.polygon && pointInPolygon(point2, region.d.polygon)) {
15974
+ return region;
15975
+ }
15976
+ }
15977
+ return null;
15978
+ }
15979
+ function computeUnitTileTemplate(viaTile, tileWidth, tileHeight, clearance, concavityTolerance) {
15980
+ const halfWidth = tileWidth / 2;
15981
+ const halfHeight = tileHeight / 2;
15982
+ const tileBounds = {
15983
+ minX: -halfWidth,
15984
+ maxX: halfWidth,
15985
+ minY: -halfHeight,
15986
+ maxY: halfHeight
15987
+ };
15988
+ const viaRegions = [];
15989
+ for (const [netName, vias] of Object.entries(viaTile.viasByNet)) {
15990
+ if (vias.length === 0) continue;
15991
+ const polygon2 = generateViaRegionPolygon(vias);
15992
+ if (polygon2.length === 0) continue;
15993
+ viaRegions.push({
15994
+ netName,
15995
+ polygon: polygon2,
15996
+ bounds: boundsFromPolygon(polygon2),
15997
+ center: centroid(polygon2)
15998
+ });
15999
+ }
16000
+ const obstaclePolygons = viaRegions.map((r) => ({
16001
+ points: extendViaRegionToTileEdge(r.polygon, tileBounds)
16002
+ }));
16003
+ const solver = new ConvexRegionsSolver({
16004
+ bounds: tileBounds,
16005
+ polygons: obstaclePolygons,
16006
+ clearance,
16007
+ concavityTolerance
16008
+ });
16009
+ solver.solve();
16010
+ const solverOutput = solver.getOutput();
16011
+ if (!solverOutput) {
16012
+ throw new Error("ConvexRegionsSolver failed to compute unit tile regions");
16013
+ }
16014
+ const convexRegions = solverOutput.regions.map((polygon2) => ({
16015
+ polygon: polygon2,
16016
+ bounds: boundsFromPolygon(polygon2),
16017
+ center: centroid(polygon2)
16018
+ }));
16019
+ return {
16020
+ viaRegions,
16021
+ convexRegions,
16022
+ tileWidth,
16023
+ tileHeight
16024
+ };
16025
+ }
15882
16026
  function generateConvexViaTopologyRegions(opts) {
15883
16027
  const tileWidth = opts.tileWidth ?? opts.tileSize ?? opts.viaTile.tileWidth;
15884
16028
  const tileHeight = opts.tileHeight ?? opts.tileSize ?? opts.viaTile.tileHeight;
@@ -15900,18 +16044,79 @@ function generateConvexViaTopologyRegions(opts) {
15900
16044
  const allPorts = [];
15901
16045
  const viaTile = { viasByNet: {}, routeSegments: [] };
15902
16046
  const viaRegions = [];
16047
+ const convexRegions = [];
15903
16048
  const gridWidth = cols * tileWidth;
15904
16049
  const gridHeight = rows * tileHeight;
15905
16050
  const gridMinX = bounds.minX + (width - gridWidth) / 2;
15906
16051
  const gridMinY = bounds.minY + (height - gridHeight) / 2;
16052
+ const gridMaxX = gridMinX + gridWidth;
16053
+ const gridMaxY = gridMinY + gridHeight;
15907
16054
  const halfWidth = tileWidth / 2;
15908
16055
  const halfHeight = tileHeight / 2;
16056
+ let portIdCounter = 0;
16057
+ const usedPortPositions = /* @__PURE__ */ new Set();
16058
+ const getPortPosKey = (x, y) => `${x.toFixed(4)},${y.toFixed(4)}`;
16059
+ const createPort = (portId, region1, region2, pos) => {
16060
+ const posKey = getPortPosKey(pos.x, pos.y);
16061
+ if (usedPortPositions.has(posKey)) {
16062
+ return null;
16063
+ }
16064
+ usedPortPositions.add(posKey);
16065
+ const port = {
16066
+ portId,
16067
+ region1,
16068
+ region2,
16069
+ d: { x: pos.x, y: pos.y }
16070
+ };
16071
+ region1.ports.push(port);
16072
+ region2.ports.push(port);
16073
+ allPorts.push(port);
16074
+ return port;
16075
+ };
16076
+ let unitTileTemplate = null;
15909
16077
  if (rows > 0 && cols > 0) {
16078
+ unitTileTemplate = computeUnitTileTemplate(
16079
+ inputViaTile,
16080
+ tileWidth,
16081
+ tileHeight,
16082
+ clearance,
16083
+ concavityTolerance
16084
+ );
16085
+ }
16086
+ if (rows > 0 && cols > 0 && unitTileTemplate) {
15910
16087
  for (let row = 0; row < rows; row++) {
15911
16088
  for (let col = 0; col < cols; col++) {
15912
16089
  const tileCenterX = gridMinX + col * tileWidth + halfWidth;
15913
16090
  const tileCenterY = gridMinY + row * tileHeight + halfHeight;
15914
16091
  const prefix = `t${row}_${col}`;
16092
+ for (const templateViaRegion of unitTileTemplate.viaRegions) {
16093
+ const translatedPolygon = translatePolygon(
16094
+ templateViaRegion.polygon,
16095
+ tileCenterX,
16096
+ tileCenterY
16097
+ );
16098
+ const viaRegion = createRegionFromPolygon(
16099
+ `${prefix}:v:${templateViaRegion.netName}`,
16100
+ translatedPolygon,
16101
+ { isViaRegion: true }
16102
+ );
16103
+ viaRegions.push(viaRegion);
16104
+ allRegions.push(viaRegion);
16105
+ }
16106
+ for (let i = 0; i < unitTileTemplate.convexRegions.length; i++) {
16107
+ const templateConvexRegion = unitTileTemplate.convexRegions[i];
16108
+ const translatedPolygon = translatePolygon(
16109
+ templateConvexRegion.polygon,
16110
+ tileCenterX,
16111
+ tileCenterY
16112
+ );
16113
+ const convexRegion = createRegionFromPolygon(
16114
+ `${prefix}:convex:${i}`,
16115
+ translatedPolygon
16116
+ );
16117
+ convexRegions.push(convexRegion);
16118
+ allRegions.push(convexRegion);
16119
+ }
15915
16120
  for (const [netName, vias] of Object.entries(viasByNet)) {
15916
16121
  if (vias.length === 0) continue;
15917
16122
  const translatedVias = translateVias(
@@ -15924,15 +16129,6 @@ function generateConvexViaTopologyRegions(opts) {
15924
16129
  viaTile.viasByNet[netName] = [];
15925
16130
  }
15926
16131
  viaTile.viasByNet[netName].push(...translatedVias);
15927
- const polygon2 = generateViaRegionPolygon(translatedVias);
15928
- if (polygon2.length === 0) continue;
15929
- const viaRegion = createRegionFromPolygon(
15930
- `${prefix}:v:${netName}`,
15931
- polygon2,
15932
- { isViaRegion: true }
15933
- );
15934
- viaRegions.push(viaRegion);
15935
- allRegions.push(viaRegion);
15936
16132
  }
15937
16133
  viaTile.routeSegments.push(
15938
16134
  ...translateRouteSegments(
@@ -15945,55 +16141,306 @@ function generateConvexViaTopologyRegions(opts) {
15945
16141
  }
15946
16142
  }
15947
16143
  }
15948
- const obstaclePolygons = viaRegions.map((r) => ({
15949
- points: r.d.polygon
15950
- }));
15951
- const solverInput = {
15952
- bounds,
15953
- polygons: obstaclePolygons,
15954
- clearance,
15955
- concavityTolerance
15956
- };
15957
- const solver = new ConvexRegionsSolver(solverInput);
15958
- solver.solve();
15959
- const solverOutput = solver.getOutput();
15960
- if (!solverOutput) {
15961
- throw new Error("ConvexRegionsSolver failed to compute regions");
16144
+ const fillerRegions = [];
16145
+ const topMargin = bounds.maxY - gridMaxY;
16146
+ const bottomMargin = gridMinY - bounds.minY;
16147
+ const leftMargin = gridMinX - bounds.minX;
16148
+ const rightMargin = bounds.maxX - gridMaxX;
16149
+ const verticalMargin = Math.max(topMargin, bottomMargin);
16150
+ const horizontalMargin = Math.max(leftMargin, rightMargin);
16151
+ const topBottomGetCorners = verticalMargin >= horizontalMargin;
16152
+ const topMinX = topBottomGetCorners ? bounds.minX : gridMinX;
16153
+ const topMaxX = topBottomGetCorners ? bounds.maxX : gridMaxX;
16154
+ const bottomMinX = topBottomGetCorners ? bounds.minX : gridMinX;
16155
+ const bottomMaxX = topBottomGetCorners ? bounds.maxX : gridMaxX;
16156
+ const leftMinY = topBottomGetCorners ? gridMinY : bounds.minY;
16157
+ const leftMaxY = topBottomGetCorners ? gridMaxY : bounds.maxY;
16158
+ const rightMinY = topBottomGetCorners ? gridMinY : bounds.minY;
16159
+ const rightMaxY = topBottomGetCorners ? gridMaxY : bounds.maxY;
16160
+ if (topMargin > 1e-3) {
16161
+ const topWidth = topMaxX - topMinX;
16162
+ const targetStripWidth = Math.max(topMargin, portPitch);
16163
+ const numTopStrips = Math.max(1, Math.floor(topWidth / targetStripWidth));
16164
+ const stripWidth = topWidth / numTopStrips;
16165
+ for (let i = 0; i < numTopStrips; i++) {
16166
+ const fillerBounds = {
16167
+ minX: topMinX + i * stripWidth,
16168
+ maxX: topMinX + (i + 1) * stripWidth,
16169
+ minY: gridMaxY,
16170
+ maxY: bounds.maxY
16171
+ };
16172
+ const regionId = `filler:top:${i}`;
16173
+ const filler = createRegionFromPolygon(
16174
+ regionId,
16175
+ rectPolygonFromBounds(fillerBounds)
16176
+ );
16177
+ fillerRegions.push(filler);
16178
+ allRegions.push(filler);
16179
+ }
15962
16180
  }
15963
- const convexRegions = solverOutput.regions.map(
15964
- (polygon2, i) => createRegionFromPolygon(`convex:${i}`, polygon2)
15965
- );
15966
- allRegions.push(...convexRegions);
15967
- let portIdCounter = 0;
15968
- for (let i = 0; i < convexRegions.length; i++) {
15969
- for (let j = i + 1; j < convexRegions.length; j++) {
15970
- const region1 = convexRegions[i];
15971
- const region2 = convexRegions[j];
16181
+ if (bottomMargin > 1e-3) {
16182
+ const bottomWidth = bottomMaxX - bottomMinX;
16183
+ const targetStripWidth = Math.max(bottomMargin, portPitch);
16184
+ const numBottomStrips = Math.max(
16185
+ 1,
16186
+ Math.floor(bottomWidth / targetStripWidth)
16187
+ );
16188
+ const stripWidth = bottomWidth / numBottomStrips;
16189
+ for (let i = 0; i < numBottomStrips; i++) {
16190
+ const fillerBounds = {
16191
+ minX: bottomMinX + i * stripWidth,
16192
+ maxX: bottomMinX + (i + 1) * stripWidth,
16193
+ minY: bounds.minY,
16194
+ maxY: gridMinY
16195
+ };
16196
+ const regionId = `filler:bottom:${i}`;
16197
+ const filler = createRegionFromPolygon(
16198
+ regionId,
16199
+ rectPolygonFromBounds(fillerBounds)
16200
+ );
16201
+ fillerRegions.push(filler);
16202
+ allRegions.push(filler);
16203
+ }
16204
+ }
16205
+ if (leftMargin > 1e-3) {
16206
+ const leftHeight = leftMaxY - leftMinY;
16207
+ const targetStripHeight = Math.max(leftMargin, portPitch);
16208
+ const numLeftStrips = Math.max(
16209
+ 1,
16210
+ Math.floor(leftHeight / targetStripHeight)
16211
+ );
16212
+ const stripHeight = leftHeight / numLeftStrips;
16213
+ for (let i = 0; i < numLeftStrips; i++) {
16214
+ const fillerBounds = {
16215
+ minX: bounds.minX,
16216
+ maxX: gridMinX,
16217
+ minY: leftMinY + i * stripHeight,
16218
+ maxY: leftMinY + (i + 1) * stripHeight
16219
+ };
16220
+ const regionId = `filler:left:${i}`;
16221
+ const filler = createRegionFromPolygon(
16222
+ regionId,
16223
+ rectPolygonFromBounds(fillerBounds)
16224
+ );
16225
+ fillerRegions.push(filler);
16226
+ allRegions.push(filler);
16227
+ }
16228
+ }
16229
+ if (rightMargin > 1e-3) {
16230
+ const rightHeight = rightMaxY - rightMinY;
16231
+ const targetStripHeight = Math.max(rightMargin, portPitch);
16232
+ const numRightStrips = Math.max(
16233
+ 1,
16234
+ Math.floor(rightHeight / targetStripHeight)
16235
+ );
16236
+ const stripHeight = rightHeight / numRightStrips;
16237
+ for (let i = 0; i < numRightStrips; i++) {
16238
+ const fillerBounds = {
16239
+ minX: gridMaxX,
16240
+ maxX: bounds.maxX,
16241
+ minY: rightMinY + i * stripHeight,
16242
+ maxY: rightMinY + (i + 1) * stripHeight
16243
+ };
16244
+ const regionId = `filler:right:${i}`;
16245
+ const filler = createRegionFromPolygon(
16246
+ regionId,
16247
+ rectPolygonFromBounds(fillerBounds)
16248
+ );
16249
+ fillerRegions.push(filler);
16250
+ allRegions.push(filler);
16251
+ }
16252
+ }
16253
+ if (unitTileTemplate && rows > 0 && cols > 0) {
16254
+ const regionsPerTile = unitTileTemplate.convexRegions.length;
16255
+ for (let row = 0; row < rows; row++) {
16256
+ for (let col = 0; col < cols; col++) {
16257
+ const tileIndex = row * cols + col;
16258
+ const tileStartIdx = tileIndex * regionsPerTile;
16259
+ for (let i = 0; i < regionsPerTile; i++) {
16260
+ for (let j = i + 1; j < regionsPerTile; j++) {
16261
+ const region1 = convexRegions[tileStartIdx + i];
16262
+ const region2 = convexRegions[tileStartIdx + j];
16263
+ const sharedEdges = findSharedEdges(
16264
+ region1.d.polygon,
16265
+ region2.d.polygon,
16266
+ clearance * 2
16267
+ );
16268
+ for (const edge of sharedEdges) {
16269
+ const portPositions = createPortsAlongEdge(edge, portPitch);
16270
+ for (const pos of portPositions) {
16271
+ createPort(
16272
+ `t${row}_${col}:convex:${i}-${j}:${portIdCounter++}`,
16273
+ region1,
16274
+ region2,
16275
+ pos
16276
+ );
16277
+ }
16278
+ }
16279
+ }
16280
+ }
16281
+ }
16282
+ }
16283
+ }
16284
+ if (unitTileTemplate && rows > 0 && cols > 0) {
16285
+ const convexPerTile = unitTileTemplate.convexRegions.length;
16286
+ const viasPerTile = unitTileTemplate.viaRegions.length;
16287
+ const numVerticalPorts = Math.floor(tileHeight / portPitch);
16288
+ const verticalPortYOffsets = [];
16289
+ for (let i = 0; i < numVerticalPorts; i++) {
16290
+ verticalPortYOffsets.push(-halfHeight + (i + 0.5) * portPitch);
16291
+ }
16292
+ const numHorizontalPorts = Math.floor(tileWidth / portPitch);
16293
+ const horizontalPortXOffsets = [];
16294
+ for (let i = 0; i < numHorizontalPorts; i++) {
16295
+ horizontalPortXOffsets.push(-halfWidth + (i + 0.5) * portPitch);
16296
+ }
16297
+ for (let row = 0; row < rows; row++) {
16298
+ for (let col = 0; col < cols; col++) {
16299
+ const tileIndex = row * cols + col;
16300
+ const convexStartIdx = tileIndex * convexPerTile;
16301
+ const viaStartIdx = tileIndex * viasPerTile;
16302
+ const tileCenterX = gridMinX + col * tileWidth + halfWidth;
16303
+ const tileCenterY = gridMinY + row * tileHeight + halfHeight;
16304
+ const tileConvexRegions = convexRegions.slice(
16305
+ convexStartIdx,
16306
+ convexStartIdx + convexPerTile
16307
+ );
16308
+ const tileViaRegions = viaRegions.slice(
16309
+ viaStartIdx,
16310
+ viaStartIdx + viasPerTile
16311
+ );
16312
+ const tileAllRegions = [...tileConvexRegions, ...tileViaRegions];
16313
+ if (col + 1 < cols) {
16314
+ const rightTileIndex = row * cols + (col + 1);
16315
+ const rightConvexStartIdx = rightTileIndex * convexPerTile;
16316
+ const rightViaStartIdx = rightTileIndex * viasPerTile;
16317
+ const rightTileConvexRegions = convexRegions.slice(
16318
+ rightConvexStartIdx,
16319
+ rightConvexStartIdx + convexPerTile
16320
+ );
16321
+ const rightTileViaRegions = viaRegions.slice(
16322
+ rightViaStartIdx,
16323
+ rightViaStartIdx + viasPerTile
16324
+ );
16325
+ const rightTileAllRegions = [
16326
+ ...rightTileConvexRegions,
16327
+ ...rightTileViaRegions
16328
+ ];
16329
+ const boundaryX = tileCenterX + halfWidth;
16330
+ for (const yOffset of verticalPortYOffsets) {
16331
+ const portY = tileCenterY + yOffset;
16332
+ const pointInCurrentTile = { x: boundaryX - 0.01, y: portY };
16333
+ const pointInRightTile = { x: boundaryX + 0.01, y: portY };
16334
+ const region1 = findRegionContainingPoint(
16335
+ pointInCurrentTile,
16336
+ tileAllRegions
16337
+ );
16338
+ const region2 = findRegionContainingPoint(
16339
+ pointInRightTile,
16340
+ rightTileAllRegions
16341
+ );
16342
+ if (region1 && region2) {
16343
+ createPort(
16344
+ `tile:${row}_${col}-${row}_${col + 1}:${portIdCounter++}`,
16345
+ region1,
16346
+ region2,
16347
+ { x: boundaryX, y: portY }
16348
+ );
16349
+ }
16350
+ }
16351
+ }
16352
+ if (row + 1 < rows) {
16353
+ const topTileIndex = (row + 1) * cols + col;
16354
+ const topConvexStartIdx = topTileIndex * convexPerTile;
16355
+ const topViaStartIdx = topTileIndex * viasPerTile;
16356
+ const topTileConvexRegions = convexRegions.slice(
16357
+ topConvexStartIdx,
16358
+ topConvexStartIdx + convexPerTile
16359
+ );
16360
+ const topTileViaRegions = viaRegions.slice(
16361
+ topViaStartIdx,
16362
+ topViaStartIdx + viasPerTile
16363
+ );
16364
+ const topTileAllRegions = [
16365
+ ...topTileConvexRegions,
16366
+ ...topTileViaRegions
16367
+ ];
16368
+ const boundaryY = tileCenterY + halfHeight;
16369
+ for (const xOffset of horizontalPortXOffsets) {
16370
+ const portX = tileCenterX + xOffset;
16371
+ const pointInCurrentTile = { x: portX, y: boundaryY - 0.01 };
16372
+ const pointInTopTile = { x: portX, y: boundaryY + 0.01 };
16373
+ const region1 = findRegionContainingPoint(
16374
+ pointInCurrentTile,
16375
+ tileAllRegions
16376
+ );
16377
+ const region2 = findRegionContainingPoint(
16378
+ pointInTopTile,
16379
+ topTileAllRegions
16380
+ );
16381
+ if (region1 && region2) {
16382
+ createPort(
16383
+ `tile:${row}_${col}-${row + 1}_${col}:${portIdCounter++}`,
16384
+ region1,
16385
+ region2,
16386
+ { x: portX, y: boundaryY }
16387
+ );
16388
+ }
16389
+ }
16390
+ }
16391
+ }
16392
+ }
16393
+ }
16394
+ for (const fillerRegion of fillerRegions) {
16395
+ const tileRegions = [...convexRegions, ...viaRegions];
16396
+ 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
16410
+ );
16411
+ }
16412
+ }
16413
+ }
16414
+ }
16415
+ for (let i = 0; i < fillerRegions.length; i++) {
16416
+ for (let j = i + 1; j < fillerRegions.length; j++) {
16417
+ const region1 = fillerRegions[i];
16418
+ const region2 = fillerRegions[j];
15972
16419
  const sharedEdges = findSharedEdges(
15973
16420
  region1.d.polygon,
15974
16421
  region2.d.polygon,
15975
- clearance * 2
15976
- // tolerance slightly larger than clearance
16422
+ 0.01
15977
16423
  );
15978
16424
  for (const edge of sharedEdges) {
16425
+ const edgeLength = Math.sqrt(
16426
+ (edge.to.x - edge.from.x) ** 2 + (edge.to.y - edge.from.y) ** 2
16427
+ );
16428
+ if (edgeLength < portPitch) {
16429
+ continue;
16430
+ }
15979
16431
  const portPositions = createPortsAlongEdge(edge, portPitch);
15980
16432
  for (const pos of portPositions) {
15981
- const port = {
15982
- portId: `convex:${i}-${j}:${portIdCounter++}`,
16433
+ createPort(
16434
+ `filler:${region1.regionId}-${region2.regionId}:${portIdCounter++}`,
15983
16435
  region1,
15984
16436
  region2,
15985
- d: { x: pos.x, y: pos.y }
15986
- };
15987
- region1.ports.push(port);
15988
- region2.ports.push(port);
15989
- allPorts.push(port);
16437
+ pos
16438
+ );
15990
16439
  }
15991
16440
  }
15992
16441
  }
15993
16442
  }
15994
16443
  for (const viaRegion of viaRegions) {
15995
- const viaCenter = viaRegion.d.center;
15996
- const candidates = [];
15997
16444
  for (const convexRegion of convexRegions) {
15998
16445
  const sharedEdges = findSharedEdges(
15999
16446
  viaRegion.d.polygon,
@@ -16003,55 +16450,15 @@ function generateConvexViaTopologyRegions(opts) {
16003
16450
  for (const edge of sharedEdges) {
16004
16451
  const portPositions = createPortsAlongEdge(edge, portPitch);
16005
16452
  for (const pos of portPositions) {
16006
- const dx = pos.x - viaCenter.x;
16007
- const dy = pos.y - viaCenter.y;
16008
- const side = classifySideFromBounds(pos, viaRegion.d.bounds);
16009
- const primaryDistance = side === "left" || side === "right" ? Math.abs(dx) : Math.abs(dy);
16010
- const orthDistance = side === "left" || side === "right" ? Math.abs(dy) : Math.abs(dx);
16011
- candidates.push({
16453
+ createPort(
16454
+ `via-convex:${viaRegion.regionId}-${convexRegion.regionId}:${portIdCounter++}`,
16455
+ viaRegion,
16012
16456
  convexRegion,
16013
- position: pos,
16014
- side,
16015
- primaryDistance,
16016
- orthDistance,
16017
- key: toCandidateKey(convexRegion.regionId, pos)
16018
- });
16457
+ pos
16458
+ );
16019
16459
  }
16020
16460
  }
16021
16461
  }
16022
- if (candidates.length === 0) continue;
16023
- const selectedCandidates = [];
16024
- const selectedKeys = /* @__PURE__ */ new Set();
16025
- const addCandidate = (candidate) => {
16026
- if (!candidate) return;
16027
- if (selectedKeys.has(candidate.key)) return;
16028
- selectedCandidates.push(candidate);
16029
- selectedKeys.add(candidate.key);
16030
- };
16031
- for (const side of ["top", "bottom", "left", "right"]) {
16032
- const sideCandidate = [...candidates].filter((candidate) => candidate.side === side).sort(compareCandidateQuality)[0];
16033
- addCandidate(sideCandidate);
16034
- }
16035
- if (selectedCandidates.length < 4) {
16036
- for (const candidate of [...candidates].sort(compareCandidateQuality)) {
16037
- addCandidate(candidate);
16038
- if (selectedCandidates.length >= 4) break;
16039
- }
16040
- }
16041
- for (const selectedCandidate of selectedCandidates.slice(0, 4)) {
16042
- const port = {
16043
- portId: `via-convex:${viaRegion.regionId}-${selectedCandidate.convexRegion.regionId}:${portIdCounter++}`,
16044
- region1: viaRegion,
16045
- region2: selectedCandidate.convexRegion,
16046
- d: {
16047
- x: selectedCandidate.position.x,
16048
- y: selectedCandidate.position.y
16049
- }
16050
- };
16051
- viaRegion.ports.push(port);
16052
- selectedCandidate.convexRegion.ports.push(port);
16053
- allPorts.push(port);
16054
- }
16055
16462
  }
16056
16463
  return {
16057
16464
  regions: allRegions,