UNPKG

@graphty/layout

Version:

graph layout algorithms based on networkx

67 lines 2.53 kB
/** * Circular layout algorithm */ import { _processParams } from '../../utils/params'; import { getNodesFromGraph } from '../../utils/graph'; import { RandomNumberGenerator } from '../../utils/random'; import { np } from '../../utils/numpy'; /** * Position nodes on a circle (2D) or sphere (3D). * * @param G - Graph or list of nodes * @param scale - Scale factor for positions * @param center - Coordinate pair around which to center the layout * @param dim - Dimension of layout (supports 2D circle or 3D sphere) * @returns Positions dictionary keyed by node */ export function circularLayout(G, scale = 1, center = null, dim = 2) { if (dim < 2) { throw new Error("cannot handle dimensions < 2"); } const processed = _processParams(G, center, dim); const nodes = getNodesFromGraph(processed.G); center = processed.center; const pos = {}; if (nodes.length === 0) { return pos; } if (nodes.length === 1) { pos[nodes[0]] = center; return pos; } if (dim === 2) { // 2D circle layout const theta = np.linspace(0, 2 * Math.PI, nodes.length + 1).slice(0, -1); nodes.forEach((node, i) => { const x = Math.cos(theta[i]) * scale + center[0]; const y = Math.sin(theta[i]) * scale + center[1]; pos[node] = [x, y]; }); } else if (dim === 3) { // 3D sphere layout using Fibonacci spiral const n = nodes.length; const goldenRatio = (1 + Math.sqrt(5)) / 2; nodes.forEach((node, i) => { // Use Fibonacci spiral for even distribution on sphere const theta = 2 * Math.PI * i / goldenRatio; const phi = Math.acos(1 - 2 * (i + 0.5) / n); const x = Math.sin(phi) * Math.cos(theta) * scale + center[0]; const y = Math.sin(phi) * Math.sin(theta) * scale + center[1]; const z = Math.cos(phi) * scale + center[2]; pos[node] = [x, y, z]; }); } else { // For higher dimensions, fall back to random on hypersphere const rng = new RandomNumberGenerator(); nodes.forEach((node) => { // Generate random point on unit hypersphere const coords = Array(dim).fill(0).map(() => rng.rand() * 2 - 1); const norm = Math.sqrt(coords.reduce((sum, c) => sum + c * c, 0)); pos[node] = coords.map((c, j) => c / norm * scale + center[j]); }); } return pos; } //# sourceMappingURL=circular.js.map