A force-directed graph is a type of graph which represents nodes and edges using physical simulation so that the connected nodes are closer to each other while the nodes that are not directly connected are pulled apart.
To simulate the force-directed graph, let's assume we replace the vertices with electrically charged steel rings of same charge. Moreover, we replace each edge with a spring, forming a mechanical system.
Now in this system, two types of forces determine the final shape of the graph:
Repulsive forces: They arise from the electrical charges on the steel rings (nodes). Every node tries to repulse the other, maintaining a clear separation among themselves, which makes the graph more readable.
Attractive forces: These are the forces that keeps the adjacent nodes connected, minimizing the length of an edge.
The vertices are placed in some initial layout and let go. By balancing the attractive and repulsive forces, the force-directed layout algorithm iteratively adjusts the positions of the nodes until an
D3.js is a standard JavaScript library for creating interactive and dynamic data visualization on the web, including force-directed graphs.
Here's how you can visualize a force-directed graph:
// Sample data (nodes and links) var nodes = [ { id: "A" }, { id: "B" }, { id: "C" }, { id: "D" }, { id: "E" }, ]; var links = [ { source: "A", target: "B" }, { source: "A", target: "C" }, { source: "A", target: "D" }, { source: "D", target: "E" }, ]; // Define the dimensions of the SVG container var width = 600; var height = 400; // Create the SVG container var svg = d3 .select("body") .append("svg") .attr("width", width) .attr("height", height); // Create a D3 force simulation var simulation = d3.forceSimulation(nodes) .force("link", d3.forceLink(links).id(d => d.id).distance(100)) .force("charge", d3.forceManyBody().strength(-200)) .force("center", d3.forceCenter(width / 2, height / 2)); // Create SVG elements for links var link = svg.selectAll("line") .data(links) .enter() .append("line") .attr("stroke", "gray") .attr("stroke-width", 2); // Create SVG elements for nodes var node = svg.selectAll("circle") .data(nodes) .enter() .append("circle") .attr("r", 20) .attr("fill", "steelblue"); // Create labels for nodes var labels = svg.selectAll("text") .data(nodes) .enter() .append("text") .text(d => d.id) .attr("font-size", 12) .attr("dx", 25) .attr("dy", 5); // Update node and link positions in each tick of the simulation simulation.on("tick", () => { link.attr("x1", d => d.source.x) .attr("y1", d => d.source.y) .attr("x2", d => d.target.x) .attr("y2", d => d.target.y); node.attr("cx", d => d.x) .attr("cy", d => d.y); labels.attr("x", d => d.x) .attr("y", d => d.y); });
Lines 1–8: Defining nodes of the graph and grouping them
Lines 10–15: Linking all the nodes with each other
Lines 18–19: Defining the width and height of the SVG container (in pixels) on which the graph will be displayed.
Lines 22–26: Attributing the SVG container with defined width and height.
Lines 29–32: Adding simulations to the graph.
Lines 35–40: Creating an SVG object for the graph's edges.
Lines 43–48: Creating an SVG object for the graph nodes.
Lines 51–58: Adding labels to the nodes of the graphs.
Lines 67–72: Defining the drag event handler and creating the simulations so the graph can mold into an equilibrium state once the code launches.
Note: Learn more about D3 transitions and D3 selectors.
Free Resources