or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

hierarchy-construction.mdindex.mdnode-methods.mdpartition-pack.mdtree-layouts.mdtreemap-layouts.md
tile.json

tree-layouts.mddocs/

Tree Layouts

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.

Capabilities

Tree Layout

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());

Cluster Layout

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());

Layout Configuration

Both tree and cluster layouts support the same configuration methods:

Size Configuration

/**
 * 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]

Separation Configuration

/**
 * 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;
});

Coordinate System

Tree and cluster layouts set x and y properties on nodes:

  • Tree layout: Positions nodes to minimize overlaps while maintaining parent-child relationships
  • Cluster layout: Positions all leaf nodes at the same level (y-coordinate)

For both layouts:

  • x represents the horizontal position
  • y represents the vertical depth from root
  • Root node is typically at y = 0
  • Deeper nodes have larger y values

Polar 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);
});