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
Positioning forces push nodes toward desired positions along specific dimensions or toward circular positions. They include x-positioning, y-positioning, and radial positioning forces that are ideal for creating organized layouts and constraining nodes to specific areas.
Creates a positioning force along the x-axis toward a specified position.
/**
* Creates a new positioning force along the x-axis
* @param x - Target x position (number or accessor function, optional, defaults to 0)
* @returns XForce instance with configuration methods
*/
function forceX(x?: number | ((d: Node, i: number, nodes: Node[]) => number)): XForce;Usage Examples:
import { forceX } from "d3-force";
// Push all nodes toward x = 400
const xForce = forceX(400);
// Variable x position based on node data
const xForce = forceX(d => d.targetX || 200);
// Push toward center of viewport
const xForce = forceX(width / 2);Creates a positioning force along the y-axis toward a specified position.
/**
* Creates a new positioning force along the y-axis
* @param y - Target y position (number or accessor function, optional, defaults to 0)
* @returns YForce instance with configuration methods
*/
function forceY(y?: number | ((d: Node, i: number, nodes: Node[]) => number)): YForce;Usage Examples:
import { forceY } from "d3-force";
// Push all nodes toward y = 300
const yForce = forceY(300);
// Variable y position based on node data
const yForce = forceY(d => d.level * 50);
// Push toward center of viewport
const yForce = forceY(height / 2);Creates a positioning force toward a circle of specified radius.
/**
* Creates a new radial positioning force toward a circle
* @param radius - Target radius (number or accessor function, required)
* @param x - Circle center x coordinate (optional, defaults to 0)
* @param y - Circle center y coordinate (optional, defaults to 0)
* @returns RadialForce instance with configuration methods
*/
function forceRadial(radius: number | ((d: Node, i: number, nodes: Node[]) => number), x?: number, y?: number): RadialForce;Usage Examples:
import { forceRadial } from "d3-force";
// Push nodes toward circle of radius 100 at origin
const radialForce = forceRadial(100);
// Circle centered at specific coordinates
const radialForce = forceRadial(150, 400, 300);
// Variable radius based on node data
const radialForce = forceRadial(d => d.layer * 50, 400, 300);/**
* Sets or gets the target x position accessor
* @param x - Target x position (number or accessor function, optional)
* @returns Current x accessor or force instance for chaining
*/
x(x?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | XForce;/**
* Sets or gets the x-positioning strength accessor
* @param strength - Strength value or accessor function (optional, defaults to 0.1)
* @returns Current strength accessor or force instance for chaining
*/
strength(strength?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | XForce;/**
* Sets or gets the target y position accessor
* @param y - Target y position (number or accessor function, optional)
* @returns Current y accessor or force instance for chaining
*/
y(y?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | YForce;/**
* Sets or gets the y-positioning strength accessor
* @param strength - Strength value or accessor function (optional, defaults to 0.1)
* @returns Current strength accessor or force instance for chaining
*/
strength(strength?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | YForce;/**
* Sets or gets the radial positioning strength accessor
* @param strength - Strength value or accessor function (optional, defaults to 0.1)
* @returns Current strength accessor or force instance for chaining
*/
strength(strength?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | RadialForce;/**
* Sets or gets the target radius accessor
* @param radius - Target radius (number or accessor function, optional)
* @returns Current radius accessor or force instance for chaining
*/
radius(radius?: number | ((d: Node, i: number, nodes: Node[]) => number)): ((d: Node, i: number, nodes: Node[]) => number) | RadialForce;/**
* Sets or gets the x-coordinate of the circle center
* @param x - Center x coordinate (optional)
* @returns Current x coordinate or force instance for chaining
*/
x(x?: number): number | RadialForce;
/**
* Sets or gets the y-coordinate of the circle center
* @param y - Center y coordinate (optional)
* @returns Current y coordinate or force instance for chaining
*/
y(y?: number): number | RadialForce;Create one-dimensional scatter plots with collision:
// Horizontal beeswarm
const simulation = forceSimulation(data)
.force("x", forceX(d => xScale(d.value)).strength(0.8))
.force("y", forceY(height / 2).strength(0.1))
.force("collide", forceCollide(3));
// Vertical beeswarm
const simulation = forceSimulation(data)
.force("x", forceX(width / 2).strength(0.1))
.force("y", forceY(d => yScale(d.value)).strength(0.8))
.force("collide", forceCollide(3));Position nodes by level or category:
const simulation = forceSimulation(nodes)
.force("x", forceX(d => {
switch(d.category) {
case "A": return 200;
case "B": return 400;
case "C": return 600;
default: return 400;
}
}).strength(0.5))
.force("y", forceY(d => d.level * 100).strength(0.8))
.force("charge", forceManyBody().strength(-50));Create layered circular arrangements:
const simulation = forceSimulation(nodes)
.force("radial", forceRadial(d => d.layer * 80, 400, 300).strength(0.7))
.force("charge", forceManyBody().strength(-30))
.force("collide", forceCollide(5));Keep network within specific bounds:
const simulation = forceSimulation(nodes)
.force("link", forceLink(links))
.force("charge", forceManyBody())
.force("center", forceCenter(400, 300))
.force("x", forceX(400).strength(0.1)) // Weak pull toward center
.force("y", forceY(300).strength(0.1)); // Weak pull toward centerPosition nodes in grid-like arrangements:
const gridSize = Math.ceil(Math.sqrt(nodes.length));
const simulation = forceSimulation(nodes)
.force("x", forceX((d, i) => (i % gridSize) * 50).strength(0.8))
.force("y", forceY((d, i) => Math.floor(i / gridSize) * 50).strength(0.8))
.force("collide", forceCollide(20));Update target positions based on user interaction:
const xForce = forceX(d => d.targetX || 400);
const yForce = forceY(d => d.targetY || 300);
simulation
.force("x", xForce)
.force("y", yForce);
function moveNodesTo(nodes, newPositions) {
nodes.forEach((node, i) => {
node.targetX = newPositions[i].x;
node.targetY = newPositions[i].y;
});
// Update force accessors
xForce.x(d => d.targetX);
yForce.y(d => d.targetY);
// Reheat simulation
simulation.alpha(0.3).restart();
}Adjust radial positioning based on container size:
const radialForce = forceRadial(100, width / 2, height / 2);
simulation.force("radial", radialForce);
window.addEventListener("resize", () => {
const newWidth = window.innerWidth;
const newHeight = window.innerHeight;
const newRadius = Math.min(newWidth, newHeight) * 0.3;
radialForce
.radius(newRadius)
.x(newWidth / 2)
.y(newHeight / 2);
simulation.alpha(0.3).restart();
});Create concentric circles with different radii:
const simulation = forceSimulation(nodes)
.force("radial", forceRadial(d => {
switch(d.ring) {
case 0: return 50; // Inner ring
case 1: return 120; // Middle ring
case 2: return 200; // Outer ring
default: return 100;
}
}, 400, 300).strength(0.8))
.force("charge", forceManyBody().strength(-20))
.force("collide", forceCollide(8));Positioning force strength determines how quickly nodes move toward target positions:
Positioning forces apply velocity changes proportional to distance from target:
// For X positioning
node.vx += (targetX - node.x) * strength * alpha;
// For Y positioning
node.vy += (targetY - node.y) * strength * alpha;
// For radial positioning
const dx = node.x - centerX;
const dy = node.y - centerY;
const distance = Math.sqrt(dx * dx + dy * dy);
const force = (targetRadius - distance) * strength * alpha / distance;
node.vx += dx * force;
node.vy += dy * force;