This simple force-directed graph shows character co-occurence in Les Misérables. A physical simulation of charged particles and springs places related characters in closer proximity, while unrelated characters are farther apart. Layout algorithm inspired by Tim Dwyer and Thomas Jakobsen. Data based on character coappearence in Victor Hugo's Les Misérables, compiled by Donald Knuth.
Compare this display to a force layout with curved links, a force layout with fisheye distortion and a matrix diagram.
xxxxxxxxxx
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.force("distant", 10)
var links = svg.append("g")
.attr("class", "links")
var nodes = svg.append("g")
.attr("class", "nodes")
d3.json("miserables.json", function(error, data) {
if (error) throw error;
function render(graph){
var link = links
.selectAll("line")
.data(graph.links)
var link_enter = link.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = nodes
.selectAll("circle")
.data(graph.nodes)
var node_enter = node
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); })
node.exit().remove()
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.attr("x1", function(d) {console.log(d); return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
}
var i=0
var interval_id = setInterval(function(){
console.log(i,data, data[i])
if (i>=data.length-1){
clearInterval(interval_id)
}
render(data[i++])
}, 1000)
});
</script>
https://d3js.org/d3.v4.min.js