/** * A {@link SugiyamaOperator} for computing a layered layout of a dag * * @packageDocumentation */ import { Dag, DagNode } from "../dag"; import { Up } from "../utils"; import { CoordNodeSizeAccessor, CoordOperator } from "./coord"; import { DefaultSimplexOperator as DefaultCoord } from "./coord/simplex"; import { DecrossOperator } from "./decross"; import { DefaultTwoLayerOperator as DefaultTwoLayer } from "./decross/two-layer"; import { LayeringOperator } from "./layering"; import { DefaultSimplexOperator as DefaultLayering } from "./layering/simplex"; import { SugiNode } from "./utils"; /** * The return from calling {@link SugiyamaOperator} * * This is the final width and height of the laid out dag. */ export interface SugiyamaInfo { /** total width after layout */ width: number; /** total height after layout */ height: number; } /** * An accessor for computing the size of a node in the layout * * If `node` is omitted, the returned size is the size of a "dummy node", a * piece of a long edge that can curve around other nodes. This accessor must * return a tuple of non-negative numbers corresponding to the node's *width* * and *height*. Since this is a layered layout, a node's height is effectively * the maximum height of all nodes in the same layer. * * If you need more control over the size of dummy nodes, see * {@link SugiNodeSizeAccessor}. * * This accessor will only be called once for each node. */ export interface NodeSizeAccessor { (node?: DagNode): readonly [number, number]; } /** * An accessor for computing the size of a node in the layout * * This interface exposes a full {@link SugiNode}, which has more information * about dummy nodes available in case different dummy nodes should have * different sizes. * * For most cases {@link NodeSizeAccessor} should be enough. */ export interface SugiNodeSizeAccessor { (node: SugiNode): readonly [number, number]; } /** the node datum of a node size accessor */ export declare type NsNodeDatum = NS extends NodeSizeAccessor ? N : never; /** the link datum of a node size accessor */ export declare type NsLinkDatum = NS extends NodeSizeAccessor ? L : never; /** * The effective {@link SugiNodeSizeAccessor} when a normal * {@link NodeSizeAccessor} is supplied. */ export interface WrappedNodeSizeAccessor extends SugiNodeSizeAccessor, NsLinkDatum> { /** the underling node size */ wrapped: NodeSize; } /** * wrap a {@link NodeSizeAccessor} turning it into an {@link SugiNodeSizeAccessor} * * Mostly useful for running the steps of {@link sugiyama} independently. */ export declare function wrapNodeSizeAccessor>(acc: NS & NodeSizeAccessor): WrappedNodeSizeAccessor & SugiNodeSizeAccessor; /** sugiyama operators */ export interface Operators { /** layering operator */ layering: LayeringOperator; /** decross operator */ decross: DecrossOperator; /** coord operator */ coord: CoordOperator; /** sugi node size operator */ sugiNodeSize: SugiNodeSizeAccessor; /** node size operator */ nodeSize: NodeSizeAccessor | null; } /** the typed dag of a set of operators */ export declare type OpsDag = Ops extends Operators ? Dag : Dag; /** * The operator used to layout a {@link Dag} using the sugiyama layered method. * * The algorithm is roughly comprised of three steps: * 1. {@link LayeringOperator | layering} - in this step, every node is * assigned a non-negative integer later such that children are guaranteed * to have higher layers than their parents. (modified with {@link layering}) * 2. {@link DecrossOperator | decrossing} - in the step, nodes in each layer * are reordered to minimize the number of crossings. (modified with {@link * decross}) * 3. {@link CoordOperator | coordinate assignment} - in the step, the * nodes are assigned x and y coordinates that respect their layer, layer * ordering, and size. (modified with {@link coord} and {@link nodeSize}) * * The algorithm is based off ideas presented in K. Sugiyama et al. [1979], but * described by {@link http://www.it.usyd.edu.au/~shhong/fab.pdf | S. Hong}. * The sugiyama layout can be configured with different algorithms for each * stage of the layout. For each stage there should be adecuate choices for * methods that balance speed and quality for your desired layout. In the * absence of those, any function that meets the interface for that stage is * valid. * * Create with {@link sugiyama}. * * @remarks * * If one wants even more control over the algorithm, each step is broken down * in the source code and can be achieved by calling an exported utility * function. If one wants to call certain pieces incrementally, or adjust how * things are called, it's recommended to look at the source and call each * component function successively. * * @example * * Sugiyama example * * @example * * ```typescript * const data = [["parent", "child"], ...]; * const create = connect(); * const dag = create(data); * const layout = sugiyama(); * const { width, height } = layout(dag); * for (const node of dag) { * console.log(node.x, node.y); * } * ``` * * @example * * This example highlights tweaking several aspects of dag rendering * ```typescript * const data = [["parent", "child"], ...]; * const create = connect(); * const dag = create(data); * const layout = sugiyama() * .nodeSize(n => n === undefined ? [0, 0] : [n.data.id.length, 2]) * .coord(greedy()); * const { width, height } = layout(dag); * for (const node of dag) { * console.log(node.x, node.y); * } * ``` */ export interface SugiyamaOperator { /** * Layout the {@link Dag} using the currently configured operator. The * returned dag nodes will have `x`, `y`, and `value` (layer), assigned. In * addition, each link will have `points` assigned to the current layout. */ (dag: OpsDag): SugiyamaInfo; /** * Set the {@link LayeringOperator}. (default: {@link SimplexOperator}) */ layering(layer: NewLayering): SugiyamaOperator>; /** * Get the current {@link LayeringOperator}. */ layering(): Ops["layering"]; /** * Set the {@link DecrossOperator}. (default: {@link TwoLayerOperator}) */ decross(dec: NewDecross): SugiyamaOperator>; /** * Get the current {@link DecrossOperator}. */ decross(): Ops["decross"]; /** * Set the {@link CoordOperator}. (default: {@link QuadOperator}) */ coord(crd: NewCoord): SugiyamaOperator>; /** * Get the current {@link CoordOperator}. */ coord(): Ops["coord"]; /** * Sets the sugiyama layout's size to the specified two-element array of * numbers [ *width*, *height* ]. When `size` is non-null the dag will be * shrunk or expanded to fit in the size, keeping all distances proportional. * If it's null, the {@link nodeSize} parameters will be respected as * coordinate sizes. (default: null) */ size(sz: readonly [number, number] | null): SugiyamaOperator; /** * Get the current layout size. */ size(): null | readonly [number, number]; /** * Sets the {@link NodeSizeAccessor}, which assigns how much space is * necessary between nodes. (defaults to [1, 1] for normal nodes and [0, 0] * for dummy nodes [undefined values]). * * @remarks * * When overriding, make sure you handle the case where the node is * undefined. Failure to do so may result in unexpected layouts. */ nodeSize(acc: NewNodeSize): SugiyamaOperator; }>>; /** * Get the current node size * * If a {@link SugiNodeSizeAccessor} was specified, this will be null. */ nodeSize(): Ops["nodeSize"]; /** * Sets this sugiyama layout's {@link SugiNodeSizeAccessor}. * * This is effectively a more powerful api above the standard * {@link NodeSizeAccessor}, and is only necessary if different dummy nodes * need different sizes. */ sugiNodeSize(sz: NewSugiNodeSize): SugiyamaOperator>; /** * Get the current sugi node size, or a {@link WrappedNodeSizeAccessor | * wrapped version} if {@link nodeSize} was specified. */ sugiNodeSize(): Ops["sugiNodeSize"]; } /** * Verify, cache, and split the results of an {@link SugiNodeSizeAccessor} into * an x and y {@link CoordNodeSizeAccessor}. * * This allows you to split an {@link SugiNodeSizeAccessor} into independent x * and y accessors, while also caching the result to prevent potentially * expensive computation from being duplicated. * * The only real reason to use this would be to run the steps of {@link * sugiyama} independently. */ export declare function cachedNodeSize(nodeSize: SugiNodeSizeAccessor, check?: boolean): readonly [CoordNodeSizeAccessor, CoordNodeSizeAccessor]; /** * Given layers and node heights, assign y coordinates. * * This is only exported so that each step of {@link sugiyama} can be executed * independently or controlled. In the future it may make sense to make * vertical coordinates part of the sugiyama operators. */ export declare function coordVertical(layers: readonly (readonly SugiNode[])[], size: CoordNodeSizeAccessor): number; /** default node size */ export declare type DefaultNodeSizeAccessor = NodeSizeAccessor; /** default sugiyama operator */ export declare type DefaultSugiyamaOperator = SugiyamaOperator<{ /** default layering */ layering: DefaultLayering; /** default decross */ decross: DefaultTwoLayer; /** default coord */ coord: DefaultCoord; /** wrapped default node size */ sugiNodeSize: WrappedNodeSizeAccessor; /** default node size */ nodeSize: DefaultNodeSizeAccessor; }>; /** * Construct a new {@link SugiyamaOperator} with the default settings. * * @example * ```typescript * const dag = hierarchy()(...); * const layout = sugiyama().nodeSize(d => d === undefined ? [0, 0] : [d.width, d.height]); * layout(dag); * for (const node of dag) { * console.log(node.x, node.y); * } * ``` */ export declare function sugiyama(...args: never[]): DefaultSugiyamaOperator;