d3-dag
Version:
Layout algorithms for visualizing directed acylic graphs.
73 lines (72 loc) • 2.54 kB
TypeScript
/**
* A {@link Decross} rearranges nodes within a layer to minimize
* decrossings.
*
* @packageDocumentation
*/
import { SugiNode } from "../sugify";
/**
* a decrossing operator rearranges the nodes in a layer to minimize decrossings.
*
* A decrossing operator takes a array of layered nodes and reorders them.
* There is no specific requirement on what the final order should be, but for
* layouts to look good, most decrossing operators should seek to minimize the
* number of link crossings, among other constraints.
*
* Minimizing the number of link crossings is an NP-Complete problem, so fully
* minimizing decrossings {@link decrossOpt | optimally} can be prohibitively
* expensive, causing javascript to crash or run forever on large dags. In
* these instances it may be necessary to use an
* {@link decrossTwoLayer | approximate decrossing minimization}. You may also
* have special constraints around the order of nodes that can be expressed
* with this operator.
*
* @example
*
* A common case might be that you want to use an optimal decrossing method if
* the layout is small, but use a heuristic if the graph is too large. This can
* be easily accomplished with a custom decrossing operator.
*
* ```ts
* const opt = decrossOpt();
* const heuristic = decrossTwoLayer();
*
* function decrossFallback(layers: SugiNode[][]): void {
* try {
* opt(layers);
* } catch {
* heuristic(layers);
* }
* }
* ```
*
* @example
*
* We illustrate a custom decrossing operator by assuming each node is ordered
* by an `ord` value. For dummy nodes (points on longer edges between nodes) we
* use the average value between the nodes on each end.
*
* ```ts
* function customDecross(layers: SugiNode<{ ord: number }, unknown>[][]): void {
* const vals = new Map<SugiNode, number>();
* for (const layer of layers) {
* for (const node of layer) {
* const { data } = node;
* const val = data.role === "node" ? data.node.data.ord ? (data.link.source.data.ord + data.link.target.data.ord) / 2;
* vals.set(node, val);
* }
* }
* for (const layer of layers) {
* layer.sort((a, b) => vals.get(a)! - vals.get(b)!);
* }
* }
* ```
*/
export interface Decross<in NodeDatum = never, in LinkDatum = never> {
/**
* remove crossings from a layered graph
*
* @param layers - the layers of nodes that this should rearrange.
*/
(layers: SugiNode<NodeDatum, LinkDatum>[][]): void;
}