This randomly-generated tree is positioned using a force simulation.
forked from mbostock's block: Force-Directed Tree
xxxxxxxxxx
<meta charset="utf-8">
<canvas width="960" height="300"></canvas>
<script src="https://d3js.org/d3.v4.0.0-alpha.49.min.js"></script>
<script>
var nodes = d3.range(400).map(function(i) {
return {
index: i
};
});
var links = d3.range(nodes.length - 1).map(function(i) {
return {
source: Math.floor(Math.sqrt(i)),
target: i + 1,
};
});
var f = function(links) {
var angles = new Array(links.length),
anglef = function(link) {
return Math.atan2(link.target.y - link.source.y, link.target.x - link.source.x);
};
function initialize() {
for (var i=0, n=links.length, link; i<n; i++) {
link = links[i];
angles[i] = anglef(link);
}
}
function force(alpha){
var sum = 0;
for (var i = 0, n = links.length; i < n; ++i) {
var link = links[i],
angle = anglef(link);
dl = Math.sin(angle-angles[i]);
sum += dl*dl;
dl *= Math.sqrt(alpha);
link.source.vx -= Math.sin(angle) * dl;
link.source.vy += Math.cos(angle) * dl;
link.target.vx += Math.sin(angle) * dl;
link.target.vy -= Math.cos(angle) * dl;
}
console.log('sum', sum);
}
force.initialize = initialize;
return force;
};
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody())
.force("link", d3.forceLink(links).distance(20).strength(1))
.force("x", d3.forceX())
.force("y", d3.forceY())
.force("f", f(links))
.on("tick", ticked);
var canvas = document.querySelector("canvas"),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height;
d3.select(canvas)
.call(d3.drag()
.container(canvas)
.subject(dragsubject)
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function ticked() {
context.clearRect(0, 0, width, height);
context.save();
context.translate(width / 2, height / 2);
context.beginPath();
links.forEach(drawLink);
context.strokeStyle = "#aaa";
context.stroke();
context.beginPath();
nodes.forEach(drawNode);
context.fill();
context.strokeStyle = "#fff";
context.stroke();
context.restore();
}
function dragsubject() {
return simulation.find(d3.event.x - width / 2, d3.event.y - height / 2);
}
function dragstarted() {
if (!d3.event.active) simulation.alphaTarget(0.3).restart()
simulation.fix(d3.event.subject);
}
function dragged() {
simulation.fix(d3.event.subject, d3.event.x, d3.event.y);
}
function dragended() {
if (!d3.event.active) simulation.alphaTarget(0);
simulation.unfix(d3.event.subject);
}
function drawLink(d) {
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
}
function drawNode(d) {
context.moveTo(d.x + 3, d.y);
context.arc(d.x, d.y, 3, 0, 2 * Math.PI);
}
</script>
https://d3js.org/d3.v4.0.0-alpha.49.min.js