d3-dag
Version:
Layout algorithms for visualizing directed acylic graphs.
165 lines (164 loc) • 5.53 kB
TypeScript
/**
* A topological layout using {@link Grid}.
*
* @packageDocumentation
*/
import { Graph, Rank } from "../graph";
import { LayoutResult, NodeSize } from "../layout";
import { Tweak } from "../tweaks";
import { U } from "../utils";
import { Lane } from "./lane";
import { LaneGreedy } from "./lane/greedy";
/** all operators for the grid layout */
export interface GridOps<in N = never, in L = never> {
/** the operator for assigning nodes to a lane */
lane: Lane<N, L>;
/** the operator for assigning nodes a rank */
rank: Rank<N, L>;
/** node size operator */
nodeSize: NodeSize<N, L>;
/** tweaks */
tweaks: readonly Tweak<N, L>[];
}
/**
* A simple grid based topological layout operator.
*
* This layout algorithm constructs a topological representation of the dag
* meant for visualization. The nodes are topologically ordered and then nodes
* are put into lanes such that an edge can travel horizontally to the lane of
* a child node, and then down without intersecting to that child.
*
* Create with {@link grid}.
*/
export interface Grid<Ops extends GridOps = GridOps> {
/**
* layout a graph with the grid layout
*
* @param grf - the graph to layout
* @returns dimensions - the width and height of the final layout
*/
(grf: Ops extends GridOps<infer N, infer L> ? Graph<N, L> : never): LayoutResult;
/**
* set a custom {@link Lane} operator
*
* The lane operator controls how nodes are assigned to horizontal lanes.
* This is the core piece of the layout. There are two builtin lane operators:
* - {@link laneGreedy} - This is a fast reasonably effective lane operator.
* It supports a number of further tweaks to alter the layout.
* - {@link laneOpt} - This assigns lanes to optimally minimize the number of
* edge crossings. This optimization is NP Hard, so outside of very small
* graphs, it will likely take too long to execute.
*
* You can also supply any function that satisfies the {@link Lane}
* interface. See that documentation for more information about implementing
* your own lane assignment.
*
* (default: {@link laneGreedy})
*
* @example
*
* ```ts
* const layout = grid().lane(laneOpt());
* ```
*
*/
lane<NewLane extends Lane>(val: NewLane): Grid<U<Ops, "lane", NewLane>>;
/** get the current lane operator */
lane(): Ops["lane"];
/**
* set the rank operator for the topological ordering
*
* Set the rank operator to the given {@link Rank} and returns a new
* version of this operator.
*
* (default: noop)
*/
rank<NewRank extends Rank>(val: NewRank): Grid<U<Ops, "rank", NewRank>>;
/** get the current rank operator */
rank(): Ops["rank"];
/**
* set the {@link Tweak}s to apply after layout
*/
tweaks<const NewTweaks extends readonly Tweak[]>(val: NewTweaks): Grid<U<Ops, "tweaks", NewTweaks>>;
/**
* get the current {@link Tweak}s.
*/
tweaks(): Ops["tweaks"];
/**
* Sets this grid layout's node size to the specified two-element array of
* numbers [ *width*, *height* ] and returns a new operator. These sizes are
* effectively the grid size, e.g. the spacing between adjacent lanes or rows
* in the grid.
*
* (default: `[1, 1]`)
*/
nodeSize<NewNodeSize extends NodeSize>(val: NewNodeSize): Grid<U<Ops, "nodeSize", NewNodeSize>>;
/** Get the current node size */
nodeSize(): Ops["nodeSize"];
/**
* Set the gap size between nodes
*
* (default: `[1, 1]`)
*/
gap(val: readonly [number, number]): Grid<Ops>;
/** Get the current gap size */
gap(): readonly [number, number];
}
/** the default grid operator */
export type DefaultGrid = Grid<{
/** default lane: greedy */
lane: LaneGreedy;
/** default rank: none */
rank: Rank<unknown, unknown>;
/** default size */
nodeSize: readonly [1, 1];
/** default tweaks: none */
tweaks: readonly [];
}>;
/**
* create a new {@link Grid} with default settings.
*
* The grid layout algorithm constructs a horizontally compact topological
* representation of the dag. The nodes are topologically ordered and then
* put into lanes such that an edge can travel horizontally to the lane of a
* child node, and then down without intersecting to that child.
*
* This layout produces good representations when you want a compressed layout
* where each node is on an independent horizontal line.
*
* <img alt="grid example" src="media://grid-greedy-topdown.png" width="200">
*
* @remarks
*
* The current implementation doesn't render multi-dags any differently, so
* multiple edges going to the same node will be rendered as a single edge.
*
* @example
*
* using the default layout
*
* ```ts
* const grf = ...
* const layout = grid();
* const { width, height } = layout(dag);
* for (const node of dag) {
* console.log(node.x, node.y);
* }
* ```
*
* @example
*
* In addition to the standard modifications of {@link Grid#rank},
* {@link Grid#nodeSize}, {@link Grid#gap}, and {@link Grid#tweaks},
* {@link Grid} also supports altering the lane assignment {@link Grid#lane}:
*
* ```ts
* const grf = ...
* const layout = grid().lane(laneOpt());
* const { width, height } = layout(dag);
* for (const node of dag) {
* console.log(node.x, node.y);
* }
* ```
*/
export declare function grid(...args: never[]): DefaultGrid;