UNPKG

vis-network

Version:

A dynamic, browser-based visualization library.

101 lines (87 loc) 2.39 kB
type Levels = Record<string | number, number>; interface Edge { connected: boolean; from: Node; fromId: string | number; to: Node; toId: string | number; } interface Node { id: string | number; edges: Edge[]; } /** * Try to assign levels to nodes according to their positions in the cyclic “hierarchy”. * * @param nodes - Nodes of the graph. * @param levels - If present levels will be added to it, if not a new object will be created. * * @returns Populated node levels. */ function fillLevelsByDirectionCyclic(nodes: Node[], levels: Levels): Levels { const edges = new Set<Edge>(); nodes.forEach((node): void => { node.edges.forEach((edge): void => { if (edge.connected) { edges.add(edge); } }); }); edges.forEach((edge): void => { const fromId = edge.from.id; const toId = edge.to.id; if (levels[fromId] == null) { levels[fromId] = 0; } if (levels[toId] == null || levels[fromId] >= levels[toId]) { levels[toId] = levels[fromId] + 1; } }); return levels; } /** * Assign levels to nodes according to their positions in the hierarchy. * * @param nodes - Nodes of the graph. * @param levels - If present levels will be added to it, if not a new object will be created. * * @returns Populated node levels. */ export function fillLevelsByDirection( nodes: Node[], levels: Levels = Object.create(null) ): Levels { const limit = nodes.length; for (const leaf of nodes) { if (!leaf.edges.every((edge): boolean => edge.to === leaf)) { // Not a leaf. continue; } levels[leaf.id] = 0; const stack: Node[] = [leaf]; let done = 0; let node: Node | undefined; while ((node = stack.pop())) { const edges = node.edges; const newLevel = levels[node.id] - 1; for (const edge of edges) { if (!edge.connected || edge.to !== node || edge.to === edge.from) { continue; } const fromId = edge.fromId; const oldLevel = levels[fromId]; if (oldLevel == null || oldLevel > newLevel) { levels[fromId] = newLevel; stack.push(edge.from); } } if (done > limit) { // This would run forever on a cyclic graph. return fillLevelsByDirectionCyclic(nodes, levels); } else { ++done; } } } return levels; }