Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 24x 24x 24x 24x 24x 160x 160x 46x 24x 160x 160x 160x 160x 204x 204x 204x 114x 24x 160x 24x 160x 160x 46x 114x 114x 204x 204x 204x 114x 24x 160x 24x 24x 160x 160x 160x 142x 24x 24x 118x 118x 118x 160x 118x 160x 24x | import type { LayoutState, LayoutNode, Layer } from "./types";
/**
* Assign layers to nodes using longest-path algorithm
* Sources (no predecessors) get layer 0
* Each node gets max(predecessor layers) + 1
*/
export function assignLayers(state: LayoutState): void {
const { nodes } = state;
// Topological sort via Kahn's algorithm
const inDegree = new Map<number, number>();
const queue: number[] = [];
const order: number[] = [];
// Initialize in-degrees
for (const [id, node] of nodes) {
inDegree.set(id, node.predecessors.length);
if (node.predecessors.length === 0) {
queue.push(id);
}
}
// Process nodes in topological order
while (queue.length > 0) {
const nodeId = queue.shift()!;
order.push(nodeId);
const node = nodes.get(nodeId)!;
for (const succId of node.successors) {
const deg = (inDegree.get(succId) ?? 1) - 1;
inDegree.set(succId, deg);
if (deg === 0) {
queue.push(succId);
}
}
}
// Handle cycles: add remaining nodes (shouldn't happen in valid DAG)
for (const [id] of nodes) {
Iif (!order.includes(id)) {
order.push(id);
}
}
// Assign layers via longest path from sources
for (const nodeId of order) {
const node = nodes.get(nodeId)!;
if (node.predecessors.length === 0) {
// Source node: layer 0
node.layer = 0;
} else {
// Max layer of predecessors + 1
let maxPredLayer = 0;
for (const predId of node.predecessors) {
const pred = nodes.get(predId);
Eif (pred && pred.layer >= 0) {
maxPredLayer = Math.max(maxPredLayer, pred.layer + 1);
}
}
node.layer = maxPredLayer;
}
}
// Ensure all nodes have valid layer (handle edge cases)
for (const node of nodes.values()) {
Iif (node.layer < 0) {
node.layer = 0;
}
}
// Build layer structures
const layerMap = new Map<number, LayoutNode[]>();
for (const node of nodes.values()) {
const arr = layerMap.get(node.layer) ?? [];
arr.push(node);
layerMap.set(node.layer, arr);
}
// Sort layers by index and create Layer objects
const sortedLayerIndices = [...layerMap.keys()].sort((a, b) => a - b);
const layers: Layer[] = [];
for (const index of sortedLayerIndices) {
const layerNodes = layerMap.get(index)!;
// Sort nodes in layer by ID for initial deterministic order
layerNodes.sort((a, b) => a.id - b.id);
layerNodes.forEach((n, i) => {
n.orderInLayer = i;
});
layers.push({
index,
nodes: layerNodes,
x: 0,
maxWidth: Math.max(...layerNodes.map((n) => n.width), 100),
});
}
state.layers = layers;
}
|