forked from mbostock's block: Force-Directed Tree
xxxxxxxxxx
<meta charset="utf-8">
<style>
circle {
stroke-width: 1.5px;
}
line {
stroke: #999;
stroke-width: 1.5px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500,
radius = 6;
var fill = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("graph.json", function(error, json) {
if (error) throw error;
var link = svg.selectAll("line")
.data(json.links)
.enter().append("line");
var node = svg.selectAll("circle")
.data(json.nodes)
.enter().append("circle")
.attr("r", radius - .75)
.style("fill", function(d) { return fill(d.group); })
.style("stroke", function(d) { return d3.rgb(fill(d.group)).darker(); })
.call(force.drag);
force
.nodes(json.nodes)
.links(json.links)
.on("tick", tick)
.start();
function tick() {
// Apply the constraints:
//
force.nodes().forEach(function(d) {
if (!d.fixed) {
var r = circle_radius(d) + 4, dx, dy, ly = 30;
// #1: constraint all nodes to the visible screen:
//d.x = Math.min(width - r, Math.max(r, d.x));
//d.y = Math.min(height - r, Math.max(r, d.y));
// #1.0: hierarchy: same level nodes have to remain with a 1 LY band vertically:
if (d.children || d._children) {
var py = 0;
if (d.parent) {
py = d.parent.y;
}
d.py = d.y = py + d.depth * ly + r;
}
// #1a: constraint all nodes to the visible screen: links
dx = Math.min(0, width - r - d.x) + Math.max(0, r - d.x);
dy = Math.min(0, height - r - d.y) + Math.max(0, r - d.y);
d.x += 2 * Math.max(-ly, Math.min(ly, dx));
d.y += 2 * Math.max(-ly, Math.min(ly, dy));
// #1b: constraint all nodes to the visible screen: charges ('repulse')
dx = Math.min(0, width - r - d.px) + Math.max(0, r - d.px);
dy = Math.min(0, height - r - d.py) + Math.max(0, r - d.py);
d.px += 2 * Math.max(-ly, Math.min(ly, dx));
d.py += 2 * Math.max(-ly, Math.min(ly, dy));
// #2: hierarchy means childs must be BELOW parents in Y direction:
if (d.parent) {
d.y = Math.max(d.y, d.parent.y + ly);
d.py = Math.max(d.py, d.parent.py + ly);
}
}
});
}
function tick(e) {
var k = 6 * e.alpha;
force.nodes().forEach(function(d) {
if (!d.fixed) {
var r = circle_radius(d) + 4, dx, dy, ly = 30;
// Push sources up and targets down to form a weak tree.
link
.each(function(d) { d.source.y -= k, d.target.y += k; })
.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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
});
}
</script>
https://d3js.org/d3.v3.min.js