UNPKG

d3-dag

Version:

Layout algorithms for visualizing directed acylic graphs.

165 lines (164 loc) 5.53 kB
/** * 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;