Force-directed graph layout using velocity Verlet integration for simulating physical forces on particles
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
The link force creates spring-like connections between nodes, pushing linked nodes together or apart according to desired link distance. It's essential for graph layouts and network visualizations, with configurable strength based on node connectivity.
Creates a new link force with specified links array.
/**
* Creates a new link force with the specified links array
* @param links - Array of link objects (optional, defaults to empty array)
* @returns LinkForce instance with configuration methods
*/
function forceLink(links?: Link[]): LinkForce;Usage Examples:
import { forceLink } from "d3-force";
// Create with links array
const links = [
{ source: 0, target: 1 },
{ source: 1, target: 2 }
];
const linkForce = forceLink(links);
// Create empty and add links later
const linkForce = forceLink();
linkForce.links(links);
// Add to simulation
simulation.force("link", forceLink(links));Configure the array of links for the force.
/**
* Sets or gets the array of links associated with this force
* @param links - Array of link objects (optional)
* @returns Current links array or force instance for chaining
*/
links(links?: Link[]): Link[] | LinkForce;Usage Examples:
const linkForce = forceLink();
// Set links with numeric indices
const links = [
{ source: 0, target: 1 },
{ source: 1, target: 2 },
{ source: 2, target: 0 }
];
linkForce.links(links);
// Set links with string IDs (requires id accessor)
const namedLinks = [
{ source: "Alice", target: "Bob" },
{ source: "Bob", target: "Carol" }
];
linkForce.id(d => d.id).links(namedLinks);
// Get current links
const currentLinks = linkForce.links();Configure how nodes are identified for link source/target resolution.
/**
* Sets or gets the node id accessor function
* @param id - ID accessor function (optional)
* @returns Current id accessor or force instance for chaining
*/
id(id?: (d: Node, i: number, nodes: Node[]) => string | number): ((d: Node, i: number, nodes: Node[]) => string | number) | LinkForce;Usage Examples:
const linkForce = forceLink();
// Use node index (default)
linkForce.id(d => d.index);
// Use custom id property
linkForce.id(d => d.id);
// Use name property
linkForce.id(d => d.name);
// Complex id generation
linkForce.id((d, i) => d.customId || i);Configure the desired distance between linked nodes.
/**
* Sets or gets the distance accessor for links
* @param distance - Distance value or accessor function (optional, defaults to 30)
* @returns Current distance accessor or force instance for chaining
*/
distance(distance?: number | ((d: Link, i: number, links: Link[]) => number)): ((d: Link, i: number, links: Link[]) => number) | LinkForce;Usage Examples:
const linkForce = forceLink(links);
// Fixed distance for all links
linkForce.distance(50);
// Variable distance based on link properties
linkForce.distance(d => d.distance || 30);
// Distance based on link type
linkForce.distance(d => {
switch (d.type) {
case "strong": return 20;
case "weak": return 80;
default: return 50;
}
});Configure the strength of link constraints.
/**
* Sets or gets the strength accessor for links
* @param strength - Strength value or accessor function (optional)
* @returns Current strength accessor or force instance for chaining
*/
strength(strength?: number | ((d: Link, i: number, links: Link[]) => number)): ((d: Link, i: number, links: Link[]) => number) | LinkForce;Usage Examples:
const linkForce = forceLink(links);
// Fixed strength for all links (overrides default calculation)
linkForce.strength(0.5);
// Variable strength based on link properties
linkForce.strength(d => d.strength || 0.3);
// Default: automatic strength based on connectivity
// (strength is automatically 1 / min(degree of source, degree of target))Configure the number of constraint resolution iterations per simulation tick.
/**
* Sets or gets the number of iterations per application
* @param iterations - Number of iterations (optional, defaults to 1)
* @returns Current iteration count or force instance for chaining
*/
iterations(iterations?: number): number | LinkForce;Usage Examples:
const linkForce = forceLink(links);
// Single iteration (default)
linkForce.iterations(1);
// Multiple iterations for rigid constraints
linkForce.iterations(3);
// High iterations for lattice structures
linkForce.iterations(5);Create a simple force-directed network:
const nodes = [
{ id: "A" },
{ id: "B" },
{ id: "C" }
];
const links = [
{ source: "A", target: "B" },
{ source: "B", target: "C" }
];
const simulation = forceSimulation(nodes)
.force("link", forceLink(links).id(d => d.id))
.force("charge", forceManyBody())
.force("center", forceCenter(400, 300));Create networks with different link types and distances:
const links = [
{ source: "root", target: "child1", type: "parent" },
{ source: "root", target: "child2", type: "parent" },
{ source: "child1", target: "grandchild1", type: "parent" },
{ source: "child1", target: "child2", type: "sibling" }
];
const simulation = forceSimulation(nodes)
.force("link", forceLink(links)
.id(d => d.id)
.distance(d => d.type === "parent" ? 50 : 80)
.strength(d => d.type === "parent" ? 0.8 : 0.2)
);Create rigid grid structures with high iterations:
// Create grid links
const gridLinks = [];
for (let i = 0; i < width; i++) {
for (let j = 0; j < height; j++) {
const nodeId = i * height + j;
if (i < width - 1) gridLinks.push({ source: nodeId, target: nodeId + height });
if (j < height - 1) gridLinks.push({ source: nodeId, target: nodeId + 1 });
}
}
const simulation = forceSimulation(gridNodes)
.force("link", forceLink(gridLinks)
.distance(20)
.strength(1)
.iterations(6) // High iterations for rigid grid
);Add or remove links dynamically:
const linkForce = forceLink(initialLinks).id(d => d.id);
simulation.force("link", linkForce);
function addLink(sourceId, targetId) {
const links = linkForce.links();
links.push({ source: sourceId, target: targetId });
// Update force with new links array
linkForce.links(links);
// Reheat simulation
simulation.alpha(0.3).restart();
}
function removeLink(sourceId, targetId) {
const links = linkForce.links().filter(link =>
!(link.source.id === sourceId && link.target.id === targetId)
);
linkForce.links(links);
simulation.alpha(0.3).restart();
}Use link weights to determine strength and distance:
const weightedLinks = [
{ source: "A", target: "B", weight: 5 },
{ source: "B", target: "C", weight: 2 },
{ source: "A", target: "C", weight: 1 }
];
const simulation = forceSimulation(nodes)
.force("link", forceLink(weightedLinks)
.id(d => d.id)
.distance(d => 100 / d.weight) // Shorter distance for stronger connections
.strength(d => d.weight / 10) // Higher strength for stronger connections
);Links are automatically enhanced with additional properties:
const link = {
source: "A", // Original: string/number/object
target: "B", // Original: string/number/object
index: 0, // Added: zero-based index in links array
// source and target are replaced with actual node object references
};
// After force initialization:
console.log(link.source); // Node object with id "A"
console.log(link.target); // Node object with id "B"d => d.index