@graphty/layout
Version:
graph layout algorithms based on networkx
72 lines • 2.7 kB
JavaScript
/**
* Bipartite layout algorithm
*/
import { _processParams } from '../../utils/params';
import { getNodesFromGraph } from '../../utils/graph';
import { rescaleLayout } from '../../utils/rescale';
/**
* Position nodes in two straight lines (bipartite layout).
*
* @param G - Graph or list of nodes
* @param nodes - Nodes in one node set of the graph
* @param align - The alignment of nodes: 'vertical' or 'horizontal'
* @param scale - Scale factor for positions
* @param center - Coordinate pair around which to center the layout
* @param aspectRatio - The ratio of the width to the height of the layout
* @returns Positions dictionary keyed by node
*/
export function bipartiteLayout(G, nodes = null, align = 'vertical', scale = 1, center = null, aspectRatio = 4 / 3) {
if (align !== 'vertical' && align !== 'horizontal') {
throw new Error("align must be either vertical or horizontal");
}
const processed = _processParams(G, center || [0, 0], 2);
const graph = processed.G;
center = processed.center;
const allNodes = getNodesFromGraph(graph);
if (allNodes.length === 0) {
return {};
}
// If nodes not provided, try to determine bipartite sets
if (!nodes) {
// A simple heuristic for bipartite detection: use nodes with even/odd indices
// This is a simplification, in Python NetworkX has bipartite.sets()
nodes = allNodes.filter((_, i) => i % 2 === 0);
}
const left = new Set(nodes);
const right = new Set(allNodes.filter((n) => !left.has(n)));
const height = 1;
const width = aspectRatio * height;
const offset = [width / 2, height / 2];
const pos = {};
// Position nodes in the left set
const leftNodes = [...left];
leftNodes.forEach((node, i) => {
const x = 0;
const y = i * height / (leftNodes.length || 1);
pos[node] = [x, y];
});
// Position nodes in the right set
const rightNodes = [...right];
rightNodes.forEach((node, i) => {
const x = width;
const y = i * height / (rightNodes.length || 1);
pos[node] = [x, y];
});
// Center positions around the origin and apply offset
for (const node in pos) {
pos[node][0] -= offset[0];
pos[node][1] -= offset[1];
}
// Rescale positions
const scaledPos = rescaleLayout(pos, scale, center);
// Handle horizontal alignment
if (align === 'horizontal') {
for (const node in scaledPos) {
const temp = scaledPos[node][0];
scaledPos[node][0] = scaledPos[node][1];
scaledPos[node][1] = temp;
}
}
return scaledPos;
}
//# sourceMappingURL=bipartite.js.map