Layout Algorithms
Seven built-in layout algorithms. Switch between them with a single property change.
import { create } from '@topokit/renderer-canvas';
const app = create(container, {
layout: 'force', // or 'combo-force', 'hierarchical', 'circular', 'dagre', 'radial', 'concentric', 'grid'
nodes: [...],
edges: [...],
});
// Switch layout at runtime
app.setLayout('hierarchical');
// Switch with options
app.setLayout('force', { strength: -200, distance: 100, iterations: 300 }); Force-Directed
Default layout. Simulates physical forces -- repulsion pushes nodes apart, edges act as springs. Best for general-purpose graphs and social networks.
app.setLayout('force', {
strength: -150, // repulsion (negative = repel)
distance: 80, // ideal edge length
iterations: 300, // simulation steps
gravity: 0.1, // pull toward center
damping: 0.9, // velocity decay per tick
}); | Option | Type | Default | Description |
|---|---|---|---|
strength | number | -150 | Repulsion force. More negative = more spread. |
distance | number | 80 | Target edge length. |
iterations | number | 300 | Simulation steps. |
gravity | number | 0.1 | Pull toward viewport center. |
damping | number | 0.9 | Velocity decay. Lower = faster settling. |
autoCollapseCombosForce | boolean | false | Top-level config option (not nested under layoutOptions). When true, bypasses the “user has already collapsed something → suppress auto-collapse” guard so force layouts always honor autoCollapseCombosThreshold. |
Combo-Force
Extends force with two extra passes that keep combo (group)
members tightly clustered: a per-combo centroid spring that pulls
siblings together, and a centroid-to-centroid Coulomb repulsion between
combos. Inter-combo repulsion is applied equally to every member,
so each combo translates rigidly rather than deforming.
force behavior is bit-identical when combo-force
is not selected — the extra passes only run inside this layout.
app.setLayout('combo-force', {
comboStrength: 0.15, // per-combo centroid spring
comboRepelStrength: 0.5, // inter-combo Coulomb repulsion multiplier
comboMinMembers: 2, // combos smaller than this skip both passes
// All force-layout options (strength, distance, iterations, gravity, damping) also apply
}); | Option | Type | Default | Description |
|---|---|---|---|
comboStrength | number | 0.15 | Spring constant pulling combo siblings toward their centroid. |
comboRepelStrength | number | 0.5 | Multiplier on centroid-to-centroid Coulomb repulsion. |
comboMinMembers | number | 2 | Combos with fewer than this many members skip both extra passes. |
Combo membership comes from node.parent. Nested combos are
not supported in this version — nesting on the combo container
itself is ignored.
Hierarchical
Layers from top to bottom (or left to right). Best for org charts, dependency trees, workflows.
app.setLayout('hierarchical', {
direction: 'TB', // 'TB' | 'BT' | 'LR' | 'RL'
levelSeparation: 120, // vertical gap between layers
nodeSeparation: 80, // horizontal gap between siblings
treeSpacing: 100, // spacing between disconnected trees
}); | Option | Type | Default | Description |
|---|---|---|---|
direction | 'TB' | 'BT' | 'LR' | 'RL' | 'TB' | Layout direction. |
levelSeparation | number | 120 | Space between levels. |
nodeSeparation | number | 80 | Space between same-level nodes. |
treeSpacing | number | 100 | Space between disconnected trees. |
Circular
All nodes on a single circle. Best for relationship visualization and connectivity patterns.
app.setLayout('circular', {
radius: 300, // circle radius (auto if omitted)
startAngle: 0, // starting angle in radians
orderBy: 'group', // order nodes by data field
}); Dagre
Sugiyama-style DAG layout. Minimizes edge crossings. Best for data pipelines and state machines.
app.setLayout('dagre', {
rankDir: 'TB', // 'TB' | 'BT' | 'LR' | 'RL'
rankSep: 80, // separation between ranks
nodeSep: 60, // separation within same rank
edgeSep: 20, // separation between edges
align: 'UL', // alignment: 'UL' | 'UR' | 'DL' | 'DR'
}); Radial
Concentric circles from a root node outward. Best for ego networks and distance-from-root visualization.
app.setLayout('radial', {
rootId: 'center-node', // auto-detected if omitted
levelSeparation: 100, // distance between rings
angleSeparation: 0.5, // minimum angle between siblings
}); Concentric
Multiple rings based on a numeric property (degree, importance, etc.). Higher-value nodes closer to center.
app.setLayout('concentric', {
sortBy: 'degree', // property or function
minRadius: 80, // innermost ring radius
levelSeparation: 60, // distance between rings
equidistant: true, // equal ring spacing
}); Grid
Uniform grid pattern. Best for tabular display where topology is secondary.
app.setLayout('grid', {
columns: 5, // auto if omitted
cellWidth: 120,
cellHeight: 120,
sortBy: 'data.name',
}); Choosing the Right Layout
| Use Case | Layout | Why |
|---|---|---|
| General exploration | force | Organic positioning reveals clusters |
| Combo / group-heavy graphs | combo-force | Keeps grouped nodes spatially together |
| Org charts / trees | hierarchical | Clear parent-child relationships |
| Data pipelines | dagre | Optimal layers, minimal crossings |
| Social networks | force / circular | Shows connectivity density |
| Ego / influence maps | radial | Distance = hops from root |
| Importance ranking | concentric | Central = most important |
| Catalog / registry | grid | Uniform, predictable |
Animated Transitions
Nodes animate smoothly between layouts when switching at runtime.
app.setLayout('circular', {}, {
animate: true,
duration: 800,
easing: 'easeInOutCubic',
}); Next Steps
- Filtering & Highlighting -- focus on subsets
- Clustering -- group nodes by property
- Web Worker Layout -- off-main-thread computation