Skip to content

Instantly share code, notes, and snippets.

@Paul-Brady
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Paul-Brady/473f257f72148a3be71f to your computer and use it in GitHub Desktop.
Save Paul-Brady/473f257f72148a3be71f to your computer and use it in GitHub Desktop.
Collapsible force directed graph.

This is based on Mike Bostock's work: http://bl.ocks.org/mbostock/1062288. I wanted to include labels on some but not all of the nodes. The labels on the terminii appear as mouseover titles or tool tips. I also wanted some of the nodes to be collapsed on page load to allow the viewer to expand them as he or she saw fit.

There are two flags in the JSON file which would have to be built into any JSON file generator. The first is color which should be obvious. The second is 'show'. If this is present, the node appears collapsed when the page loads.

{
"name": "Center",
"color":1,
"children": [
{
"name": "First_1",
"color":2,
"collapse":1,
"children": [
{
"name": "First_2",
"color":3,
"collapse":1,
"children": [
{"name": "First_3", "size": 10},
{"name": "First_3", "size": 10},
{"name": "First_3", "size": 10}
]
},
{
"name": "First_2",
"color":3,
"collapse":1,
"children": [
{"name": "First_3", "size": 15},
{"name": "First_3", "size": 15}
]
},
{
"name": "First_2",
"color":3,
"collapse":1,
"children": [
{"name": "First_3", "size": 20}
]
},
{
"name": "First_2",
"color":3,
"collapse":1,
"children": [
{"name": "First_3", "size": 25},
{"name": "First_3", "size": 25},
{"name": "First_3", "size": 25},
{"name": "First_3", "size": 25}
]
},
{
"name": "First_2",
"color":3,
"collapse":1,
"children": [
{"name": "First_3", "size": 30},
{"name": "First_3", "size": 30}
]
},{
"name": "First_2",
"color":3,
"collapse":1,
"children": [
{"name": "First_3", "size": 35},
{"name": "First_3", "size": 35}
]
},{
"name": "First_2",
"color":3,
"collapse":1,
"children": [
{"name": "First_3", "size": 40},
{"name": "First_3", "size": 40},
{"name": "First_3", "size": 40}
]
}
]
},
{
"name": "Second_1",
"color":2,
"collapse":1,
"children": [
{
"name": "Second_2",
"color":3,
"collapse":1,
"children": [
{"name": "Second_3", "size": 10},
{"name": "Second_3", "size": 10},
{"name": "Second_3", "size": 10},
{"name": "Second_3", "size": 10},
{"name": "Second_3", "size": 10},
{"name": "Second_3", "size": 10}
]
},
{
"name": "Second_2",
"color":3,
"collapse":1,
"children": [
{"name": "Second_3", "size": 15}
]
},
{
"name": "Second_2",
"color":3,
"collapse":1,
"children": []
},
{
"name": "Second_2",
"color":3,
"collapse":1,
"children": [
{"name": "Second_3", "size": 25},
{"name": "Second_3", "size": 25},
{"name": "Second_3", "size": 25}
]
},
{
"name": "Second_2",
"color":3,
"collapse":1,
"children": [
{"name": "Second_3", "size": 30},
{"name": "Second_3", "size": 30},
{"name": "Second_3", "size": 30}
]
},{
"name": "Second_2",
"color":3,
"collapse":1,
"children": [
{"name": "Second_3", "size": 35},
{"name": "Second_3", "size": 35}
]
},{
"name": "Second_2",
"color":3,
"collapse":1,
"children": [
{"name": "Second_3", "size": 40}
]
}
]
}
]
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node circle {
cursor: pointer;
}
.node text {
font: 10px sans-serif;
pointer-events: none;
text-anchor: middle;
}
line.link {
fill: none;
stroke: #9ecae1;
stroke-width: 1.5px;
}
</style>
<body>
<div id="info"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
/* BUILT ON THE WORK OF MIKE BOSTOCK - http://bl.ocks.org/mbostock/1062288 */
var width = 960,
height = 500,
root,
nodes,
links,
show_name=false;
var force = d3.layout.force()
.linkDistance(80)
.charge(-120)
.gravity(.05)
.size([width, height])
.on("tick", tick);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll(".link"),
node = svg.selectAll(".node");
d3.json("demo.json", function(error, json) {
root = json;
start();
});
function start(){
nodes = flatten(root),
links = d3.layout.tree().links(nodes);
for(var i=0; i<nodes.length; i++){
if(nodes[i].collapse){
// console.log(nodes[i].name);
toggle(nodes[i]);
}
}
restart();
}
function restart(){
nodes = flatten(root),
links = d3.layout.tree().links(nodes);
update();
}
function update() {
// Restart the force layout.
force
.nodes(nodes)
.links(links)
.start();
// Update links.
link = link.data(links, function(d) { return d.target.id; });
link.exit().remove();
link.enter().insert("line", ".node")
.attr("class", "link");
// Update nodes.
node = node.data(nodes, function(d) { return d.id; });
node.exit().remove();
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on("click", click)
.call(force.drag);
nodeEnter.append("circle")
.attr("r", function(d) { return Math.sqrt(d.size) || 4.5; })
.append("svg:title")
.text(function(d){return d._children ? "" : d.children ? "" : d.name;});
nodeEnter.transition()
.attr("r", function(d) { return d.children ? 4.5 : Math.sqrt(d.size) ; });
nodeEnter.append("text")
.attr("dy", ".35em")
.text(function(d) { return d._children ? d.name : d.children ? d.name : ""; });
node.select("circle")
.style("fill", color);
}
function tick() {
link.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; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
function color(d) {
if(!d.color){
return "#9999ff"; // terminal node
}else if(d._children){
return "#4050ff"; // collapsed node
}else{
if(d.color==1){
return "#e77471"; // central node
}else if(d.color==2){
return "#0088ff"; // first tier node
}else{
return "#66aadd" // second tier node
}
}
}
function toggle(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
}
// Toggle children on click.
function click(d){
if (d3.event.defaultPrevented) return; // ignore drag
toggle(d);
restart();
}
// Returns a list of all nodes under the root.
function flatten(root) {
var nodes = [], i = 0;
function recurse(node) {
if (node.children) node.size = node.children.reduce(function(p, v) { return p + recurse(v); }, 0);
if (!node.id) node.id = ++i;
nodes.push(node);
return node.size;
}
root.size = recurse(root);
return nodes;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment