/** * Where a flow diagram's nodes and ribbons go. * * Kept apart from the component for the same reason the splitter's maths is: * it is the part with answers that can be checked, and a test that has to * mount a chart to check them is a test nobody writes twice. * * ## The problem the layout is solving * * A node's column is decided by the flow, not by the caller — a stage that * receives from another has to be drawn after it or the ribbon runs backwards. * Its height is its value. Neither of those is a choice. What is left over is * the *order within a column*, and that is the whole difficulty: the same graph * drawn in two orders is the same numbers with a different number of crossings, * and crossings are the only thing that makes one of these unreadable. * * There is no cheap exact answer — minimising crossings is the kind of problem * that is only solved by trying — so the order is settled by relaxation. Every * node is pulled towards the average height of what it connects to, the pull * weakens each round, and nodes that end up overlapping are pushed apart. Six * rounds is where the picture stops visibly improving. * * ## Why it never throws * * A flow that loops back on itself has no left-to-right reading at all, and the * textbook answer is to refuse the graph. A chart cannot refuse: the data is * usually arriving from somewhere nobody in the room controls, and a screen * that crashes on a bad row is worse than one that draws the rows it can. So a * link that closes a loop is dropped, counted, and reported on the layout — * the caller can say so, and the rest of the diagram is still true. */ /** Which column a node is pushed into when the flow leaves a choice. */ export type SankeyAlign = 'justify' | 'left' | 'right' | 'center'; export interface SankeyLayoutNodeInput { /** Stable key the links name. */ id: string; /** * Pins the node's total, for a stage whose links do not account for all of * it — a step that also loses some of what it received to nowhere. */ value?: number; } export interface SankeyLayoutLinkInput { source: string; target: string; value: number; } export interface SankeyLayoutOptions { width: number; height: number; /** How thick a node's bar is drawn, in points. */ nodeWidth: number; /** The gap asked for between two nodes in a column, in points. */ nodePadding: number; align: SankeyAlign; /** Relaxation rounds. More is steadier and slower; six is the useful knee. */ iterations: number; } export interface SankeyLayoutNode { id: string; /** Position in the input array, so a caller can find its own datum again. */ index: number; /** Which column it landed in, counting from the left. */ layer: number; /** What flows through it: the larger of what arrives and what leaves. */ value: number; x0: number; x1: number; y0: number; y1: number; } export interface SankeyLayoutLink { index: number; /** * Position in the caller's link array. Not the same as `index`: links that * name nothing or close a loop are dropped, so the drawn order closes up * behind them while this still points at the row it came from. */ input: number; /** Index into the laid-out nodes, not into the caller's array. */ source: number; target: number; value: number; /** How thick the ribbon is, in points. */ width: number; /** The ribbon's centre where it leaves the source, in points from the top. */ y0: number; /** And where it meets the target. */ y1: number; } export interface SankeyLayout { nodes: SankeyLayoutNode[]; links: SankeyLayoutLink[]; /** How many columns the flow turned out to need. */ columns: number; /** Links dropped for closing a loop, naming nothing, or carrying nothing. */ dropped: number; } /** * Lays out a flow diagram. * * Returns positions in points inside a `width` by `height` box. An input that * cannot be drawn — no nodes, no room, nothing carrying a value — comes back * empty rather than as a diagram of zeroes. */ export declare function sankeyLayout(nodeInput: readonly SankeyLayoutNodeInput[], linkInput: readonly SankeyLayoutLinkInput[], options: SankeyLayoutOptions): SankeyLayout; //# sourceMappingURL=sankey-layout.d.ts.map