Skip to main content
Version: 4.1

Force directed layout

ForceDirected class in the layout namespace implements automatic layouts for graphs using a force-directed approach. This is useful for, usually larger, undirected graphs. It is more useful for visualizing planar graphs with not many connections between elements. You can look at the API documentation here.

Usage​

Force directed layout auto-layouts graphs based on three forces: repulsive force (forces elements to move out of each other), attractive force (tries to get elements together like a spring) and a gravity force (tendency of the elements to move to a certain point). Gravity force is optional and is disabled by default. To enable it you need to set the gravityCenter parameter.

The ForceDirected constructor accepts multiple parameters for configuring the layout process. There is no one-fits-all set of parameters that would give great result for all possible graphs. Therefore, it is suggested that you to play with the parameters, try to set different values so that it gives a good result for your use case.

Let's create a simple setup for the force directed layout:

const forceLayout = new layout.ForceDirected({
graph: graph,
layoutArea: {
x: 0,
y: 0,
width: 800,
height: 800
},
gravityCenter: { x: 400, y: 400 }
});

Here the graph is a mandatory option, which specifies which cells should be laid out. The layoutArea option specifies the area where the layout should take place. It is optional option which restricts the movement of shapes. The gravityCenter option specifies the center of the gravity force, if it is not presented the gravity force is not applied to the graph.

You can control the layout process using several methods of the layout class:

  • start() - initiates layout process;
  • step() - applies forces to the graph and updates the position of shapes for one step;
  • restart - restarts the layout process with a given temperature;
  • finalize() - simulating the layout until it finishes the process;
note

It is recommended to use the requestAnimationFrame for stepping the layout. JointJS provides helper function util.nextFrame() for working with animation.

Here is the example of a function which creates an animation using force directed layout:

function animate() {
util.nextFrame(animate);
forceLayout.step();
}

Here is the example of force directed layout in action. You can add new elements by clicking on the elements and drag them to another position:

Cell attributes​

Besides the option which you pass to the constructor, you can control the behavior of the layout by setting attributes on specific cells. They are all contained in the forceDirectedAttributes object in model attributes. You can change the name of the attribute using attributesName static property. The following attributes are supported:

AttributeDescription
weightSets the weight of the element
fixedMakes the position of the element fixed in place
radiusSets the radius for the element
strengthSets the strength of the link
distanceSets the target distance of the link
note

The attributes are read only once at the time of adding element to the layout. If you want to change the attributes during the layout process, you have to call the changeElementData() or changeLinkData() method.

in the following example we set the specific weight of the ellipse element to allow them to be more spread out, and specify the rectangle elements as fixed with the determined position to create nice diagram:

Setting the weight attribute in a shape definition:

export class Attribute extends shapes.standard.Ellipse {
defaults() {
return util.defaults({
// other attributes
forceDirectedAttributes: {
weight: 3
},
}, super.defaults);
}
}

Setting the fixed attribute:

// snippet of a graph definition
{
id: 1,
type: 'erd.Entity',
// fixed position
position: { x: 400, y: 350 },
forceDirectedAttributes: {
fixed: true
},
attrs: {
label: {
text: 'Subject'
}
}
},

You can look at the demo here. You can drag fixed elements to the desired position and other elements will be automatically placed around them. As you can see in the graph.ts file we are not setting positions of other elements because they are automatically placed by the force directed layout. Also, make notice that we are setting linkBias to false as it is not very suitable for graphs with fixed elements. Additionally, we are using radial force to keep the elements away from each other:

Radial force​

The radial force is a special force that can be used to keep the elements away from each other. It is useful when you want to create a diagram where elements are placed around a specific point. You can enable radial force by setting the radialForceStrength option in the constructor. The radial force is applied to all elements in the graph.

Here is an example of a radial force in action. The elements in the example are moved by a gravity to the center of the paper (using gravityType:'elementUniform'). The repulsive force is disabled by setting charge:0 and the tTarget is set to 0.4 to allow smooth simulation when elements are moved around:

const forceLayout = new layout.ForceDirected({
graph: graph,
gravityCenter: { x: 400, y: 400 },
gravityType: 'elementUniform',
gravity: 500,
radialForceStrength: 1000,
charge: 0,
tTarget: 0.4
});

Additionally, you can remove the elements from the simulation by right-clicking them. It is achieved with the layout.removeElement method.

paper.on('element:contextmenu', (elementView: dia.ElementView, evt) => {
evt.preventDefault();
evt.stopPropagation();

const element = elementView.model;

forceLayout.removeElement(element.id);
element.remove();
});

Stay in the know

Be where thousands of diagramming enthusiasts meet

Star us on GitHub