D3’s force layout uses a physics based simulator for positioning visual elements. Forces can be set up between elements, for example:
- all elements repel one another
- elements are attracted to center(s) of gravity
- linked elements (e.g. friendship) are a fixed distance apart (network visualisation)
- elements may not overlap (collision detection)
The force layout allows us to position elements in a way that would be difficult to achieve using other means.
As an example we have a number of circles (each of which has a category
C) and add the following forces:
- all circles attract one another (to clump circles together)
- collision detection (to stop circles overlapping)
- circles are attracted to one of three centers, depending on their category
The force layout requires a larger amount of computation (typically requiring a few seconds of time) than other D3 layouts and and the solution is calculated in a step by step (iterative) manner. Usually the positions of the SVG/HTML elements are updated as the simulation iterates, which is why we see the circles jostling into position.
Setting up a force simulation
Broadly speaking there are 4 steps to setting up a force simulation:
- create an array of objects
forceSimulation, passing in the array of objects
- add one or more force functions (e.g.
forceCollide) to the system
- set up a callback function to update the element positions after each tick
Let’s start with a minimal example:
Here we’ve created a simple array of 5 objects and have added two force functions
forceCenter to the system. (The first of these makes the elements repel each other while the second attracts the elements towards a centre point.)
Each time the simulation iterates the function
ticked will be called. This function joins the
nodes array to
circle elements and updates their positions:
The power and flexibility of the force simulation is centred around force functions which adjust the position and velocity of elements to achieve a number of effects such as attraction, repulstion and collision detection. We can define our own force functions but D3 comes with a number of useful ones built in:
forceCenter(for setting the center of gravity of the system)
forceManyBody(for making elements attract or repel one another)
forceCollide(for preventing elements overlapping)
forceY(for attracting elements to a given point)
forceLink(for creating a fixed distance between connected elements)
Force functions are added to the simulation using
.force() where the first argument is a user defined id and the second argument the force function:
Let’s look at the inbuilt force functions one by one.
forceCenter is useful (if not essential) for centering your elements as a whole about a center point. (Without it elements might disappear off the page.)
It can either be initialised with a center position:
or using the configuration functions
We add it to the system using:
See below for usage examples.
forceManyBody causes all elements to attract or repel one another. The strength of the attraction or repulsion can be set using
.strength() where a positive value will cause elements to attract one another while a negative value causes elements to repel each other. The default value is
As a rule of thumb, when creating network diagrams we want the elements to repel one another while for visualisations where we’re clumping elements together, attractive forces are necessary.
forceCollide is used to stop elements overlapping and is particularly useful when ‘clumping’ circles together.
We must specify the radius of the elements using
forceX and forceY
forceY cause elements to be attracted towards specified position(s). We can use a single center for all elements or apply the force on a per-element basis. The strength of attraction can be configured using
As an example suppose we have a number of elements, each of which has a category
2. We can add a
forceX force function to attract the elements to an x-coordinate
500 based on the element’s category:
Note the above example also uses
If our data has a numeric dimension we can use
forceY to position elements along an axis:
Do use the above with caution as the x position of the elements is only approximate.
forceLink pushes linked elements to be a fixed distance apart. It requires an array of links that specify which elements we want to link together. Each link object specifies a source and target element, where the value is the element’s array index:
We can then pass our links array into the
forceLink function using
The distance and strength of the linked elements can be configured using
.distance() (default value is 30) and