@tscircuit/hypergraph 0.0.44 → 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.
- package/dist/index.d.ts +7 -4
- package/dist/index.js +522 -139
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1081,10 +1081,13 @@ type Bounds = {
|
|
|
1081
1081
|
/**
|
|
1082
1082
|
* Generates a via topology using convex regions computed by ConvexRegionsSolver.
|
|
1083
1083
|
*
|
|
1084
|
-
*
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
*
|
|
1084
|
+
* New tiled approach:
|
|
1085
|
+
* 1. Compute convex regions for a single unit tile (centered at origin)
|
|
1086
|
+
* 2. Replicate the tile's regions across the grid by translation
|
|
1087
|
+
* 3. Create rectangular filler regions for outer areas:
|
|
1088
|
+
* - Top/bottom regions extend horizontally across full bounds width
|
|
1089
|
+
* - Left/right regions extend vertically between top/bottom regions
|
|
1090
|
+
* 4. Create ports between adjacent tiles and between tiles and filler regions
|
|
1088
1091
|
*/
|
|
1089
1092
|
declare function generateConvexViaTopologyRegions(opts: {
|
|
1090
1093
|
viaTile: ViaTile;
|
package/dist/index.js
CHANGED
|
@@ -5778,6 +5778,32 @@ function generateDefaultViaTopologyRegions(opts) {
|
|
|
5778
5778
|
}
|
|
5779
5779
|
|
|
5780
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
|
+
}
|
|
5781
5807
|
function computeDifferentNetCrossingsForPolygon(region, port1, port2) {
|
|
5782
5808
|
const polygon2 = region.d.polygon;
|
|
5783
5809
|
if (!polygon2 || polygon2.length < 3) {
|
|
@@ -5790,16 +5816,20 @@ function computeDifferentNetCrossingsForPolygon(region, port1, port2) {
|
|
|
5790
5816
|
let crossings = 0;
|
|
5791
5817
|
const assignments = region.assignments ?? [];
|
|
5792
5818
|
for (const assignment of assignments) {
|
|
5793
|
-
const
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
);
|
|
5797
|
-
const existingT2 = getPortPerimeterTInRegion(
|
|
5798
|
-
assignment.regionPort2,
|
|
5799
|
-
region
|
|
5800
|
-
);
|
|
5819
|
+
const existingPort1 = assignment.regionPort1;
|
|
5820
|
+
const existingPort2 = assignment.regionPort2;
|
|
5821
|
+
const existingT1 = getPortPerimeterTInRegion(existingPort1, region);
|
|
5822
|
+
const existingT2 = getPortPerimeterTInRegion(existingPort2, region);
|
|
5801
5823
|
const existingChord = [existingT1, existingT2];
|
|
5802
|
-
if (
|
|
5824
|
+
if (chordsIntersect(
|
|
5825
|
+
newChord,
|
|
5826
|
+
existingChord,
|
|
5827
|
+
perimeter,
|
|
5828
|
+
port1,
|
|
5829
|
+
port2,
|
|
5830
|
+
existingPort1,
|
|
5831
|
+
existingPort2
|
|
5832
|
+
)) {
|
|
5803
5833
|
crossings++;
|
|
5804
5834
|
}
|
|
5805
5835
|
}
|
|
@@ -5817,16 +5847,20 @@ function computeCrossingAssignmentsForPolygon(region, port1, port2) {
|
|
|
5817
5847
|
const crossingAssignments = [];
|
|
5818
5848
|
const assignments = region.assignments ?? [];
|
|
5819
5849
|
for (const assignment of assignments) {
|
|
5820
|
-
const
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
);
|
|
5824
|
-
const existingT2 = getPortPerimeterTInRegion(
|
|
5825
|
-
assignment.regionPort2,
|
|
5826
|
-
region
|
|
5827
|
-
);
|
|
5850
|
+
const existingPort1 = assignment.regionPort1;
|
|
5851
|
+
const existingPort2 = assignment.regionPort2;
|
|
5852
|
+
const existingT1 = getPortPerimeterTInRegion(existingPort1, region);
|
|
5853
|
+
const existingT2 = getPortPerimeterTInRegion(existingPort2, region);
|
|
5828
5854
|
const existingChord = [existingT1, existingT2];
|
|
5829
|
-
if (
|
|
5855
|
+
if (chordsIntersect(
|
|
5856
|
+
newChord,
|
|
5857
|
+
existingChord,
|
|
5858
|
+
perimeter,
|
|
5859
|
+
port1,
|
|
5860
|
+
port2,
|
|
5861
|
+
existingPort1,
|
|
5862
|
+
existingPort2
|
|
5863
|
+
)) {
|
|
5830
5864
|
crossingAssignments.push(assignment);
|
|
5831
5865
|
}
|
|
5832
5866
|
}
|
|
@@ -15697,10 +15731,7 @@ function findSharedEdges(polygon1, polygon2, tolerance = 0.01) {
|
|
|
15697
15731
|
x: a1.x + overlap.to * dx,
|
|
15698
15732
|
y: a1.y + overlap.to * dy
|
|
15699
15733
|
};
|
|
15700
|
-
|
|
15701
|
-
if (edgeLen > tolerance) {
|
|
15702
|
-
sharedEdges.push({ from, to });
|
|
15703
|
-
}
|
|
15734
|
+
sharedEdges.push({ from, to });
|
|
15704
15735
|
}
|
|
15705
15736
|
}
|
|
15706
15737
|
return sharedEdges;
|
|
@@ -15768,35 +15799,6 @@ function centroid(points) {
|
|
|
15768
15799
|
}
|
|
15769
15800
|
return { x: cx / points.length, y: cy / points.length };
|
|
15770
15801
|
}
|
|
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
15802
|
function createRegionFromPolygon(regionId, polygon2, opts) {
|
|
15801
15803
|
const bounds = boundsFromPolygon(polygon2);
|
|
15802
15804
|
return {
|
|
@@ -15879,6 +15881,124 @@ function translateRouteSegments(routeSegments, dx, dy, prefix) {
|
|
|
15879
15881
|
}))
|
|
15880
15882
|
}));
|
|
15881
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
|
+
}
|
|
15882
16002
|
function generateConvexViaTopologyRegions(opts) {
|
|
15883
16003
|
const tileWidth = opts.tileWidth ?? opts.tileSize ?? opts.viaTile.tileWidth;
|
|
15884
16004
|
const tileHeight = opts.tileHeight ?? opts.tileSize ?? opts.viaTile.tileHeight;
|
|
@@ -15900,18 +16020,79 @@ function generateConvexViaTopologyRegions(opts) {
|
|
|
15900
16020
|
const allPorts = [];
|
|
15901
16021
|
const viaTile = { viasByNet: {}, routeSegments: [] };
|
|
15902
16022
|
const viaRegions = [];
|
|
16023
|
+
const convexRegions = [];
|
|
15903
16024
|
const gridWidth = cols * tileWidth;
|
|
15904
16025
|
const gridHeight = rows * tileHeight;
|
|
15905
16026
|
const gridMinX = bounds.minX + (width - gridWidth) / 2;
|
|
15906
16027
|
const gridMinY = bounds.minY + (height - gridHeight) / 2;
|
|
16028
|
+
const gridMaxX = gridMinX + gridWidth;
|
|
16029
|
+
const gridMaxY = gridMinY + gridHeight;
|
|
15907
16030
|
const halfWidth = tileWidth / 2;
|
|
15908
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;
|
|
15909
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) {
|
|
15910
16063
|
for (let row = 0; row < rows; row++) {
|
|
15911
16064
|
for (let col = 0; col < cols; col++) {
|
|
15912
16065
|
const tileCenterX = gridMinX + col * tileWidth + halfWidth;
|
|
15913
16066
|
const tileCenterY = gridMinY + row * tileHeight + halfHeight;
|
|
15914
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
|
+
}
|
|
15915
16096
|
for (const [netName, vias] of Object.entries(viasByNet)) {
|
|
15916
16097
|
if (vias.length === 0) continue;
|
|
15917
16098
|
const translatedVias = translateVias(
|
|
@@ -15924,15 +16105,6 @@ function generateConvexViaTopologyRegions(opts) {
|
|
|
15924
16105
|
viaTile.viasByNet[netName] = [];
|
|
15925
16106
|
}
|
|
15926
16107
|
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
16108
|
}
|
|
15937
16109
|
viaTile.routeSegments.push(
|
|
15938
16110
|
...translateRouteSegments(
|
|
@@ -15945,55 +16117,306 @@ function generateConvexViaTopologyRegions(opts) {
|
|
|
15945
16117
|
}
|
|
15946
16118
|
}
|
|
15947
16119
|
}
|
|
15948
|
-
const
|
|
15949
|
-
|
|
15950
|
-
|
|
15951
|
-
const
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
const
|
|
15958
|
-
|
|
15959
|
-
const
|
|
15960
|
-
|
|
15961
|
-
|
|
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
|
+
}
|
|
15962
16156
|
}
|
|
15963
|
-
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
15967
|
-
|
|
15968
|
-
|
|
15969
|
-
|
|
15970
|
-
|
|
15971
|
-
|
|
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];
|
|
15972
16395
|
const sharedEdges = findSharedEdges(
|
|
15973
16396
|
region1.d.polygon,
|
|
15974
16397
|
region2.d.polygon,
|
|
15975
|
-
|
|
15976
|
-
// tolerance slightly larger than clearance
|
|
16398
|
+
0.01
|
|
15977
16399
|
);
|
|
15978
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
|
+
}
|
|
15979
16407
|
const portPositions = createPortsAlongEdge(edge, portPitch);
|
|
15980
16408
|
for (const pos of portPositions) {
|
|
15981
|
-
|
|
15982
|
-
|
|
16409
|
+
createPort(
|
|
16410
|
+
`filler:${region1.regionId}-${region2.regionId}:${portIdCounter++}`,
|
|
15983
16411
|
region1,
|
|
15984
16412
|
region2,
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
region1.ports.push(port);
|
|
15988
|
-
region2.ports.push(port);
|
|
15989
|
-
allPorts.push(port);
|
|
16413
|
+
pos
|
|
16414
|
+
);
|
|
15990
16415
|
}
|
|
15991
16416
|
}
|
|
15992
16417
|
}
|
|
15993
16418
|
}
|
|
15994
16419
|
for (const viaRegion of viaRegions) {
|
|
15995
|
-
const viaCenter = viaRegion.d.center;
|
|
15996
|
-
const candidates = [];
|
|
15997
16420
|
for (const convexRegion of convexRegions) {
|
|
15998
16421
|
const sharedEdges = findSharedEdges(
|
|
15999
16422
|
viaRegion.d.polygon,
|
|
@@ -16003,55 +16426,15 @@ function generateConvexViaTopologyRegions(opts) {
|
|
|
16003
16426
|
for (const edge of sharedEdges) {
|
|
16004
16427
|
const portPositions = createPortsAlongEdge(edge, portPitch);
|
|
16005
16428
|
for (const pos of portPositions) {
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
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({
|
|
16429
|
+
createPort(
|
|
16430
|
+
`via-convex:${viaRegion.regionId}-${convexRegion.regionId}:${portIdCounter++}`,
|
|
16431
|
+
viaRegion,
|
|
16012
16432
|
convexRegion,
|
|
16013
|
-
|
|
16014
|
-
|
|
16015
|
-
primaryDistance,
|
|
16016
|
-
orthDistance,
|
|
16017
|
-
key: toCandidateKey(convexRegion.regionId, pos)
|
|
16018
|
-
});
|
|
16433
|
+
pos
|
|
16434
|
+
);
|
|
16019
16435
|
}
|
|
16020
16436
|
}
|
|
16021
16437
|
}
|
|
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
16438
|
}
|
|
16056
16439
|
return {
|
|
16057
16440
|
regions: allRegions,
|