@tscircuit/hypergraph 0.0.43 → 0.0.45

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 +22 -79
  2. package/dist/index.js +540 -832
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5504,7 +5504,9 @@ var via_tile_4_regions_default = {
5504
5504
  }
5505
5505
  ]
5506
5506
  }
5507
- ]
5507
+ ],
5508
+ tileWidth: 3.735441,
5509
+ tileHeight: 4.029267
5508
5510
  };
5509
5511
 
5510
5512
  // lib/ViaGraphSolver/via-graph-generator/generateViaTopologyRegions.ts
@@ -5770,647 +5772,38 @@ var generateViaTopologyRegions = (viaTile, opts) => {
5770
5772
  return { regions, ports };
5771
5773
  };
5772
5774
 
5773
- // lib/ViaGraphSolver/via-graph-generator/generateViaTopologyGrid.ts
5774
- var DEFAULT_PORT_PITCH = 0.4;
5775
- var DEFAULT_TILE_SIZE = 5;
5776
- function translateGraph(graph, dx, dy, prefix) {
5777
- const regionMap = /* @__PURE__ */ new Map();
5778
- const regions = graph.regions.map((r) => {
5779
- const newRegion = {
5780
- regionId: `${prefix}:${r.regionId}`,
5781
- ports: [],
5782
- d: {
5783
- bounds: {
5784
- minX: r.d.bounds.minX + dx,
5785
- maxX: r.d.bounds.maxX + dx,
5786
- minY: r.d.bounds.minY + dy,
5787
- maxY: r.d.bounds.maxY + dy
5788
- },
5789
- center: {
5790
- x: r.d.center.x + dx,
5791
- y: r.d.center.y + dy
5792
- },
5793
- polygon: r.d.polygon?.map((p) => ({ x: p.x + dx, y: p.y + dy })),
5794
- isPad: r.d.isPad,
5795
- isThroughJumper: r.d.isThroughJumper,
5796
- isConnectionRegion: r.d.isConnectionRegion,
5797
- isViaRegion: r.d.isViaRegion
5798
- }
5799
- };
5800
- regionMap.set(r, newRegion);
5801
- return newRegion;
5802
- });
5803
- const ports = graph.ports.map((p) => {
5804
- const newPort = {
5805
- portId: `${prefix}:${p.portId}`,
5806
- region1: regionMap.get(p.region1),
5807
- region2: regionMap.get(p.region2),
5808
- d: { x: p.d.x + dx, y: p.d.y + dy }
5809
- };
5810
- newPort.region1.ports.push(newPort);
5811
- newPort.region2.ports.push(newPort);
5812
- return newPort;
5813
- });
5814
- return { regions, ports };
5815
- }
5816
- function translateRouteSegments(routeSegments, dx, dy, prefix) {
5817
- return routeSegments.map((segment2) => ({
5818
- routeId: `${prefix}:${segment2.routeId}`,
5819
- fromPort: `${prefix}:${segment2.fromPort}`,
5820
- toPort: `${prefix}:${segment2.toPort}`,
5821
- layer: segment2.layer,
5822
- segments: segment2.segments.map((point2) => ({
5823
- x: point2.x + dx,
5824
- y: point2.y + dy
5825
- }))
5826
- }));
5827
- }
5828
- function createBoundaryPorts(portIdPrefix, region1, region2, axis, fixedCoord, start, end, portPitch) {
5829
- const length = Math.abs(end - start);
5830
- if (length < 1e-3) return [];
5831
- const count = Math.max(1, Math.floor(length / portPitch));
5832
- const min = Math.min(start, end);
5833
- const ports = [];
5834
- for (let i = 0; i < count; i++) {
5835
- const t = (i + 0.5) / count;
5836
- const varying = min + t * length;
5837
- const x = axis === "horizontal" ? varying : fixedCoord;
5838
- const y = axis === "horizontal" ? fixedCoord : varying;
5839
- const portId = count === 1 ? portIdPrefix : `${portIdPrefix}_${i}`;
5840
- const port = {
5841
- portId,
5842
- region1,
5843
- region2,
5844
- d: { x, y }
5845
- };
5846
- region1.ports.push(port);
5847
- region2.ports.push(port);
5848
- ports.push(port);
5849
- }
5850
- return ports;
5851
- }
5852
- function createRectRegion(regionId, minX, maxX, minY, maxY) {
5853
- const polygon2 = [
5854
- { x: minX, y: minY },
5855
- { x: maxX, y: minY },
5856
- { x: maxX, y: maxY },
5857
- { x: minX, y: maxY }
5858
- ];
5859
- return {
5860
- regionId,
5861
- ports: [],
5862
- d: {
5863
- bounds: { minX, maxX, minY, maxY },
5864
- center: { x: (minX + maxX) / 2, y: (minY + maxY) / 2 },
5865
- polygon: polygon2,
5866
- isPad: false
5867
- }
5868
- };
5869
- }
5870
- function findRegionBySuffix(graph, suffix) {
5871
- const region = graph.regions.find((r) => r.regionId.endsWith(`:${suffix}`));
5872
- if (!region) throw new Error(`Region with suffix ${suffix} not found`);
5873
- return region;
5874
- }
5875
- function getBoundarySegments(polygon2, edge, boundaryCoord) {
5876
- const eps = 1e-3;
5877
- const segments = [];
5878
- for (let i = 0; i < polygon2.length; i++) {
5879
- const a = polygon2[i];
5880
- const b = polygon2[(i + 1) % polygon2.length];
5881
- const isVerticalEdge = edge === "right" || edge === "left";
5882
- if (isVerticalEdge) {
5883
- if (Math.abs(a.x - boundaryCoord) < eps && Math.abs(b.x - boundaryCoord) < eps) {
5884
- segments.push({ from: Math.min(a.y, b.y), to: Math.max(a.y, b.y) });
5885
- }
5886
- } else {
5887
- if (Math.abs(a.y - boundaryCoord) < eps && Math.abs(b.y - boundaryCoord) < eps) {
5888
- segments.push({ from: Math.min(a.x, b.x), to: Math.max(a.x, b.x) });
5889
- }
5890
- }
5891
- }
5892
- return segments;
5893
- }
5894
- function rangeOverlap(a, b) {
5895
- const from = Math.max(a.from, b.from);
5896
- const to = Math.min(a.to, b.to);
5897
- if (to - from < 1e-3) return null;
5898
- return { from, to };
5899
- }
5900
- function generateViaTopologyGrid(opts) {
5901
- const tileSize = opts.tileSize ?? DEFAULT_TILE_SIZE;
5902
- const portPitch = opts.portPitch ?? DEFAULT_PORT_PITCH;
5903
- const { bounds, viaTile: inputViaTile } = opts;
5904
- const { viasByNet, routeSegments } = inputViaTile;
5905
- const width = bounds.maxX - bounds.minX;
5906
- const height = bounds.maxY - bounds.minY;
5907
- const cols = Math.floor(width / tileSize);
5908
- const rows = Math.floor(height / tileSize);
5909
- const allRegions = [];
5910
- const allPorts = [];
5911
- const viaTile = { viasByNet: {}, routeSegments: [] };
5912
- const gridWidth = cols * tileSize;
5913
- const gridHeight = rows * tileSize;
5914
- const gridMinX = bounds.minX + (width - gridWidth) / 2;
5915
- const gridMinY = bounds.minY + (height - gridHeight) / 2;
5916
- const gridMaxX = gridMinX + gridWidth;
5917
- const gridMaxY = gridMinY + gridHeight;
5918
- const tileGraphs = [];
5919
- if (rows > 0 && cols > 0) {
5920
- const baseGraph = generateViaTopologyRegions(inputViaTile, {
5921
- graphSize: tileSize,
5922
- idPrefix: "v"
5923
- });
5924
- const half = tileSize / 2;
5925
- const outerIds = ["T", "B", "L", "R"];
5926
- const baseBoundarySegs = {};
5927
- for (const id of outerIds) {
5928
- const r = baseGraph.regions.find((r2) => r2.regionId === `v:${id}`);
5929
- baseBoundarySegs[id] = {
5930
- right: getBoundarySegments(r.d.polygon, "right", half),
5931
- left: getBoundarySegments(r.d.polygon, "left", -half),
5932
- top: getBoundarySegments(r.d.polygon, "top", half),
5933
- bottom: getBoundarySegments(r.d.polygon, "bottom", -half)
5934
- };
5935
- }
5936
- for (let row = 0; row < rows; row++) {
5937
- tileGraphs[row] = [];
5938
- for (let col = 0; col < cols; col++) {
5939
- const tileCenterX = gridMinX + col * tileSize + half;
5940
- const tileCenterY = gridMinY + row * tileSize + half;
5941
- const prefix = `t${row}_${col}`;
5942
- const tile = translateGraph(baseGraph, tileCenterX, tileCenterY, prefix);
5943
- tileGraphs[row][col] = tile;
5944
- allRegions.push(...tile.regions);
5945
- allPorts.push(...tile.ports);
5946
- for (const [netName, vias] of Object.entries(viasByNet)) {
5947
- if (!viaTile.viasByNet[netName]) {
5948
- viaTile.viasByNet[netName] = [];
5949
- }
5950
- for (const via of vias) {
5951
- viaTile.viasByNet[netName].push({
5952
- viaId: `${prefix}:${via.viaId}`,
5953
- diameter: via.diameter,
5954
- position: {
5955
- x: via.position.x + tileCenterX,
5956
- y: via.position.y + tileCenterY
5957
- }
5958
- });
5959
- }
5960
- }
5961
- viaTile.routeSegments.push(
5962
- ...translateRouteSegments(
5963
- routeSegments,
5964
- tileCenterX,
5965
- tileCenterY,
5966
- prefix
5967
- )
5968
- );
5969
- }
5970
- }
5971
- const hPairs = [
5972
- {
5973
- leftRegionId: "T",
5974
- leftEdge: "right",
5975
- rightRegionId: "T",
5976
- rightEdge: "left"
5977
- },
5978
- {
5979
- leftRegionId: "R",
5980
- leftEdge: "right",
5981
- rightRegionId: "L",
5982
- rightEdge: "left"
5983
- },
5984
- {
5985
- leftRegionId: "B",
5986
- leftEdge: "right",
5987
- rightRegionId: "B",
5988
- rightEdge: "left"
5989
- }
5990
- ];
5991
- for (let row = 0; row < rows; row++) {
5992
- for (let col = 0; col < cols - 1; col++) {
5993
- const leftTile = tileGraphs[row][col];
5994
- const rightTile = tileGraphs[row][col + 1];
5995
- const boundaryX = gridMinX + (col + 1) * tileSize;
5996
- const yOffset = gridMinY + row * tileSize + half;
5997
- for (const pair of hPairs) {
5998
- const leftSegs = baseBoundarySegs[pair.leftRegionId][pair.leftEdge];
5999
- const rightSegs = baseBoundarySegs[pair.rightRegionId][pair.rightEdge];
6000
- for (const ls of leftSegs) {
6001
- for (const rs of rightSegs) {
6002
- const overlap = rangeOverlap(ls, rs);
6003
- if (!overlap) continue;
6004
- const r1 = findRegionBySuffix(leftTile, `v:${pair.leftRegionId}`);
6005
- const r2 = findRegionBySuffix(
6006
- rightTile,
6007
- `v:${pair.rightRegionId}`
6008
- );
6009
- const tag = `${pair.leftRegionId}${pair.rightRegionId}`;
6010
- const ports = createBoundaryPorts(
6011
- `cross:h:${row}_${col}:${tag}`,
6012
- r1,
6013
- r2,
6014
- "vertical",
6015
- boundaryX,
6016
- overlap.from + yOffset,
6017
- overlap.to + yOffset,
6018
- portPitch
6019
- );
6020
- allPorts.push(...ports);
6021
- }
6022
- }
6023
- }
6024
- }
6025
- }
6026
- const vPairs = [
6027
- {
6028
- bottomRegionId: "T",
6029
- bottomEdge: "top",
6030
- topRegionId: "B",
6031
- topEdge: "bottom"
6032
- },
6033
- {
6034
- bottomRegionId: "L",
6035
- bottomEdge: "top",
6036
- topRegionId: "L",
6037
- topEdge: "bottom"
6038
- },
6039
- {
6040
- bottomRegionId: "R",
6041
- bottomEdge: "top",
6042
- topRegionId: "R",
6043
- topEdge: "bottom"
6044
- }
6045
- ];
6046
- for (let row = 0; row < rows - 1; row++) {
6047
- for (let col = 0; col < cols; col++) {
6048
- const bottomTile = tileGraphs[row][col];
6049
- const topTile = tileGraphs[row + 1][col];
6050
- const boundaryY = gridMinY + (row + 1) * tileSize;
6051
- const xOffset = gridMinX + col * tileSize + half;
6052
- for (const pair of vPairs) {
6053
- const bottomSegs = baseBoundarySegs[pair.bottomRegionId][pair.bottomEdge];
6054
- const topSegs = baseBoundarySegs[pair.topRegionId][pair.topEdge];
6055
- for (const bs of bottomSegs) {
6056
- for (const ts of topSegs) {
6057
- const overlap = rangeOverlap(bs, ts);
6058
- if (!overlap) continue;
6059
- const r1 = findRegionBySuffix(
6060
- bottomTile,
6061
- `v:${pair.bottomRegionId}`
6062
- );
6063
- const r2 = findRegionBySuffix(topTile, `v:${pair.topRegionId}`);
6064
- const tag = `${pair.bottomRegionId}${pair.topRegionId}`;
6065
- const ports = createBoundaryPorts(
6066
- `cross:v:${row}_${col}:${tag}`,
6067
- r1,
6068
- r2,
6069
- "horizontal",
6070
- boundaryY,
6071
- overlap.from + xOffset,
6072
- overlap.to + xOffset,
6073
- portPitch
6074
- );
6075
- allPorts.push(...ports);
6076
- }
6077
- }
6078
- }
6079
- }
6080
- }
6081
- }
6082
- const hasTopGap = bounds.maxY > gridMaxY || rows === 0;
6083
- const hasBottomGap = bounds.minY < gridMinY || rows === 0;
6084
- const hasLeftGap = bounds.minX < gridMinX || cols === 0;
6085
- const hasRightGap = bounds.maxX > gridMaxX || cols === 0;
6086
- let outerTop = null;
6087
- let outerBottom = null;
6088
- let outerLeft = null;
6089
- let outerRight = null;
6090
- const frameMinX = cols > 0 ? gridMinX : bounds.minX;
6091
- const frameMaxX = cols > 0 ? gridMaxX : bounds.maxX;
6092
- const frameMinY = rows > 0 ? gridMinY : bounds.minY;
6093
- const frameMaxY = rows > 0 ? gridMaxY : bounds.maxY;
6094
- if (hasLeftGap && bounds.minX < frameMinX) {
6095
- outerLeft = createRectRegion(
6096
- "outer:L",
6097
- bounds.minX,
6098
- frameMinX,
6099
- bounds.minY,
6100
- bounds.maxY
6101
- );
6102
- allRegions.push(outerLeft);
6103
- }
6104
- if (hasRightGap && bounds.maxX > frameMaxX) {
6105
- outerRight = createRectRegion(
6106
- "outer:R",
6107
- frameMaxX,
6108
- bounds.maxX,
6109
- bounds.minY,
6110
- bounds.maxY
6111
- );
6112
- allRegions.push(outerRight);
6113
- }
6114
- if (hasTopGap && bounds.maxY > frameMaxY) {
6115
- outerTop = createRectRegion(
6116
- "outer:T",
6117
- frameMinX,
6118
- frameMaxX,
6119
- frameMaxY,
6120
- bounds.maxY
6121
- );
6122
- allRegions.push(outerTop);
6123
- }
6124
- if (hasBottomGap && bounds.minY < frameMinY) {
6125
- outerBottom = createRectRegion(
6126
- "outer:B",
6127
- frameMinX,
6128
- frameMaxX,
6129
- bounds.minY,
6130
- frameMinY
6131
- );
6132
- allRegions.push(outerBottom);
6133
- }
6134
- if (outerTop && outerLeft) {
6135
- allPorts.push(
6136
- ...createBoundaryPorts(
6137
- "outer:T-L",
6138
- outerTop,
6139
- outerLeft,
6140
- "vertical",
6141
- frameMinX,
6142
- frameMaxY,
6143
- bounds.maxY,
6144
- portPitch
6145
- )
6146
- );
6147
- }
6148
- if (outerTop && outerRight) {
6149
- allPorts.push(
6150
- ...createBoundaryPorts(
6151
- "outer:T-R",
6152
- outerTop,
6153
- outerRight,
6154
- "vertical",
6155
- frameMaxX,
6156
- frameMaxY,
6157
- bounds.maxY,
6158
- portPitch
6159
- )
6160
- );
6161
- }
6162
- if (outerBottom && outerLeft) {
6163
- allPorts.push(
6164
- ...createBoundaryPorts(
6165
- "outer:B-L",
6166
- outerBottom,
6167
- outerLeft,
6168
- "vertical",
6169
- frameMinX,
6170
- bounds.minY,
6171
- frameMinY,
6172
- portPitch
6173
- )
6174
- );
6175
- }
6176
- if (outerBottom && outerRight) {
6177
- allPorts.push(
6178
- ...createBoundaryPorts(
6179
- "outer:B-R",
6180
- outerBottom,
6181
- outerRight,
6182
- "vertical",
6183
- frameMaxX,
6184
- bounds.minY,
6185
- frameMinY,
6186
- portPitch
6187
- )
6188
- );
6189
- }
6190
- if (rows > 0 && cols > 0) {
6191
- const half = tileSize / 2;
6192
- if (outerTop) {
6193
- for (let col = 0; col < cols; col++) {
6194
- const tile = tileGraphs[rows - 1][col];
6195
- const tileT = findRegionBySuffix(tile, "v:T");
6196
- const baseT = generateViaTopologyRegions(inputViaTile, {
6197
- graphSize: tileSize,
6198
- idPrefix: "v"
6199
- }).regions.find((r) => r.regionId === "v:T");
6200
- const topSegs = getBoundarySegments(baseT.d.polygon, "top", half);
6201
- for (const seg of topSegs) {
6202
- const tileCenterX = gridMinX + col * tileSize + half;
6203
- allPorts.push(
6204
- ...createBoundaryPorts(
6205
- `outer:T-tile${col}`,
6206
- outerTop,
6207
- tileT,
6208
- "horizontal",
6209
- gridMaxY,
6210
- seg.from + tileCenterX,
6211
- seg.to + tileCenterX,
6212
- portPitch
6213
- )
6214
- );
6215
- }
6216
- }
6217
- }
6218
- if (outerBottom) {
6219
- for (let col = 0; col < cols; col++) {
6220
- const tile = tileGraphs[0][col];
6221
- const tileB = findRegionBySuffix(tile, "v:B");
6222
- const baseB = generateViaTopologyRegions(inputViaTile, {
6223
- graphSize: tileSize,
6224
- idPrefix: "v"
6225
- }).regions.find((r) => r.regionId === "v:B");
6226
- const bottomSegs = getBoundarySegments(
6227
- baseB.d.polygon,
6228
- "bottom",
6229
- -half
6230
- );
6231
- for (const seg of bottomSegs) {
6232
- const tileCenterX = gridMinX + col * tileSize + half;
6233
- allPorts.push(
6234
- ...createBoundaryPorts(
6235
- `outer:B-tile${col}`,
6236
- outerBottom,
6237
- tileB,
6238
- "horizontal",
6239
- gridMinY,
6240
- seg.from + tileCenterX,
6241
- seg.to + tileCenterX,
6242
- portPitch
6243
- )
6244
- );
6245
- }
6246
- }
6247
- }
6248
- if (outerLeft) {
6249
- for (let row = 0; row < rows; row++) {
6250
- const tile = tileGraphs[row][0];
6251
- const tileL = findRegionBySuffix(tile, "v:L");
6252
- const baseL = generateViaTopologyRegions(inputViaTile, {
6253
- graphSize: tileSize,
6254
- idPrefix: "v"
6255
- }).regions.find((r) => r.regionId === "v:L");
6256
- const leftSegs = getBoundarySegments(baseL.d.polygon, "left", -half);
6257
- for (const seg of leftSegs) {
6258
- const tileCenterY = gridMinY + row * tileSize + half;
6259
- allPorts.push(
6260
- ...createBoundaryPorts(
6261
- `outer:L-tile${row}`,
6262
- outerLeft,
6263
- tileL,
6264
- "vertical",
6265
- gridMinX,
6266
- seg.from + tileCenterY,
6267
- seg.to + tileCenterY,
6268
- portPitch
6269
- )
6270
- );
6271
- }
6272
- }
6273
- }
6274
- if (outerRight) {
6275
- for (let row = 0; row < rows; row++) {
6276
- const tile = tileGraphs[row][cols - 1];
6277
- const tileR = findRegionBySuffix(tile, "v:R");
6278
- const baseR = generateViaTopologyRegions(inputViaTile, {
6279
- graphSize: tileSize,
6280
- idPrefix: "v"
6281
- }).regions.find((r) => r.regionId === "v:R");
6282
- const rightSegs = getBoundarySegments(baseR.d.polygon, "right", half);
6283
- for (const seg of rightSegs) {
6284
- const tileCenterY = gridMinY + row * tileSize + half;
6285
- allPorts.push(
6286
- ...createBoundaryPorts(
6287
- `outer:R-tile${row}`,
6288
- outerRight,
6289
- tileR,
6290
- "vertical",
6291
- gridMaxX,
6292
- seg.from + tileCenterY,
6293
- seg.to + tileCenterY,
6294
- portPitch
6295
- )
6296
- );
6297
- }
6298
- }
6299
- }
6300
- if (outerLeft) {
6301
- const baseGraph = generateViaTopologyRegions(inputViaTile, {
6302
- graphSize: tileSize,
6303
- idPrefix: "v"
6304
- });
6305
- const baseT = baseGraph.regions.find((r) => r.regionId === "v:T");
6306
- const baseB = baseGraph.regions.find((r) => r.regionId === "v:B");
6307
- const tLeftSegs = getBoundarySegments(baseT.d.polygon, "left", -half);
6308
- const bLeftSegs = getBoundarySegments(baseB.d.polygon, "left", -half);
6309
- for (let row = 0; row < rows; row++) {
6310
- const tile = tileGraphs[row][0];
6311
- const tileT = findRegionBySuffix(tile, "v:T");
6312
- const tileB = findRegionBySuffix(tile, "v:B");
6313
- const tileCenterY = gridMinY + row * tileSize + half;
6314
- for (const seg of tLeftSegs) {
6315
- allPorts.push(
6316
- ...createBoundaryPorts(
6317
- `outer:L-tileT${row}`,
6318
- outerLeft,
6319
- tileT,
6320
- "vertical",
6321
- gridMinX,
6322
- seg.from + tileCenterY,
6323
- seg.to + tileCenterY,
6324
- portPitch
6325
- )
6326
- );
6327
- }
6328
- for (const seg of bLeftSegs) {
6329
- allPorts.push(
6330
- ...createBoundaryPorts(
6331
- `outer:L-tileB${row}`,
6332
- outerLeft,
6333
- tileB,
6334
- "vertical",
6335
- gridMinX,
6336
- seg.from + tileCenterY,
6337
- seg.to + tileCenterY,
6338
- portPitch
6339
- )
6340
- );
6341
- }
6342
- }
6343
- }
6344
- if (outerRight) {
6345
- const baseGraph = generateViaTopologyRegions(inputViaTile, {
6346
- graphSize: tileSize,
6347
- idPrefix: "v"
6348
- });
6349
- const baseT = baseGraph.regions.find((r) => r.regionId === "v:T");
6350
- const baseB = baseGraph.regions.find((r) => r.regionId === "v:B");
6351
- const tRightSegs = getBoundarySegments(baseT.d.polygon, "right", half);
6352
- const bRightSegs = getBoundarySegments(baseB.d.polygon, "right", half);
6353
- for (let row = 0; row < rows; row++) {
6354
- const tile = tileGraphs[row][cols - 1];
6355
- const tileT = findRegionBySuffix(tile, "v:T");
6356
- const tileB = findRegionBySuffix(tile, "v:B");
6357
- const tileCenterY = gridMinY + row * tileSize + half;
6358
- for (const seg of tRightSegs) {
6359
- allPorts.push(
6360
- ...createBoundaryPorts(
6361
- `outer:R-tileT${row}`,
6362
- outerRight,
6363
- tileT,
6364
- "vertical",
6365
- gridMaxX,
6366
- seg.from + tileCenterY,
6367
- seg.to + tileCenterY,
6368
- portPitch
6369
- )
6370
- );
6371
- }
6372
- for (const seg of bRightSegs) {
6373
- allPorts.push(
6374
- ...createBoundaryPorts(
6375
- `outer:R-tileB${row}`,
6376
- outerRight,
6377
- tileB,
6378
- "vertical",
6379
- gridMaxX,
6380
- seg.from + tileCenterY,
6381
- seg.to + tileCenterY,
6382
- portPitch
6383
- )
6384
- );
6385
- }
6386
- }
6387
- }
6388
- } else {
6389
- if (outerTop && outerBottom) {
6390
- }
6391
- if (outerLeft && outerRight) {
6392
- }
6393
- }
6394
- return {
6395
- regions: allRegions,
6396
- ports: allPorts,
6397
- viaTile,
6398
- tileCount: { rows, cols }
6399
- };
6400
- }
6401
-
6402
5775
  // lib/ViaGraphSolver/defaultTopology.ts
6403
5776
  function generateDefaultViaTopologyRegions(opts) {
6404
5777
  return generateViaTopologyRegions(via_tile_4_regions_default, opts);
6405
5778
  }
6406
- function generateDefaultViaTopologyGrid(opts) {
6407
- return generateViaTopologyGrid({
6408
- ...opts,
6409
- viaTile: via_tile_4_regions_default
6410
- });
6411
- }
6412
5779
 
6413
5780
  // lib/ViaGraphSolver/polygonPerimeterUtils.ts
5781
+ function lineSegmentsIntersect(p1, p2, p3, p4, eps = 1e-9) {
5782
+ const pointsCoincident = (a, b) => Math.abs(a.x - b.x) < eps && Math.abs(a.y - b.y) < eps;
5783
+ if (pointsCoincident(p1, p3) || pointsCoincident(p1, p4) || pointsCoincident(p2, p3) || pointsCoincident(p2, p4)) {
5784
+ return false;
5785
+ }
5786
+ const cross2 = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
5787
+ const d1 = cross2(p3, p4, p1);
5788
+ const d2 = cross2(p3, p4, p2);
5789
+ const d3 = cross2(p1, p2, p3);
5790
+ const d4 = cross2(p1, p2, p4);
5791
+ if (d1 * d2 < 0 && d3 * d4 < 0) {
5792
+ return true;
5793
+ }
5794
+ return false;
5795
+ }
5796
+ function chordsIntersect(newChord, existingChord, perimeter, newPort1, newPort2, existingPort1, existingPort2) {
5797
+ if (chordsCross(newChord, existingChord, perimeter)) {
5798
+ return true;
5799
+ }
5800
+ return lineSegmentsIntersect(
5801
+ newPort1.d,
5802
+ newPort2.d,
5803
+ existingPort1.d,
5804
+ existingPort2.d
5805
+ );
5806
+ }
6414
5807
  function computeDifferentNetCrossingsForPolygon(region, port1, port2) {
6415
5808
  const polygon2 = region.d.polygon;
6416
5809
  if (!polygon2 || polygon2.length < 3) {
@@ -6423,16 +5816,20 @@ function computeDifferentNetCrossingsForPolygon(region, port1, port2) {
6423
5816
  let crossings = 0;
6424
5817
  const assignments = region.assignments ?? [];
6425
5818
  for (const assignment of assignments) {
6426
- const existingT1 = getPortPerimeterTInRegion(
6427
- assignment.regionPort1,
6428
- region
6429
- );
6430
- const existingT2 = getPortPerimeterTInRegion(
6431
- assignment.regionPort2,
6432
- region
6433
- );
5819
+ const existingPort1 = assignment.regionPort1;
5820
+ const existingPort2 = assignment.regionPort2;
5821
+ const existingT1 = getPortPerimeterTInRegion(existingPort1, region);
5822
+ const existingT2 = getPortPerimeterTInRegion(existingPort2, region);
6434
5823
  const existingChord = [existingT1, existingT2];
6435
- if (chordsCross(newChord, existingChord, perimeter)) {
5824
+ if (chordsIntersect(
5825
+ newChord,
5826
+ existingChord,
5827
+ perimeter,
5828
+ port1,
5829
+ port2,
5830
+ existingPort1,
5831
+ existingPort2
5832
+ )) {
6436
5833
  crossings++;
6437
5834
  }
6438
5835
  }
@@ -6450,16 +5847,20 @@ function computeCrossingAssignmentsForPolygon(region, port1, port2) {
6450
5847
  const crossingAssignments = [];
6451
5848
  const assignments = region.assignments ?? [];
6452
5849
  for (const assignment of assignments) {
6453
- const existingT1 = getPortPerimeterTInRegion(
6454
- assignment.regionPort1,
6455
- region
6456
- );
6457
- const existingT2 = getPortPerimeterTInRegion(
6458
- assignment.regionPort2,
6459
- region
6460
- );
5850
+ const existingPort1 = assignment.regionPort1;
5851
+ const existingPort2 = assignment.regionPort2;
5852
+ const existingT1 = getPortPerimeterTInRegion(existingPort1, region);
5853
+ const existingT2 = getPortPerimeterTInRegion(existingPort2, region);
6461
5854
  const existingChord = [existingT1, existingT2];
6462
- if (chordsCross(newChord, existingChord, perimeter)) {
5855
+ if (chordsIntersect(
5856
+ newChord,
5857
+ existingChord,
5858
+ perimeter,
5859
+ port1,
5860
+ port2,
5861
+ existingPort1,
5862
+ existingPort2
5863
+ )) {
6463
5864
  crossingAssignments.push(assignment);
6464
5865
  }
6465
5866
  }
@@ -16296,7 +15697,7 @@ function projectOntoSegment(p, a, b) {
16296
15697
  const distance3 = Math.sqrt((p.x - closestX) ** 2 + (p.y - closestY) ** 2);
16297
15698
  return { t, distance: distance3 };
16298
15699
  }
16299
- function rangeOverlap2(t1Start, t1End, t2Start, t2End) {
15700
+ function rangeOverlap(t1Start, t1End, t2Start, t2End) {
16300
15701
  const min1 = Math.min(t1Start, t1End);
16301
15702
  const max1 = Math.max(t1Start, t1End);
16302
15703
  const min2 = Math.min(t2Start, t2End);
@@ -16318,7 +15719,7 @@ function findSharedEdges(polygon1, polygon2, tolerance = 0.01) {
16318
15719
  const proj1 = projectOntoSegment(b1, a1, a2);
16319
15720
  const proj2 = projectOntoSegment(b2, a1, a2);
16320
15721
  if (proj1.distance > tolerance || proj2.distance > tolerance) continue;
16321
- const overlap = rangeOverlap2(0, 1, proj1.t, proj2.t);
15722
+ const overlap = rangeOverlap(0, 1, proj1.t, proj2.t);
16322
15723
  if (!overlap) continue;
16323
15724
  const dx = a2.x - a1.x;
16324
15725
  const dy = a2.y - a1.y;
@@ -16330,10 +15731,7 @@ function findSharedEdges(polygon1, polygon2, tolerance = 0.01) {
16330
15731
  x: a1.x + overlap.to * dx,
16331
15732
  y: a1.y + overlap.to * dy
16332
15733
  };
16333
- const edgeLen = Math.sqrt((to.x - from.x) ** 2 + (to.y - from.y) ** 2);
16334
- if (edgeLen > tolerance) {
16335
- sharedEdges.push({ from, to });
16336
- }
15734
+ sharedEdges.push({ from, to });
16337
15735
  }
16338
15736
  }
16339
15737
  return sharedEdges;
@@ -16356,9 +15754,7 @@ function createPortsAlongEdge(edge, portPitch = 0.4) {
16356
15754
  }
16357
15755
 
16358
15756
  // lib/ViaGraphSolver/via-graph-generator/generateConvexViaTopologyRegions.ts
16359
- var DEFAULT_PORT_PITCH2 = 0.4;
16360
- var DEFAULT_TILE_WIDTH = 3.727;
16361
- var DEFAULT_TILE_HEIGHT = 4.03;
15757
+ var DEFAULT_PORT_PITCH = 0.4;
16362
15758
  var DEFAULT_CLEARANCE = 0.1;
16363
15759
  function deduplicateConsecutivePoints(points, tolerance = 1e-3) {
16364
15760
  if (points.length <= 1) return points;
@@ -16403,35 +15799,6 @@ function centroid(points) {
16403
15799
  }
16404
15800
  return { x: cx / points.length, y: cy / points.length };
16405
15801
  }
16406
- function classifySideFromBounds(point2, bounds) {
16407
- const distances = {
16408
- left: Math.abs(point2.x - bounds.minX),
16409
- right: Math.abs(point2.x - bounds.maxX),
16410
- bottom: Math.abs(point2.y - bounds.minY),
16411
- top: Math.abs(point2.y - bounds.maxY)
16412
- };
16413
- let bestSide = "left";
16414
- let bestDistance = distances.left;
16415
- for (const side of ["right", "bottom", "top"]) {
16416
- if (distances[side] < bestDistance) {
16417
- bestSide = side;
16418
- bestDistance = distances[side];
16419
- }
16420
- }
16421
- return bestSide;
16422
- }
16423
- function toCandidateKey(regionId, point2) {
16424
- return `${regionId}:${point2.x.toFixed(6)},${point2.y.toFixed(6)}`;
16425
- }
16426
- function compareCandidateQuality(a, b) {
16427
- if (Math.abs(a.primaryDistance - b.primaryDistance) > 1e-6) {
16428
- return b.primaryDistance - a.primaryDistance;
16429
- }
16430
- if (Math.abs(a.orthDistance - b.orthDistance) > 1e-6) {
16431
- return a.orthDistance - b.orthDistance;
16432
- }
16433
- return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
16434
- }
16435
15802
  function createRegionFromPolygon(regionId, polygon2, opts) {
16436
15803
  const bounds = boundsFromPolygon(polygon2);
16437
15804
  return {
@@ -16502,7 +15869,7 @@ function translateVias(vias, dx, dy, prefix) {
16502
15869
  }
16503
15870
  }));
16504
15871
  }
16505
- function translateRouteSegments2(routeSegments, dx, dy, prefix) {
15872
+ function translateRouteSegments(routeSegments, dx, dy, prefix) {
16506
15873
  return routeSegments.map((segment2) => ({
16507
15874
  routeId: `${prefix}:${segment2.routeId}`,
16508
15875
  fromPort: `${prefix}:${segment2.fromPort}`,
@@ -16514,10 +15881,133 @@ function translateRouteSegments2(routeSegments, dx, dy, prefix) {
16514
15881
  }))
16515
15882
  }));
16516
15883
  }
15884
+ function translatePolygon(polygon2, dx, dy) {
15885
+ return polygon2.map((p) => ({ x: p.x + dx, y: p.y + dy }));
15886
+ }
15887
+ function rectPolygonFromBounds(b) {
15888
+ return [
15889
+ { x: b.minX, y: b.minY },
15890
+ { x: b.maxX, y: b.minY },
15891
+ { x: b.maxX, y: b.maxY },
15892
+ { x: b.minX, y: b.maxY }
15893
+ ];
15894
+ }
15895
+ function extendViaRegionToTileEdge(polygon2, tileBounds, threshold = 0.1) {
15896
+ if (polygon2.length === 0) return polygon2;
15897
+ const polyBounds = boundsFromPolygon(polygon2);
15898
+ const distToLeft = polyBounds.minX - tileBounds.minX;
15899
+ const distToRight = tileBounds.maxX - polyBounds.maxX;
15900
+ const distToBottom = polyBounds.minY - tileBounds.minY;
15901
+ const distToTop = tileBounds.maxY - polyBounds.maxY;
15902
+ const extendLeft = distToLeft > 0 && distToLeft < threshold;
15903
+ const extendRight = distToRight > 0 && distToRight < threshold;
15904
+ const extendBottom = distToBottom > 0 && distToBottom < threshold;
15905
+ const extendTop = distToTop > 0 && distToTop < threshold;
15906
+ if (!extendLeft && !extendRight && !extendBottom && !extendTop) {
15907
+ return polygon2;
15908
+ }
15909
+ const result = polygon2.map((p) => {
15910
+ let x = p.x;
15911
+ let y = p.y;
15912
+ if (extendLeft && Math.abs(p.x - polyBounds.minX) < 1e-3) {
15913
+ x = tileBounds.minX;
15914
+ }
15915
+ if (extendRight && Math.abs(p.x - polyBounds.maxX) < 1e-3) {
15916
+ x = tileBounds.maxX;
15917
+ }
15918
+ if (extendBottom && Math.abs(p.y - polyBounds.minY) < 1e-3) {
15919
+ y = tileBounds.minY;
15920
+ }
15921
+ if (extendTop && Math.abs(p.y - polyBounds.maxY) < 1e-3) {
15922
+ y = tileBounds.maxY;
15923
+ }
15924
+ return { x, y };
15925
+ });
15926
+ return deduplicateConsecutivePoints(result);
15927
+ }
15928
+ function pointInPolygon(point2, polygon2) {
15929
+ let inside2 = false;
15930
+ const n = polygon2.length;
15931
+ for (let i = 0, j = n - 1; i < n; j = i++) {
15932
+ const xi = polygon2[i].x;
15933
+ const yi = polygon2[i].y;
15934
+ const xj = polygon2[j].x;
15935
+ const yj = polygon2[j].y;
15936
+ 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;
15937
+ if (onEdge) return true;
15938
+ if (yi > point2.y !== yj > point2.y) {
15939
+ const intersectX = (xj - xi) * (point2.y - yi) / (yj - yi) + xi;
15940
+ if (point2.x < intersectX) {
15941
+ inside2 = !inside2;
15942
+ }
15943
+ }
15944
+ }
15945
+ return inside2;
15946
+ }
15947
+ function findRegionContainingPoint(point2, regions) {
15948
+ for (const region of regions) {
15949
+ if (region.d.polygon && pointInPolygon(point2, region.d.polygon)) {
15950
+ return region;
15951
+ }
15952
+ }
15953
+ return null;
15954
+ }
15955
+ function computeUnitTileTemplate(viaTile, tileWidth, tileHeight, clearance, concavityTolerance) {
15956
+ const halfWidth = tileWidth / 2;
15957
+ const halfHeight = tileHeight / 2;
15958
+ const tileBounds = {
15959
+ minX: -halfWidth,
15960
+ maxX: halfWidth,
15961
+ minY: -halfHeight,
15962
+ maxY: halfHeight
15963
+ };
15964
+ const viaRegions = [];
15965
+ for (const [netName, vias] of Object.entries(viaTile.viasByNet)) {
15966
+ if (vias.length === 0) continue;
15967
+ const polygon2 = generateViaRegionPolygon(vias);
15968
+ if (polygon2.length === 0) continue;
15969
+ viaRegions.push({
15970
+ netName,
15971
+ polygon: polygon2,
15972
+ bounds: boundsFromPolygon(polygon2),
15973
+ center: centroid(polygon2)
15974
+ });
15975
+ }
15976
+ const obstaclePolygons = viaRegions.map((r) => ({
15977
+ points: extendViaRegionToTileEdge(r.polygon, tileBounds)
15978
+ }));
15979
+ const solver = new ConvexRegionsSolver({
15980
+ bounds: tileBounds,
15981
+ polygons: obstaclePolygons,
15982
+ clearance,
15983
+ concavityTolerance
15984
+ });
15985
+ solver.solve();
15986
+ const solverOutput = solver.getOutput();
15987
+ if (!solverOutput) {
15988
+ throw new Error("ConvexRegionsSolver failed to compute unit tile regions");
15989
+ }
15990
+ const convexRegions = solverOutput.regions.map((polygon2) => ({
15991
+ polygon: polygon2,
15992
+ bounds: boundsFromPolygon(polygon2),
15993
+ center: centroid(polygon2)
15994
+ }));
15995
+ return {
15996
+ viaRegions,
15997
+ convexRegions,
15998
+ tileWidth,
15999
+ tileHeight
16000
+ };
16001
+ }
16517
16002
  function generateConvexViaTopologyRegions(opts) {
16518
- const tileWidth = opts.tileWidth ?? opts.tileSize ?? DEFAULT_TILE_WIDTH;
16519
- const tileHeight = opts.tileHeight ?? opts.tileSize ?? DEFAULT_TILE_HEIGHT;
16520
- const portPitch = opts.portPitch ?? DEFAULT_PORT_PITCH2;
16003
+ const tileWidth = opts.tileWidth ?? opts.tileSize ?? opts.viaTile.tileWidth;
16004
+ const tileHeight = opts.tileHeight ?? opts.tileSize ?? opts.viaTile.tileHeight;
16005
+ if (tileWidth === void 0 || tileHeight === void 0) {
16006
+ throw new Error(
16007
+ "tileWidth and tileHeight must be provided either in opts or in viaTile"
16008
+ );
16009
+ }
16010
+ const portPitch = opts.portPitch ?? DEFAULT_PORT_PITCH;
16521
16011
  const clearance = opts.clearance ?? DEFAULT_CLEARANCE;
16522
16012
  const concavityTolerance = opts.concavityTolerance ?? 0;
16523
16013
  const { bounds, viaTile: inputViaTile } = opts;
@@ -16530,18 +16020,79 @@ function generateConvexViaTopologyRegions(opts) {
16530
16020
  const allPorts = [];
16531
16021
  const viaTile = { viasByNet: {}, routeSegments: [] };
16532
16022
  const viaRegions = [];
16023
+ const convexRegions = [];
16533
16024
  const gridWidth = cols * tileWidth;
16534
16025
  const gridHeight = rows * tileHeight;
16535
16026
  const gridMinX = bounds.minX + (width - gridWidth) / 2;
16536
16027
  const gridMinY = bounds.minY + (height - gridHeight) / 2;
16028
+ const gridMaxX = gridMinX + gridWidth;
16029
+ const gridMaxY = gridMinY + gridHeight;
16537
16030
  const halfWidth = tileWidth / 2;
16538
16031
  const halfHeight = tileHeight / 2;
16032
+ let portIdCounter = 0;
16033
+ const usedPortPositions = /* @__PURE__ */ new Set();
16034
+ const getPortPosKey = (x, y) => `${x.toFixed(4)},${y.toFixed(4)}`;
16035
+ const createPort = (portId, region1, region2, pos) => {
16036
+ const posKey = getPortPosKey(pos.x, pos.y);
16037
+ if (usedPortPositions.has(posKey)) {
16038
+ return null;
16039
+ }
16040
+ usedPortPositions.add(posKey);
16041
+ const port = {
16042
+ portId,
16043
+ region1,
16044
+ region2,
16045
+ d: { x: pos.x, y: pos.y }
16046
+ };
16047
+ region1.ports.push(port);
16048
+ region2.ports.push(port);
16049
+ allPorts.push(port);
16050
+ return port;
16051
+ };
16052
+ let unitTileTemplate = null;
16539
16053
  if (rows > 0 && cols > 0) {
16054
+ unitTileTemplate = computeUnitTileTemplate(
16055
+ inputViaTile,
16056
+ tileWidth,
16057
+ tileHeight,
16058
+ clearance,
16059
+ concavityTolerance
16060
+ );
16061
+ }
16062
+ if (rows > 0 && cols > 0 && unitTileTemplate) {
16540
16063
  for (let row = 0; row < rows; row++) {
16541
16064
  for (let col = 0; col < cols; col++) {
16542
16065
  const tileCenterX = gridMinX + col * tileWidth + halfWidth;
16543
16066
  const tileCenterY = gridMinY + row * tileHeight + halfHeight;
16544
16067
  const prefix = `t${row}_${col}`;
16068
+ for (const templateViaRegion of unitTileTemplate.viaRegions) {
16069
+ const translatedPolygon = translatePolygon(
16070
+ templateViaRegion.polygon,
16071
+ tileCenterX,
16072
+ tileCenterY
16073
+ );
16074
+ const viaRegion = createRegionFromPolygon(
16075
+ `${prefix}:v:${templateViaRegion.netName}`,
16076
+ translatedPolygon,
16077
+ { isViaRegion: true }
16078
+ );
16079
+ viaRegions.push(viaRegion);
16080
+ allRegions.push(viaRegion);
16081
+ }
16082
+ for (let i = 0; i < unitTileTemplate.convexRegions.length; i++) {
16083
+ const templateConvexRegion = unitTileTemplate.convexRegions[i];
16084
+ const translatedPolygon = translatePolygon(
16085
+ templateConvexRegion.polygon,
16086
+ tileCenterX,
16087
+ tileCenterY
16088
+ );
16089
+ const convexRegion = createRegionFromPolygon(
16090
+ `${prefix}:convex:${i}`,
16091
+ translatedPolygon
16092
+ );
16093
+ convexRegions.push(convexRegion);
16094
+ allRegions.push(convexRegion);
16095
+ }
16545
16096
  for (const [netName, vias] of Object.entries(viasByNet)) {
16546
16097
  if (vias.length === 0) continue;
16547
16098
  const translatedVias = translateVias(
@@ -16554,18 +16105,9 @@ function generateConvexViaTopologyRegions(opts) {
16554
16105
  viaTile.viasByNet[netName] = [];
16555
16106
  }
16556
16107
  viaTile.viasByNet[netName].push(...translatedVias);
16557
- const polygon2 = generateViaRegionPolygon(translatedVias);
16558
- if (polygon2.length === 0) continue;
16559
- const viaRegion = createRegionFromPolygon(
16560
- `${prefix}:v:${netName}`,
16561
- polygon2,
16562
- { isViaRegion: true }
16563
- );
16564
- viaRegions.push(viaRegion);
16565
- allRegions.push(viaRegion);
16566
16108
  }
16567
16109
  viaTile.routeSegments.push(
16568
- ...translateRouteSegments2(
16110
+ ...translateRouteSegments(
16569
16111
  routeSegments,
16570
16112
  tileCenterX,
16571
16113
  tileCenterY,
@@ -16575,55 +16117,306 @@ function generateConvexViaTopologyRegions(opts) {
16575
16117
  }
16576
16118
  }
16577
16119
  }
16578
- const obstaclePolygons = viaRegions.map((r) => ({
16579
- points: r.d.polygon
16580
- }));
16581
- const solverInput = {
16582
- bounds,
16583
- polygons: obstaclePolygons,
16584
- clearance,
16585
- concavityTolerance
16586
- };
16587
- const solver = new ConvexRegionsSolver(solverInput);
16588
- solver.solve();
16589
- const solverOutput = solver.getOutput();
16590
- if (!solverOutput) {
16591
- throw new Error("ConvexRegionsSolver failed to compute regions");
16120
+ const fillerRegions = [];
16121
+ const topMargin = bounds.maxY - gridMaxY;
16122
+ const bottomMargin = gridMinY - bounds.minY;
16123
+ const leftMargin = gridMinX - bounds.minX;
16124
+ const rightMargin = bounds.maxX - gridMaxX;
16125
+ const verticalMargin = Math.max(topMargin, bottomMargin);
16126
+ const horizontalMargin = Math.max(leftMargin, rightMargin);
16127
+ const topBottomGetCorners = verticalMargin >= horizontalMargin;
16128
+ const topMinX = topBottomGetCorners ? bounds.minX : gridMinX;
16129
+ const topMaxX = topBottomGetCorners ? bounds.maxX : gridMaxX;
16130
+ const bottomMinX = topBottomGetCorners ? bounds.minX : gridMinX;
16131
+ const bottomMaxX = topBottomGetCorners ? bounds.maxX : gridMaxX;
16132
+ const leftMinY = topBottomGetCorners ? gridMinY : bounds.minY;
16133
+ const leftMaxY = topBottomGetCorners ? gridMaxY : bounds.maxY;
16134
+ const rightMinY = topBottomGetCorners ? gridMinY : bounds.minY;
16135
+ const rightMaxY = topBottomGetCorners ? gridMaxY : bounds.maxY;
16136
+ if (topMargin > 1e-3) {
16137
+ const topWidth = topMaxX - topMinX;
16138
+ const targetStripWidth = Math.max(topMargin, portPitch);
16139
+ const numTopStrips = Math.max(1, Math.floor(topWidth / targetStripWidth));
16140
+ const stripWidth = topWidth / numTopStrips;
16141
+ for (let i = 0; i < numTopStrips; i++) {
16142
+ const fillerBounds = {
16143
+ minX: topMinX + i * stripWidth,
16144
+ maxX: topMinX + (i + 1) * stripWidth,
16145
+ minY: gridMaxY,
16146
+ maxY: bounds.maxY
16147
+ };
16148
+ const regionId = `filler:top:${i}`;
16149
+ const filler = createRegionFromPolygon(
16150
+ regionId,
16151
+ rectPolygonFromBounds(fillerBounds)
16152
+ );
16153
+ fillerRegions.push(filler);
16154
+ allRegions.push(filler);
16155
+ }
16592
16156
  }
16593
- const convexRegions = solverOutput.regions.map(
16594
- (polygon2, i) => createRegionFromPolygon(`convex:${i}`, polygon2)
16595
- );
16596
- allRegions.push(...convexRegions);
16597
- let portIdCounter = 0;
16598
- for (let i = 0; i < convexRegions.length; i++) {
16599
- for (let j = i + 1; j < convexRegions.length; j++) {
16600
- const region1 = convexRegions[i];
16601
- const region2 = convexRegions[j];
16157
+ if (bottomMargin > 1e-3) {
16158
+ const bottomWidth = bottomMaxX - bottomMinX;
16159
+ const targetStripWidth = Math.max(bottomMargin, portPitch);
16160
+ const numBottomStrips = Math.max(
16161
+ 1,
16162
+ Math.floor(bottomWidth / targetStripWidth)
16163
+ );
16164
+ const stripWidth = bottomWidth / numBottomStrips;
16165
+ for (let i = 0; i < numBottomStrips; i++) {
16166
+ const fillerBounds = {
16167
+ minX: bottomMinX + i * stripWidth,
16168
+ maxX: bottomMinX + (i + 1) * stripWidth,
16169
+ minY: bounds.minY,
16170
+ maxY: gridMinY
16171
+ };
16172
+ const regionId = `filler:bottom:${i}`;
16173
+ const filler = createRegionFromPolygon(
16174
+ regionId,
16175
+ rectPolygonFromBounds(fillerBounds)
16176
+ );
16177
+ fillerRegions.push(filler);
16178
+ allRegions.push(filler);
16179
+ }
16180
+ }
16181
+ if (leftMargin > 1e-3) {
16182
+ const leftHeight = leftMaxY - leftMinY;
16183
+ const targetStripHeight = Math.max(leftMargin, portPitch);
16184
+ const numLeftStrips = Math.max(
16185
+ 1,
16186
+ Math.floor(leftHeight / targetStripHeight)
16187
+ );
16188
+ const stripHeight = leftHeight / numLeftStrips;
16189
+ for (let i = 0; i < numLeftStrips; i++) {
16190
+ const fillerBounds = {
16191
+ minX: bounds.minX,
16192
+ maxX: gridMinX,
16193
+ minY: leftMinY + i * stripHeight,
16194
+ maxY: leftMinY + (i + 1) * stripHeight
16195
+ };
16196
+ const regionId = `filler:left:${i}`;
16197
+ const filler = createRegionFromPolygon(
16198
+ regionId,
16199
+ rectPolygonFromBounds(fillerBounds)
16200
+ );
16201
+ fillerRegions.push(filler);
16202
+ allRegions.push(filler);
16203
+ }
16204
+ }
16205
+ if (rightMargin > 1e-3) {
16206
+ const rightHeight = rightMaxY - rightMinY;
16207
+ const targetStripHeight = Math.max(rightMargin, portPitch);
16208
+ const numRightStrips = Math.max(
16209
+ 1,
16210
+ Math.floor(rightHeight / targetStripHeight)
16211
+ );
16212
+ const stripHeight = rightHeight / numRightStrips;
16213
+ for (let i = 0; i < numRightStrips; i++) {
16214
+ const fillerBounds = {
16215
+ minX: gridMaxX,
16216
+ maxX: bounds.maxX,
16217
+ minY: rightMinY + i * stripHeight,
16218
+ maxY: rightMinY + (i + 1) * stripHeight
16219
+ };
16220
+ const regionId = `filler:right:${i}`;
16221
+ const filler = createRegionFromPolygon(
16222
+ regionId,
16223
+ rectPolygonFromBounds(fillerBounds)
16224
+ );
16225
+ fillerRegions.push(filler);
16226
+ allRegions.push(filler);
16227
+ }
16228
+ }
16229
+ if (unitTileTemplate && rows > 0 && cols > 0) {
16230
+ const regionsPerTile = unitTileTemplate.convexRegions.length;
16231
+ for (let row = 0; row < rows; row++) {
16232
+ for (let col = 0; col < cols; col++) {
16233
+ const tileIndex = row * cols + col;
16234
+ const tileStartIdx = tileIndex * regionsPerTile;
16235
+ for (let i = 0; i < regionsPerTile; i++) {
16236
+ for (let j = i + 1; j < regionsPerTile; j++) {
16237
+ const region1 = convexRegions[tileStartIdx + i];
16238
+ const region2 = convexRegions[tileStartIdx + j];
16239
+ const sharedEdges = findSharedEdges(
16240
+ region1.d.polygon,
16241
+ region2.d.polygon,
16242
+ clearance * 2
16243
+ );
16244
+ for (const edge of sharedEdges) {
16245
+ const portPositions = createPortsAlongEdge(edge, portPitch);
16246
+ for (const pos of portPositions) {
16247
+ createPort(
16248
+ `t${row}_${col}:convex:${i}-${j}:${portIdCounter++}`,
16249
+ region1,
16250
+ region2,
16251
+ pos
16252
+ );
16253
+ }
16254
+ }
16255
+ }
16256
+ }
16257
+ }
16258
+ }
16259
+ }
16260
+ if (unitTileTemplate && rows > 0 && cols > 0) {
16261
+ const convexPerTile = unitTileTemplate.convexRegions.length;
16262
+ const viasPerTile = unitTileTemplate.viaRegions.length;
16263
+ const numVerticalPorts = Math.floor(tileHeight / portPitch);
16264
+ const verticalPortYOffsets = [];
16265
+ for (let i = 0; i < numVerticalPorts; i++) {
16266
+ verticalPortYOffsets.push(-halfHeight + (i + 0.5) * portPitch);
16267
+ }
16268
+ const numHorizontalPorts = Math.floor(tileWidth / portPitch);
16269
+ const horizontalPortXOffsets = [];
16270
+ for (let i = 0; i < numHorizontalPorts; i++) {
16271
+ horizontalPortXOffsets.push(-halfWidth + (i + 0.5) * portPitch);
16272
+ }
16273
+ for (let row = 0; row < rows; row++) {
16274
+ for (let col = 0; col < cols; col++) {
16275
+ const tileIndex = row * cols + col;
16276
+ const convexStartIdx = tileIndex * convexPerTile;
16277
+ const viaStartIdx = tileIndex * viasPerTile;
16278
+ const tileCenterX = gridMinX + col * tileWidth + halfWidth;
16279
+ const tileCenterY = gridMinY + row * tileHeight + halfHeight;
16280
+ const tileConvexRegions = convexRegions.slice(
16281
+ convexStartIdx,
16282
+ convexStartIdx + convexPerTile
16283
+ );
16284
+ const tileViaRegions = viaRegions.slice(
16285
+ viaStartIdx,
16286
+ viaStartIdx + viasPerTile
16287
+ );
16288
+ const tileAllRegions = [...tileConvexRegions, ...tileViaRegions];
16289
+ if (col + 1 < cols) {
16290
+ const rightTileIndex = row * cols + (col + 1);
16291
+ const rightConvexStartIdx = rightTileIndex * convexPerTile;
16292
+ const rightViaStartIdx = rightTileIndex * viasPerTile;
16293
+ const rightTileConvexRegions = convexRegions.slice(
16294
+ rightConvexStartIdx,
16295
+ rightConvexStartIdx + convexPerTile
16296
+ );
16297
+ const rightTileViaRegions = viaRegions.slice(
16298
+ rightViaStartIdx,
16299
+ rightViaStartIdx + viasPerTile
16300
+ );
16301
+ const rightTileAllRegions = [
16302
+ ...rightTileConvexRegions,
16303
+ ...rightTileViaRegions
16304
+ ];
16305
+ const boundaryX = tileCenterX + halfWidth;
16306
+ for (const yOffset of verticalPortYOffsets) {
16307
+ const portY = tileCenterY + yOffset;
16308
+ const pointInCurrentTile = { x: boundaryX - 0.01, y: portY };
16309
+ const pointInRightTile = { x: boundaryX + 0.01, y: portY };
16310
+ const region1 = findRegionContainingPoint(
16311
+ pointInCurrentTile,
16312
+ tileAllRegions
16313
+ );
16314
+ const region2 = findRegionContainingPoint(
16315
+ pointInRightTile,
16316
+ rightTileAllRegions
16317
+ );
16318
+ if (region1 && region2) {
16319
+ createPort(
16320
+ `tile:${row}_${col}-${row}_${col + 1}:${portIdCounter++}`,
16321
+ region1,
16322
+ region2,
16323
+ { x: boundaryX, y: portY }
16324
+ );
16325
+ }
16326
+ }
16327
+ }
16328
+ if (row + 1 < rows) {
16329
+ const topTileIndex = (row + 1) * cols + col;
16330
+ const topConvexStartIdx = topTileIndex * convexPerTile;
16331
+ const topViaStartIdx = topTileIndex * viasPerTile;
16332
+ const topTileConvexRegions = convexRegions.slice(
16333
+ topConvexStartIdx,
16334
+ topConvexStartIdx + convexPerTile
16335
+ );
16336
+ const topTileViaRegions = viaRegions.slice(
16337
+ topViaStartIdx,
16338
+ topViaStartIdx + viasPerTile
16339
+ );
16340
+ const topTileAllRegions = [
16341
+ ...topTileConvexRegions,
16342
+ ...topTileViaRegions
16343
+ ];
16344
+ const boundaryY = tileCenterY + halfHeight;
16345
+ for (const xOffset of horizontalPortXOffsets) {
16346
+ const portX = tileCenterX + xOffset;
16347
+ const pointInCurrentTile = { x: portX, y: boundaryY - 0.01 };
16348
+ const pointInTopTile = { x: portX, y: boundaryY + 0.01 };
16349
+ const region1 = findRegionContainingPoint(
16350
+ pointInCurrentTile,
16351
+ tileAllRegions
16352
+ );
16353
+ const region2 = findRegionContainingPoint(
16354
+ pointInTopTile,
16355
+ topTileAllRegions
16356
+ );
16357
+ if (region1 && region2) {
16358
+ createPort(
16359
+ `tile:${row}_${col}-${row + 1}_${col}:${portIdCounter++}`,
16360
+ region1,
16361
+ region2,
16362
+ { x: portX, y: boundaryY }
16363
+ );
16364
+ }
16365
+ }
16366
+ }
16367
+ }
16368
+ }
16369
+ }
16370
+ for (const fillerRegion of fillerRegions) {
16371
+ const tileRegions = [...convexRegions, ...viaRegions];
16372
+ for (const tileRegion of tileRegions) {
16373
+ const sharedEdges = findSharedEdges(
16374
+ tileRegion.d.polygon,
16375
+ fillerRegion.d.polygon,
16376
+ clearance * 2
16377
+ );
16378
+ for (const edge of sharedEdges) {
16379
+ const portPositions = createPortsAlongEdge(edge, portPitch);
16380
+ for (const pos of portPositions) {
16381
+ createPort(
16382
+ `filler:${tileRegion.regionId}-${fillerRegion.regionId}:${portIdCounter++}`,
16383
+ tileRegion,
16384
+ fillerRegion,
16385
+ pos
16386
+ );
16387
+ }
16388
+ }
16389
+ }
16390
+ }
16391
+ for (let i = 0; i < fillerRegions.length; i++) {
16392
+ for (let j = i + 1; j < fillerRegions.length; j++) {
16393
+ const region1 = fillerRegions[i];
16394
+ const region2 = fillerRegions[j];
16602
16395
  const sharedEdges = findSharedEdges(
16603
16396
  region1.d.polygon,
16604
16397
  region2.d.polygon,
16605
- clearance * 2
16606
- // tolerance slightly larger than clearance
16398
+ 0.01
16607
16399
  );
16608
16400
  for (const edge of sharedEdges) {
16401
+ const edgeLength = Math.sqrt(
16402
+ (edge.to.x - edge.from.x) ** 2 + (edge.to.y - edge.from.y) ** 2
16403
+ );
16404
+ if (edgeLength < portPitch) {
16405
+ continue;
16406
+ }
16609
16407
  const portPositions = createPortsAlongEdge(edge, portPitch);
16610
16408
  for (const pos of portPositions) {
16611
- const port = {
16612
- portId: `convex:${i}-${j}:${portIdCounter++}`,
16409
+ createPort(
16410
+ `filler:${region1.regionId}-${region2.regionId}:${portIdCounter++}`,
16613
16411
  region1,
16614
16412
  region2,
16615
- d: { x: pos.x, y: pos.y }
16616
- };
16617
- region1.ports.push(port);
16618
- region2.ports.push(port);
16619
- allPorts.push(port);
16413
+ pos
16414
+ );
16620
16415
  }
16621
16416
  }
16622
16417
  }
16623
16418
  }
16624
16419
  for (const viaRegion of viaRegions) {
16625
- const viaCenter = viaRegion.d.center;
16626
- const candidates = [];
16627
16420
  for (const convexRegion of convexRegions) {
16628
16421
  const sharedEdges = findSharedEdges(
16629
16422
  viaRegion.d.polygon,
@@ -16633,55 +16426,15 @@ function generateConvexViaTopologyRegions(opts) {
16633
16426
  for (const edge of sharedEdges) {
16634
16427
  const portPositions = createPortsAlongEdge(edge, portPitch);
16635
16428
  for (const pos of portPositions) {
16636
- const dx = pos.x - viaCenter.x;
16637
- const dy = pos.y - viaCenter.y;
16638
- const side = classifySideFromBounds(pos, viaRegion.d.bounds);
16639
- const primaryDistance = side === "left" || side === "right" ? Math.abs(dx) : Math.abs(dy);
16640
- const orthDistance = side === "left" || side === "right" ? Math.abs(dy) : Math.abs(dx);
16641
- candidates.push({
16429
+ createPort(
16430
+ `via-convex:${viaRegion.regionId}-${convexRegion.regionId}:${portIdCounter++}`,
16431
+ viaRegion,
16642
16432
  convexRegion,
16643
- position: pos,
16644
- side,
16645
- primaryDistance,
16646
- orthDistance,
16647
- key: toCandidateKey(convexRegion.regionId, pos)
16648
- });
16433
+ pos
16434
+ );
16649
16435
  }
16650
16436
  }
16651
16437
  }
16652
- if (candidates.length === 0) continue;
16653
- const selectedCandidates = [];
16654
- const selectedKeys = /* @__PURE__ */ new Set();
16655
- const addCandidate = (candidate) => {
16656
- if (!candidate) return;
16657
- if (selectedKeys.has(candidate.key)) return;
16658
- selectedCandidates.push(candidate);
16659
- selectedKeys.add(candidate.key);
16660
- };
16661
- for (const side of ["top", "bottom", "left", "right"]) {
16662
- const sideCandidate = [...candidates].filter((candidate) => candidate.side === side).sort(compareCandidateQuality)[0];
16663
- addCandidate(sideCandidate);
16664
- }
16665
- if (selectedCandidates.length < 4) {
16666
- for (const candidate of [...candidates].sort(compareCandidateQuality)) {
16667
- addCandidate(candidate);
16668
- if (selectedCandidates.length >= 4) break;
16669
- }
16670
- }
16671
- for (const selectedCandidate of selectedCandidates.slice(0, 4)) {
16672
- const port = {
16673
- portId: `via-convex:${viaRegion.regionId}-${selectedCandidate.convexRegion.regionId}:${portIdCounter++}`,
16674
- region1: viaRegion,
16675
- region2: selectedCandidate.convexRegion,
16676
- d: {
16677
- x: selectedCandidate.position.x,
16678
- y: selectedCandidate.position.y
16679
- }
16680
- };
16681
- viaRegion.ports.push(port);
16682
- selectedCandidate.convexRegion.ports.push(port);
16683
- allPorts.push(port);
16684
- }
16685
16438
  }
16686
16439
  return {
16687
16440
  regions: allRegions,
@@ -16718,8 +16471,8 @@ function createConvexViaGraphFromXYConnections(xyConnections, viaTile = via_tile
16718
16471
  } = generateConvexViaTopologyRegions({
16719
16472
  viaTile,
16720
16473
  bounds,
16721
- tileWidth: opts?.tileWidth,
16722
- tileHeight: opts?.tileHeight,
16474
+ tileWidth: opts?.tileWidth ?? viaTile.tileWidth,
16475
+ tileHeight: opts?.tileHeight ?? viaTile.tileHeight,
16723
16476
  tileSize: opts?.tileSize,
16724
16477
  portPitch: opts?.portPitch,
16725
16478
  clearance: opts?.clearance,
@@ -16736,48 +16489,6 @@ function createConvexViaGraphFromXYConnections(xyConnections, viaTile = via_tile
16736
16489
  tileCount
16737
16490
  };
16738
16491
  }
16739
-
16740
- // lib/ViaGraphSolver/via-graph-generator/createViaGraphFromXYConnections.ts
16741
- function calculateBoundsFromConnections2(xyConnections) {
16742
- if (xyConnections.length === 0) {
16743
- throw new Error("Cannot calculate bounds from empty connections array");
16744
- }
16745
- let minX = Infinity;
16746
- let maxX = -Infinity;
16747
- let minY = Infinity;
16748
- let maxY = -Infinity;
16749
- for (const conn of xyConnections) {
16750
- minX = Math.min(minX, conn.start.x, conn.end.x);
16751
- maxX = Math.max(maxX, conn.start.x, conn.end.x);
16752
- minY = Math.min(minY, conn.start.y, conn.end.y);
16753
- maxY = Math.max(maxY, conn.start.y, conn.end.y);
16754
- }
16755
- return { minX, maxX, minY, maxY };
16756
- }
16757
- function createViaGraphFromXYConnections(xyConnections, viaTile, opts) {
16758
- const bounds = calculateBoundsFromConnections2(xyConnections);
16759
- const {
16760
- regions,
16761
- ports,
16762
- viaTile: generatedViaTile,
16763
- tileCount
16764
- } = generateViaTopologyGrid({
16765
- viaTile,
16766
- bounds,
16767
- tileSize: opts?.tileSize,
16768
- portPitch: opts?.portPitch
16769
- });
16770
- const baseGraph = { regions, ports };
16771
- const graphWithConnections = createViaGraphWithConnections(
16772
- baseGraph,
16773
- xyConnections
16774
- );
16775
- return {
16776
- ...graphWithConnections,
16777
- viaTile: generatedViaTile,
16778
- tileCount
16779
- };
16780
- }
16781
16492
  export {
16782
16493
  ConnectBuilder,
16783
16494
  HyperGraphSolver,
@@ -16795,14 +16506,11 @@ export {
16795
16506
  convertHyperGraphToSerializedHyperGraph,
16796
16507
  createConvexViaGraphFromXYConnections,
16797
16508
  createGraphWithConnectionsFromBaseGraph,
16798
- createViaGraphFromXYConnections,
16799
16509
  createViaGraphWithConnections,
16800
16510
  generateConvexViaTopologyRegions,
16801
- generateDefaultViaTopologyGrid,
16802
16511
  generateDefaultViaTopologyRegions,
16803
16512
  generateJumperGrid,
16804
16513
  generateJumperX4Grid,
16805
- generateViaTopologyGrid,
16806
16514
  generateViaTopologyRegions,
16807
16515
  rotateGraph90Degrees,
16808
16516
  via_tile_4_regions_default as viaTile