Force-directed graph layout using velocity Verlet integration for simulating physical forces on particles
npx @tessl/cli install tessl/npm-d3-force@3.0.0d3-force is a JavaScript ES6 module that implements a velocity Verlet numerical integrator for simulating physical forces on particles. It provides a comprehensive force-directed graph layout system specifically designed for information visualization, offering various force types including center forces, collision detection, link forces, many-body forces, and positional forces.
npm install d3-forceimport { forceSimulation, forceCenter, forceCollide, forceLink, forceManyBody, forceRadial, forceX, forceY, x, y } from "d3-force";For CommonJS:
const { forceSimulation, forceCenter, forceCollide, forceLink, forceManyBody, forceRadial, forceX, forceY, x, y } = require("d3-force");import { forceSimulation, forceCenter, forceCollide, forceLink, forceManyBody } from "d3-force";
// Create nodes and links data
const nodes = [
{ id: "A" },
{ id: "B" },
{ id: "C" }
];
const links = [
{ source: "A", target: "B" },
{ source: "B", target: "C" }
];
// Create and configure simulation
const simulation = forceSimulation(nodes)
.force("link", forceLink(links).id(d => d.id))
.force("charge", forceManyBody())
.force("center", forceCenter(400, 300));
// Listen for simulation events
simulation.on("tick", () => {
console.log("Nodes positions updated:", nodes);
});
simulation.on("end", () => {
console.log("Simulation completed");
});d3-force is built around several key components:
forceSimulation manages the physics loop, alpha cooling, and force applicationCore simulation engine that manages the physics loop, applies forces, and handles node lifecycle. Essential for all force-directed layouts and physics simulations.
function forceSimulation(nodes?: Node[]): Simulation;
interface Simulation {
tick(iterations?: number): Simulation;
restart(): Simulation;
stop(): Simulation;
nodes(nodes?: Node[]): Node[] | Simulation;
alpha(alpha?: number): number | Simulation;
alphaMin(min?: number): number | Simulation;
alphaDecay(decay?: number): number | Simulation;
alphaTarget(target?: number): number | Simulation;
velocityDecay(decay?: number): number | Simulation;
randomSource(source?: () => number): (() => number) | Simulation;
force(name: string, force?: Force): Force | Simulation;
find(x: number, y: number, radius?: number): Node | undefined;
on(typenames: string, listener?: (this: Simulation) => void): Simulation | ((this: Simulation) => void);
}Centering force translates nodes uniformly so that the mean position of all nodes is at a given position. Maintains relative positioning while keeping nodes centered in viewport.
function forceCenter(x?: number, y?: number): CenterForce;
interface CenterForce {
x(x?: number): number | CenterForce;
y(y?: number): number | CenterForce;
strength(strength?: number): number | CenterForce;
}Collision force treats nodes as circles with configurable radius and prevents overlapping. Uses quadtree spatial partitioning for efficient collision detection.
function forceCollide(radius?: number | ((d: Node, i: number, nodes: Node[]) => number)): CollideForce;
interface CollideForce {
radius(radius?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | CollideForce;
strength(strength?: number): number | CollideForce;
iterations(iterations?: number): number | CollideForce;
}Link force creates spring-like connections between nodes, maintaining desired distances. Essential for graph layouts and network visualizations.
function forceLink(links?: Link[]): LinkForce;
interface LinkForce {
links(links?: Link[]): Link[] | LinkForce;
id(id?: (d: Node, i: number, nodes: Node[]) => string | number): ((d: Node, i: number, nodes: Node[]) => string | number) | LinkForce;
distance(distance?: number | ((d: Link, i: number, links: Link[]) => number)): ((d: Link, i: number, links: Link[]) => number) | LinkForce;
strength(strength?: number | ((d: Link, i: number, links: Link[]) => number)): ((d: Link, i: number, links: Link[]) => number) | LinkForce;
iterations(iterations?: number): number | LinkForce;
}Many-body force applies mutually among all nodes using Barnes-Hut approximation for efficient n-body simulation. Can simulate gravity (attraction) or electrostatic charge (repulsion).
function forceManyBody(): ManyBodyForce;
interface ManyBodyForce {
strength(strength?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | ManyBodyForce;
theta(theta?: number): number | ManyBodyForce;
distanceMin(distance?: number): number | ManyBodyForce;
distanceMax(distance?: number): number | ManyBodyForce;
}Positioning forces push nodes toward desired positions along specific dimensions. Includes x-positioning, y-positioning, and radial positioning for various layout patterns.
function forceX(x?: number | ((d: Node, i: number, nodes: Node[]) => number)): XForce;
function forceY(y?: number | ((d: Node, i: number, nodes: Node[]) => number)): YForce;
function forceRadial(radius: number | ((d: Node, i: number, nodes: Node[]) => number), x?: number, y?: number): RadialForce;
interface XForce {
strength(strength?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | XForce;
x(x?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | XForce;
}
interface YForce {
strength(strength?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | YForce;
y(y?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | YForce;
}
interface RadialForce {
strength(strength?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | RadialForce;
radius(radius?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | RadialForce;
x(x?: number): number | RadialForce;
y(y?: number): number | RadialForce;
}Default accessor functions for extracting x and y coordinates from nodes.
function x(d: Node): number;
function y(d: Node): number;interface Node {
index?: number;
x?: number;
y?: number;
vx?: number;
vy?: number;
fx?: number | null;
fy?: number | null;
[key: string]: any;
}
interface Link {
source: Node | string | number;
target: Node | string | number;
index?: number;
[key: string]: any;
}
interface Force {
(alpha: number): void;
initialize?(nodes: Node[], random: () => number): void;
}