Skip to content

Instantly share code, notes, and snippets.

@jmacqueen
Last active December 27, 2015 19:39
Show Gist options
  • Save jmacqueen/7378919 to your computer and use it in GitHub Desktop.
Save jmacqueen/7378919 to your computer and use it in GitHub Desktop.
D3.js: Exploring Scaling Options for Force Directed Layouts

Using the random node and link generator, I wanted to play with some scaling options to get a feel for what the values all mean. Here we have some basic HTML5 sliders to set ranges. Sorry old IE users. I didn't feel like busting out jQueryUI.

Gravity acts to pull all of the nodes into the center of the view.

Charge is the force acting in between the individual nodes. The Charge Scaling slider applies a proportional weighting to each node based on the number of links it has. Set at 0, all nodes have the default charge of -30.

The Node Scaling adjusts the size of the individual nodes proportional to the number of links it possesses.

Node scaling is very handy in identifying major influencers (the most highly connected) in the graph. Increasing the gravity and charge scaling together help impose greater structure to the graph display.

If you want to interact with these D3.js gists without downloading them yourself, replace https://gist.github.com with http://bl.ocks.org to go to Mike Bostock's excellent website for running D3.js gists.

body {
font-family: Helvetica,sans-serif;
font-size: 14px;
}
.controls {
border-bottom: solid 1px black;
padding: 1em 5%;
text-align: center;
}
.controls label {
display: inline-block;
margin: 0.5em 1em;
}
span[id $= "Value"] {
display: inline-block;
width: 3em;
}
svg {
border: solid 1px black;
display: block;
margin: 1em auto;
max-width: 100%;
}
.node {
fill: lightblue;
stroke: black;
}
.link {
stroke: black;
stroke-width: 1px;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Force Layout Link Scaling</title>
<link rel="stylesheet" href="force-layout-scaling.css">
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<div class="controls">
<label>Gravity:&nbsp;<input id="gravitySlider" type="range" min=0 max=2 step=0.1>&nbsp;<span id="gravityValue"></span></label>
<label>Charge Scaling:&nbsp;<input id="chargeSlider" type="range" min=0 max=400 step=10>&nbsp;<span id="chargeValue"></span></label>
<label>Node Scaling:&nbsp;<input id="nodeSlider" type="range" min=0 max=4 step=0.1>&nbsp;<span id="nodeValue"></span></label>
</div>
<script>
var width = 800;
var height = 400;
var numPoints = 100;
var initGravity = 0.2;
var svg = d3.select("body").append("svg")
.attr("width", width).attr("height", height)
.attr("viewBox", "0 0 " + width + " " + height)
.attr("preserveAspectRatio", "xMidYMid");
var force = d3.layout.force().size([width,height]);
var nodes = [];
for (var j=0;j<numPoints;j++){
nodes[j] = {
name: "Node " + j
};
}
var links = [];
for (j=0;j<numPoints;j++){
var src = Math.floor(Math.random() * numPoints);
var tar = Math.floor(Math.random() * numPoints);
if (tar === src) {
if (src != 0) {
tar = src - 1;
} else {
tar = 1;
}
}
links[j] = {
source: src,
target: tar
};
}
force.links(links);
var linkSelection = svg.selectAll("line").data(links);
linkSelection.enter()
.insert("line")
.classed("link", true);
force.nodes(nodes);
var nodeSelection = svg.selectAll("circle.node").data(nodes);
nodeSelection.enter()
.append("circle")
.attr("r", 5)
.classed("node", true)
.call(force.drag);
force.gravity(initGravity);
force.on("start", function(){
force.charge(function(node,i){
var chargeScale = d3.select("#chargeSlider").property("valueAsNumber");
return -1*(chargeScale * node.weight + 30);
});
});
force.on("tick", function(e){
var nodeScaling = d3.select("#nodeSlider").property("valueAsNumber");
linkSelection.attr("x1", function(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;});
nodeSelection.attr("cx", function(d){return d.x;})
.attr("cy", function(d) {return d.y})
.attr("r", function(d){return d.weight * nodeScaling + 2;});
});
d3.select("#gravitySlider").on("change", function(){
var val = this.value;
d3.select("#gravityValue").text(val);
force.gravity(val);
force.resume();
});
d3.select("#chargeSlider").on("change", function(){
var val = this.value;
d3.select("#chargeValue").text(val);
force.start();
});
d3.select("#nodeSlider").on("change", function(){
var val = this.value;
d3.select("#nodeValue").text(val);
force.resume();
});
// Main
d3.select("#gravitySlider").property("value",force.gravity());
d3.select("#gravityValue").text(force.gravity());
d3.select("#chargeSlider").property("value", 0);
d3.select("#chargeValue").text(0);
d3.select("#nodeSlider").property("value", 0);
d3.select("#nodeValue").text(0);
force.start();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment