Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>d3</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.js" charset="utf-8"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.js" charset="utf-8"></script>
<style type="text/css">
.node rect {
fill: #ecf0f1;
stroke: white;
stroke-width: 1px;
cursor: pointer;
}
.node {
font: 10px monospace;
}
.link text {
font: 12px monospace;
stroke: none;
fill: black;
}
.link {
fill: none;
stroke: #7f8c8d;
stroke-width: 1px;
}
</style>
</head>
<body>
<div id="nodes" style="position: absolute; top: 0; bottom: 0; left: 0; right: 0"/>
<script type="text/javascript">
var width = document.getElementById('nodes').clientWidth;
var height = document.getElementById('nodes').clientHeight;
var cluster = d3.layout.cluster()
.size([width - 70, height - 70]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.x, d.y] });
var svg = d3.select('#nodes').append('svg')
.attr('height', height)
.attr('width', width)
.append('g')
.attr('transform', 'translate(0, 30)');
function parse_nodes(r, n, used_children) {
if(_.isUndefined(used_children)) {
used_children = {}
}
var node = n.nodes[r]
if(_.has(node, 'original_children')) {
node.children = _.reduce(node.original_children, function(agg, c) {
if(_.isUndefined(used_children[c])) {
used_children[c] = true;
return agg.concat(parse_nodes(c, n, used_children));
}
return agg;
}, []);
}
return n.nodes[r];
}
function parse_links(r, n, agg) {
if(_.isUndefined(agg)) {
agg = [];
}
if(_.has(n.nodes[r], 'original_children')) {
return _.flatten(agg.concat(_.map(n.nodes[r].original_children, function(c) {
return parse_links(c, n, agg).concat({source: n.nodes[r], target: n.nodes[c]});
})));
}
return agg;
}
function links_in_root_paths(node, n, agg) {
if(_.isUndefined(agg)) {
agg = [];
}
if(!_.isNull(node, 'parents')) {
return _.flatten(agg.concat(_.map(node.parents, function(p) {
return links_in_root_paths(n.nodes[p], n, agg).concat({source_id: p, target_id: node.id});
})));
}
return agg;
}
function on_mouseover(n, node) {
svg.selectAll('.link')
.attr('style', function(link) {
var root_paths = _.indexBy(links_in_root_paths(node, n), function(l) { return l.source_id + '|' + l.target_id} )
if(_.has(root_paths, link.source.id + '|' + link.target.id)) {
return 'stroke: #2ecc71;';
}
else {
return 'opacity: 0.1;';
}
})
d3.event.stopPropagation();
}
function on_mouseout(node) {
svg.selectAll('.link')
.attr('style', 'stroke: #7f8c8d; opacity: 1');
}
function on_click(n, node) {
_.forEach(node.parents || [], function(p) {
n.nodes[p].original_children = _.without(n.nodes[p].original_children, node.id);
_.forEach(node.original_children || [], function (c) {
n.nodes[p].original_children = n.nodes[p].original_children.concat(c);
n.nodes[c].parents = _.without(n.nodes[c].parents, node.id).concat(p);
});
});
delete n.nodes[node.id];
render(n);
}
function render(n) {
var nodes = cluster.nodes(parse_nodes(n.root, n));
var links = parse_links(n.root, n);
svg.selectAll('.link').remove();
var link = svg.selectAll('.link')
.data(links)
.enter().append('g')
.attr('class', 'link')
link.append('path')
.attr('d', diagonal)
link.append('text')
.attr('x', function(d) { return (d.source.x + d.target.x) / 2; })
.attr('y', function(d) { return (d.source.y + d.target.y) / 2; })
.attr('text-anchor', 'middle')
.text(function(d) { return n.links[d.source.id + '|' + d.target.id] || '' })
var node = svg.selectAll('.node')
.data(nodes, function(d) { return d.id })
node.transition()
.duration(200)
.attr('transform', function(d) { return 'translate(' + d.x + ', ' + d.y + ')'; });
new_node = node.enter().append('g')
.attr('class', 'node')
.attr('transform', function(d) { return 'translate(' + d.x + ', ' + d.y + ')'; });
new_node.append('rect')
.attr('width', 80)
.attr('height', 20)
.attr('transform', 'translate(-40, -15)')
.on('mouseover', _.partial(on_mouseover, n))
.on('mouseout', on_mouseout)
.on('click', _.partial(on_click, n));
new_node.append('text')
.attr('dx', 0)
.attr('dy', -1)
.attr("pointer-events", "none")
.style('text-anchor', 'middle')
.text(function(d) { return d.name; });
node.exit()
.transition()
.duration(100)
.style('opacity', 0)
.remove()
svg.selectAll('.link, .node')
.sort(function(a, b) {
if(_.has(a, 'parents') && !_.has(b, 'parents')) {
return 1;
}
else if(_.has(b, 'parents') && !_.has(a, 'parents')) {
return -1;
}
else {
return 0;
}
})
}
d3.json("multi.json", function(error, n) { render(n); });
</script>
</body>
</html>
{
"nodes": {
"0": {"id": "0", "name": "sharma", "parents": null, "original_children": ["1", "2", "3", "9", "5"]},
"1": {"id": "1", "name": "ondra", "parents": ["0"], "original_children": ["3", "5"]},
"2": {"id": "2", "name": "woods", "parents": ["0"], "original_children": ["4"]},
"3": {"id": "3", "name": "caldwell", "parents": ["0", "1"], "original_children": ["6", "7", "8"]},
"4": {"id": "4", "name": "jorgensen", "parents": ["2"]},
"5": {"id": "5", "name": "bisharat", "parents": ["1", "0"]},
"6": {"id": "6", "name": "classen", "parents": ["3", "8"]},
"7": {"id": "7", "name": "graham", "parents": ["3"]},
"8": {"id": "8", "name": "robinson", "parents": ["3"], "original_children": ["10", "6"]},
"9": {"id": "9", "name": "siegriest", "parents": ["0"], "original_children": ["11"]},
"10": {"id": "10", "name": "cardwell", "parents": ["8"]},
"11": {"id": "11", "name": "andrada", "parents": ["9"]}
},
"root": "0",
"links": {
"0|1": "76%",
"0|2": "34%",
"0|3": "7%",
"0|5": "1%",
"0|9": "17%",
"1|3": "40%",
"1|5": "60%",
"2|4": "100%",
"3|6": "22%",
"3|7": "50%",
"3|8": "28%",
"8|10": "7%",
"8|6": "93%",
"9|10": "28%",
"9|6": "72%",
"9|11": "50%"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment