To have a forceSimulation responsively follow the center of the window when it is resized is not easy, as the forceCenter
is only initialized at the beginning. Here we just move the canvas origin instead. (Answering a question by @ashnur on d3’s Slack.)
forked from Fil's block: Urquhart Force Mesh
xxxxxxxxxx
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.js"></script>
<script>
var width = 960,
height = 500,
τ = 2 * Math.PI,
maxLength = 10000,
maxLength2 = maxLength * maxLength;
var nodes = d3.range(200).map(function() {
return {
x: (0.5 - Math.random()) * width,
y: (0.5 - Math.random()) * height
};
});
function windowCenter(){
return [window.innerWidth/2, window.innerHeight/2];
}
var force = d3.forceSimulation()
.nodes(nodes.slice())
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(function(d,i){
return i ? -25 : -1000
}))
.force("x", d3.forceX())
.force("y", d3.forceY())
.force("center", d3.forceCenter(0, 0))
.on("tick", ticked);
var voronoi = d3.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; });
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height)
.on("ontouchstart" in document ? "touchmove" : "mousemove", moved);
var context = canvas.node().getContext("2d");
function moved() {
var p1 = d3.mouse(this);
nodes[0].fx = p1[0] - windowCenter()[0];
nodes[0].fy = p1[1] - windowCenter()[1];
force.alpha(0.1).restart();
}
function urquhart(diagram){
var urquhart = d3.map();
diagram.links()
.forEach(function(link) {
var v = d3.extent([link.source.index, link.target.index]);
urquhart.set(v, link);
});
urquhart._remove = [];
diagram.triangles()
.forEach(function(t) {
var l = 0, length = 0, i="bleh", v;
for (var j=0; j<3; j++) {
var a = t[j], b = t[(j+1)%3];
v = d3.extent([a.index, b.index]);
length = (a.x-b.x) * (a.x-b.x) + (a.y-b.y) * (a.y-b.y);
if (length > l) {
l = length;
i = v;
}
}
urquhart._remove.push(i);
});
//console.log(JSON.stringify(urquhart._remove))
urquhart._remove.forEach(function(i) {
if (urquhart.has(i)) urquhart.remove(i);
});
return urquhart.values();
}
function ticked() {
var diagram = voronoi(nodes);
//var links = diagram.links();
var links = urquhart(diagram);
let ctr = windowCenter();
canvas .attr('width', ctr[0]*2) .attr('height', ctr[1]*2)
// setting width/height clear the canvas, so…
//context.clearRect(0, 0, ctr[0]*2, ctr[1]*2);
context.translate(ctr[0], ctr[1]);
context.beginPath();
for (var i = 0, n = links.length; i < n; ++i) {
var link = links[i],
dx = link.source.x - link.target.x,
dy = link.source.y - link.target.y;
if (dx * dx + dy * dy < maxLength2) {
context.moveTo(link.source.x, link.source.y);
context.lineTo(link.target.x, link.target.y);
}
}
context.lineWidth = 1;
context.strokeStyle = "#bbb";
context.stroke();
context.beginPath();
for (var i = 0, n = nodes.length; i < n; ++i) {
var node = nodes[i];
context.moveTo(node.x, node.y);
context.arc(node.x, node.y, 2, 0, τ);
}
context.lineWidth = 3;
context.strokeStyle = "#fff";
context.stroke();
context.fillStyle = "#000";
context.fill();
// draw the center
context.beginPath();
context.moveTo(0,0);
context.arc(0, 0, 4, 0, τ);
context.fillStyle = "red";
context.fill();
context.translate(-ctr[0], -ctr[1]);
}
</script>
https://d3js.org/d3.v4.js