UNPKG

d3-dag

Version:

Layout algorithms for visualizing directed acylic graphs.

364 lines (363 loc) 15.2 kB
/** * a way to enforce specific rankings of nodes * * A rank accessor assigns a numeric rank to some nodes. In general, nodes with * a lower rank come *before* nodes with a higher rank. Layering operators that * take a rank accessor additionally guarantee that nodes with the same rank * will share the same layer (if possible). * * @example * * This example demonstrates passing through a rank value that might exist on a * node. * * ```ts * function memberRank({ data }: GraphNode<{ rank?: number }>): number | undefined { * return data.rank; * } * ``` */ export interface Rank<in NodeDatum = never, in LinkDatum = never> { /** * compute the rank of a node * * @param node - the node to compute the rank of * @returns rank - the rank of the node; if defined lower ranks indicate * nodes that come earlier (lower y's) */ (node: GraphNode<NodeDatum, LinkDatum>): number | undefined; } /** * a link between nodes, with attached information * * The immutable version of {@link MutGraphLink}. */ export interface GraphLink<out NodeDatum = unknown, out LinkDatum = unknown> { /** The dag node this link comes from */ readonly source: GraphNode<NodeDatum, LinkDatum>; /** The dag node this link goes to */ readonly target: GraphNode<NodeDatum, LinkDatum>; /** layout control points assigned to the link */ points: [number, number][]; /** user data attached to this link */ data: LinkDatum; } /** * a mutable link between nodes * * The mutable version of {@link GraphLink}. */ export interface MutGraphLink<in out NodeDatum, in out LinkDatum> extends GraphLink<NodeDatum, LinkDatum> { readonly source: MutGraphNode<NodeDatum, LinkDatum>; readonly target: MutGraphNode<NodeDatum, LinkDatum>; /** remove this link from the graph */ delete(): void; } /** * A graph representation * * Graphs are collections of {@link GraphNode}s, held together by * {@link GraphLink}s, although there's no requirement that they're connected. * Graphs support iterating over their {@link nodes} and {@link links} as well * as checking if certain properties hold. Each node is also defined as a graph * of just its connected component, so all methods of a graph also apply to any * individual node, treated as all nodes reachable from the current node. * * The structure of a Graph is immutable, allowing for appropriate variance * with readonly methods. For a graph whos structure can be modified, see * {@link MutGraph}. * * Methods names preceeded by an `n` will return a number, often the length of * the iterable produces by the method sans-prefix. */ export interface Graph<out NodeDatum = unknown, out LinkDatum = unknown> { /** * an iterator over every {@link GraphNode | node} in the graph * * @remarks * Be careful not to modify the graph structure while iterating as any * modification, including adding or removing links, can change the behavior * of iteration giving unexpected results. If you want to guarantee * consistent iteration, allocating an array first with `[...graph.nodes()]` will * ensure consistent iteration. */ nodes(): IterableIterator<GraphNode<NodeDatum, LinkDatum>>; /** * compute a topological order of the graph * * If the graph can't be represented in topological order, this will try to * minimize the number of edge inversions. Optimally minimizing inversions is * np-complete, so this will only be approximate. * * You can optionally specify a {@link Rank} accessor that defines a * numerical rank for every node. Nodes with a lower rank will come before * nodes of a higher rank even if that requires more edge inversions. Nodes * without a rank are unconstrained. */ topological(rank?: Rank<NodeDatum, LinkDatum>): GraphNode<NodeDatum, LinkDatum>[]; /** * an iterator over every {@link GraphLink | link} in the graph */ links(): IterableIterator<GraphLink<NodeDatum, LinkDatum>>; /** the number of nodes in the graph */ nnodes(): number; /** the number of links in the graph */ nlinks(): number; /** * an iterator over the roots of the graph * * The roots are defined as a set of nodes such that every node in the graph * is a descendant of one of the roots, an no root is a descendant of any * other root. It is guaranteed to include every node with no parents, but * one node in a cycle may be chosen if there is no unique root for that * cycle. * * @remarks the current implementation will return a minimal root set as long * as source cycles contain a node with a single parent. */ roots(): IterableIterator<GraphNode<NodeDatum, LinkDatum>>; /** * an iterator over the leaves of the graph * * The leaves are defined as a set of nodes such that every node in the graph * is an ancestor of one of the leaves, an no leaf is an ancestor of any * other leaf. It is guaranteed to include every node with no children, but * one node in a cycle may be chosen if there is no unique leaf for that * cycle. * * @remarks the current implementation will return a minimal leaf set as long * as target cycles contain a node with a single child. */ leaves(): IterableIterator<GraphNode<NodeDatum, LinkDatum>>; /** * split a graph into connected components * * @remarks * Since each node behaves like a graph of its connected component, * this is equivalent with returning a graph of each connected component. * * @returns splits an iterable over a single node in each connected * component */ split(): IterableIterator<GraphNode<NodeDatum, LinkDatum>>; /** * true if every node in the graph is reachable from every other */ connected(): boolean; /** * true if at least one node in this graph has multiple links to the same child */ multi(): boolean; /** * true if there no cycles in the graph */ acyclic(): boolean; /** * serialize the graph * * @remarks this is intended to be called automatically by `JSON.stringify`. */ toJSON(): unknown; } /** * a mutable graph representation * * In addition to all of the methods inherent to {@link Graph}s, this also * allows structure graph modification with the {@link node} and {@link link} * methods to either create an empty node or create a link between two nodes. * Nodes and links can be removed with the delete method on * {@link MutGraphNode#delete | nodes} and {@link MutGraphLink#delete | links}. * * ## Performance * * In order to keep track of caches, internally adding links runs union-find * which has near, but not quite, constant complexity. However, various methods * can run in linear time making some access patterns run in worst case * quadratic if they're called in between modifications. This was ultimately * deemed acceptable as all modification will likely come before a layout, and * that will always take linear time. */ export interface MutGraph<in out NodeDatum, in out LinkDatum> extends Graph<NodeDatum, LinkDatum> { nodes(): IterableIterator<MutGraphNode<NodeDatum, LinkDatum>>; links(): IterableIterator<MutGraphLink<NodeDatum, LinkDatum>>; split(): IterableIterator<MutGraphNode<NodeDatum, LinkDatum>>; roots(): IterableIterator<MutGraphNode<NodeDatum, LinkDatum>>; leaves(): IterableIterator<MutGraphNode<NodeDatum, LinkDatum>>; /** * add a new node with datum * * If `undefined` extends NodeDatum, datum can be elided and simply called as * ```ts * grf.node(); * ``` */ node(...datum: undefined extends NodeDatum ? [datum?: NodeDatum] : [datum: NodeDatum]): MutGraphNode<NodeDatum, LinkDatum>; /** * add a new link from source to target * * If `undefined` extends LinkDatum, datum can be elided and simply called as * ```ts * grf.link(source, target); * ``` */ link(source: MutGraphNode<NodeDatum, LinkDatum>, target: MutGraphNode<NodeDatum, LinkDatum>, ...datum: undefined extends LinkDatum ? [datum?: LinkDatum] : [datum: LinkDatum]): MutGraphLink<NodeDatum, LinkDatum>; } export interface GraphNodeMixin<out NodeDatum, out LinkDatum> { /** user data for this node */ data: NodeDatum; /** raw (possibly undefined) x coordinate for a node */ ux?: number | undefined; /** raw (possibly undefined) y coordinate for a node */ uy?: number | undefined; /** * view of {@link ux} that throws if ux is undefined */ x: number; /** * view of {@link uy} that throws if uy is undefined */ y: number; /** the number of unique parent nodes */ nparents(): number; /** the number of unique child nodes */ nchildren(): number; /** the number of parent links */ nparentLinks(): number; /** the number of child links */ nchildLinks(): number; /** the number of links from a specific node */ nparentLinksTo(node: GraphNode<NodeDatum, LinkDatum>): number; /** iterator over every link from a specific node */ parentLinksTo(node: GraphNode<NodeDatum, LinkDatum>): IterableIterator<GraphLink<NodeDatum, LinkDatum>>; /** the number of links to a specific node */ nchildLinksTo(node: GraphNode<NodeDatum, LinkDatum>): number; /** iterator over every link to a specific node */ childLinksTo(node: GraphNode<NodeDatum, LinkDatum>): IterableIterator<GraphLink<NodeDatum, LinkDatum>>; /** iterator over this node's unique parent nodes */ parents(): IterableIterator<GraphNode<NodeDatum, LinkDatum>>; /** iterator over this node's unique child nodes */ children(): IterableIterator<GraphNode<NodeDatum, LinkDatum>>; /** iterator over this node's unique parent nodes and the number of links from them */ parentCounts(): IterableIterator<[GraphNode<NodeDatum, LinkDatum>, number]>; /** iterator over this node's unique child nodes and the number of links to them */ childCounts(): IterableIterator<[GraphNode<NodeDatum, LinkDatum>, number]>; /** * iterator of links to this node from its parents * * The order of links is guaranteed to not change between iterations. */ parentLinks(): IterableIterator<GraphLink<NodeDatum, LinkDatum>>; /** * iterator of links from this node to its children * * The order of links is guaranteed to not change between iterations. */ childLinks(): IterableIterator<GraphLink<NodeDatum, LinkDatum>>; /** * iterator of all nodes reachable though parents * * The iterator includes this node. */ ancestors(): IterableIterator<GraphNode<NodeDatum, LinkDatum>>; /** * iterator of all nodes reachable though children * * The iterator includes this node. */ descendants(): IterableIterator<GraphNode<NodeDatum, LinkDatum>>; } /** * a node in a graph * * Nodes provide all the same interfaces that {@link Graph}s do in terms of * node properties and iteration. When called on a node, the nodes behaves as a * graph of just its connected component. Therefore new nodes will always be * {@link acyclic}, {@link connected}, and not {@link multi}. * * In addition, nodes also expose properties of their immediate neighborhoods, * iterators and counts of nodes they directly touch. * * This is the immutable version of {@link MutGraphNode}. */ export interface GraphNode<out NodeDatum = unknown, out LinkDatum = unknown> extends Graph<NodeDatum, LinkDatum>, GraphNodeMixin<NodeDatum, LinkDatum> { } /** * a mutable node in a graph * * This possesses all of the methods of both {@link MutGraph} and {@link * GraphNode}, while also adding the ability to directly add {@link parent}s or * {@link child}ren and to {@link delete | remove} the node and all links from * the graph. */ export interface MutGraphNode<in out NodeDatum, in out LinkDatum> extends MutGraph<NodeDatum, LinkDatum>, GraphNodeMixin<NodeDatum, LinkDatum> { /** {@inheritDoc GraphNode#parentLinksTo} */ parentLinksTo(node: GraphNode<NodeDatum, LinkDatum>): IterableIterator<MutGraphLink<NodeDatum, LinkDatum>>; /** {@inheritDoc GraphNode#childLinksTo} */ childLinksTo(node: GraphNode<NodeDatum, LinkDatum>): IterableIterator<MutGraphLink<NodeDatum, LinkDatum>>; /** {@inheritDoc GraphNode#parents} */ parents(): IterableIterator<MutGraphNode<NodeDatum, LinkDatum>>; /** {@inheritDoc GraphNode#children} */ children(): IterableIterator<MutGraphNode<NodeDatum, LinkDatum>>; /** {@inheritDoc GraphNode#parentCounts} */ parentCounts(): IterableIterator<[ MutGraphNode<NodeDatum, LinkDatum>, number ]>; /** {@inheritDoc GraphNode#childCounts} */ childCounts(): IterableIterator<[MutGraphNode<NodeDatum, LinkDatum>, number]>; /** {@inheritDoc GraphNode#parentLinks} */ parentLinks(): IterableIterator<MutGraphLink<NodeDatum, LinkDatum>>; /** {@inheritDoc GraphNode#childLinks} */ childLinks(): IterableIterator<MutGraphLink<NodeDatum, LinkDatum>>; /** {@inheritDoc GraphNode#ancestors} */ ancestors(): IterableIterator<MutGraphNode<NodeDatum, LinkDatum>>; /** {@inheritDoc GraphNode#descendants} */ descendants(): IterableIterator<MutGraphNode<NodeDatum, LinkDatum>>; /** add a new link from a parent node */ parent(par: MutGraphNode<NodeDatum, LinkDatum>, ...datum: undefined extends LinkDatum ? [datum?: LinkDatum] : [datum: LinkDatum]): MutGraphLink<NodeDatum, LinkDatum>; /** add a new link to a child node */ child(chi: MutGraphNode<NodeDatum, LinkDatum>, ...datum: undefined extends LinkDatum ? [datum?: LinkDatum] : [datum: LinkDatum]): MutGraphLink<NodeDatum, LinkDatum>; /** * remove this node and all of its links from the graph * * Once a node is deleted, none of its methods are valid or guaranteed to do * the correct thing. */ delete(): void; } /** * create a new mutable empty {@link MutGraph} * * When creating a new mutable graph via typescript, you must specify the type * of the node data and link data you intend on using, since these types are * invariant for mutable graphs. * * @example * Creating a graph with node and link data: * * ```ts * // create a new graph * const grf = graph<string, number>(); * // add two nodes with some data * const a = grf.node("a"); * const b = grf.node("b"); * // add a new link with the data `1` * const link = grf.link(a, b, 1); * grf.connected(); // true * ``` * * @example * If `undefined` extends the data types then they can be omitted: * ``` * // create a new graph * const grf = graph<undefined | string, undefined | number>(); * const a = grf.node(); * const b = grf.node(); * const link = grf.link(a, b); * grf.connected(); // true * ``` * * @typeParam NodeDatum - the extra user data attached to each node * @typeParam LinkDatum - the extra data attached to each link */ export declare function graph<NodeDatum, LinkDatum>(): MutGraph<NodeDatum, LinkDatum>;