Skip to content

Force-Directed Network

The Force-Directed Network lays out a graph of nodes and links with a physics simulation: repulsion pushes nodes apart, link springs pull connected nodes together, and a centering force keeps the whole thing on screen. Use it when the shape of a relationship graph is what you want to read — social graphs, dependency trees, topic maps — and no fixed ordering exists to lay it out by. charge, linkDistance, linkStrength, centerStrength and iterations tune the simulation, and root picks the node the entry animation springs from. The layout is deterministic, so the same data always settles the same way; reweighting relaxes the simulation from its current positions rather than restarting it. Canvas is the default target; pass an SVG or terminal context to draw the same graph elsewhere.

NOTE

For the full API, see the Charts API Reference.

Example

Usage

ts
import {
    createForceDirectedChart,
} from '@ripl/charts';

const chart = createForceDirectedChart('#container', {
    nodes: [
        {
            id: 'a',
            label: 'A',
            group: 'x',
        },
        {
            id: 'b',
            label: 'B',
            group: 'x',
        },
        {
            id: 'c',
            label: 'C',
            group: 'y',
        },
    ],
    links: [
        {
            source: 'a',
            target: 'b',
            value: 4,
        },
        {
            source: 'b',
            target: 'c',
            value: 2,
        },
    ],
});

Data Format

Provide nodes (each with a unique id, optional label, group, value, color) and links (each with source/target node ids and an optional value). Node size defaults to its link degree when no value is given; nodes in the same group share a color.

Options

A full configuration for this chart. The options every chart shares — padding, title, animation, theme and the rest — behave the same everywhere and are documented on Shared Options.

ts
createForceDirectedChart('#container', {
    nodes,
    links,
    nodeRadius: 7,
    // Negative charge repels; a larger magnitude spreads the graph further.
    charge: -160,
    linkDistance: 40,
    linkStrength: 0.6,
    centerStrength: 0.05,
    // Simulation passes run before the layout is drawn.
    iterations: 300,
    // The node the layout springs out from on entry; defaults to the highest-degree node.
    root: 'core',
    legend: { position: 'bottom' },
    format: 'number',
});

Events

Subscribe with chart.on(...). A handler receives an Event object, not the payload directly — the payload is on event.data, and carries the interacted datum plus its { x, y } anchor in chart pixels. event.target and event.stopPropagation() are also available.

ts
// Emitted when a node is clicked.
chart.on('nodeclick', event => console.log(event.data)); // event.data: ForceDirectedNodeEvent<TData>
// Emitted when the pointer enters a node.
chart.on('nodeenter', event => console.log(event.data)); // event.data: ForceDirectedNodeEvent<TData>
// Emitted when the pointer leaves a node.
chart.on('nodeleave', event => console.log(event.data)); // event.data: ForceDirectedNodeEvent<TData>
// Emitted when a link is clicked.
chart.on('linkclick', event => console.log(event.data)); // event.data: ForceDirectedLinkEvent
// Emitted when the pointer enters a link.
chart.on('linkenter', event => console.log(event.data)); // event.data: ForceDirectedLinkEvent
// Emitted when the pointer leaves a link.
chart.on('linkleave', event => console.log(event.data)); // event.data: ForceDirectedLinkEvent