Node-link tree diagrams that position nodes and show topology through explicit connections. Includes the "tidy" tree layout using the Reingold-Tilford algorithm and cluster/dendrogram layouts.
Creates a "tidy" tree layout using the Reingold-Tilford algorithm for compact, non-overlapping node positioning.
/**
* Creates a tree layout function
* @returns Tree layout function with configuration methods
*/
function tree();
interface TreeLayout {
/** Apply tree layout to hierarchy root */
(root: Node): Node;
/** Get/set the layout size [width, height] */
size(size?: [number, number]): TreeLayout | [number, number] | null;
/** Get/set the node size [width, height] for fixed node dimensions */
nodeSize(size?: [number, number]): TreeLayout | [number, number] | null;
/** Get/set the separation function between adjacent nodes */
separation(separation?: (a: Node, b: Node) => number): TreeLayout | Function;
}Usage Examples:
import { hierarchy, tree } from "d3-hierarchy";
const data = {
name: "root",
children: [
{ name: "branch1", children: [{ name: "leaf1" }, { name: "leaf2" }] },
{ name: "branch2", children: [{ name: "leaf3" }] }
]
};
// Basic tree layout
const root = hierarchy(data);
const treeLayout = tree().size([400, 200]);
const layoutRoot = treeLayout(root);
// Access computed coordinates
layoutRoot.each(node => {
console.log(node.data.name, node.x, node.y);
});
// Tree with fixed node sizes
const nodeTree = tree().nodeSize([50, 100]);
const nodeRoot = nodeTree(root.copy());
// Tree with custom separation
const customTree = tree()
.size([600, 300])
.separation((a, b) => a.parent === b.parent ? 1 : 2);
const customRoot = customTree(root.copy());Creates a cluster (dendrogram) layout where all leaf nodes are positioned at the same level.
/**
* Creates a cluster layout function
* @returns Cluster layout function with configuration methods
*/
function cluster();
interface ClusterLayout {
/** Apply cluster layout to hierarchy root */
(root: Node): Node;
/** Get/set the layout size [width, height] */
size(size?: [number, number]): ClusterLayout | [number, number] | null;
/** Get/set the node size [width, height] for fixed node dimensions */
nodeSize(size?: [number, number]): ClusterLayout | [number, number] | null;
/** Get/set the separation function between adjacent nodes */
separation(separation?: (a: Node, b: Node) => number): ClusterLayout | Function;
}Usage Examples:
import { hierarchy, cluster } from "d3-hierarchy";
// Basic cluster layout - all leaves at same level
const root = hierarchy(data);
const clusterLayout = cluster().size([400, 200]);
const clusterRoot = clusterLayout(root);
// Cluster with custom separation for dendrograms
const dendrogram = cluster()
.size([360, 200])
.separation((a, b) => (a.parent === b.parent ? 1 : 2) / a.depth);
const dendrogramRoot = dendrogram(root.copy());
// Fixed node size cluster
const fixedCluster = cluster().nodeSize([20, 180]);
const fixedRoot = fixedCluster(root.copy());Both tree and cluster layouts support the same configuration methods:
/**
* Set the layout size - scales output to fit within bounds
* @param size - [width, height] array or null to get current size
* @returns Layout function for chaining or current size
*/
size(size?: [number, number]): Layout | [number, number] | null;
/**
* Set fixed node dimensions - positions nodes with exact spacing
* @param size - [width, height] for each node or null to get current
* @returns Layout function for chaining or current node size
*/
nodeSize(size?: [number, number]): Layout | [number, number] | null;Usage Examples:
// Layout scaled to fit 800x600 area
const fitLayout = tree().size([800, 600]);
// Layout with 40px horizontal and 80px vertical spacing
const spacedLayout = tree().nodeSize([40, 80]);
// Get current configuration
const currentSize = fitLayout.size(); // [800, 600]
const currentNodeSize = spacedLayout.nodeSize(); // [40, 80]/**
* Set the separation function between adjacent nodes
* @param separation - Function returning separation distance
* @returns Layout function for chaining or current separation function
*/
separation(separation?: (a: Node, b: Node) => number): Layout | Function;The separation function receives two adjacent nodes and returns the desired separation distance.
Usage Examples:
// Default separation: 1 for siblings, 2 for cousins
const defaultSep = tree().separation((a, b) => a.parent === b.parent ? 1 : 2);
// Custom separation based on node depth (for radial layouts)
const radialSep = cluster().separation((a, b) => {
return (a.parent === b.parent ? 1 : 2) / a.depth;
});
// Separation based on node data
const dataSep = tree().separation((a, b) => {
if (a.parent === b.parent) {
return a.data.width + b.data.width; // Based on content width
}
return 2;
});Tree and cluster layouts set x and y properties on nodes:
For both layouts:
x represents the horizontal positiony represents the vertical depth from rooty = 0y valuesPolar Coordinates:
Convert to polar coordinates for radial trees:
const polarTree = tree().size([2 * Math.PI, 200]);
const root = polarTree(hierarchy(data));
root.each(node => {
const angle = node.x;
const radius = node.y;
node.x = radius * Math.cos(angle);
node.y = radius * Math.sin(angle);
});