Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active February 8, 2017 17:00
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 Fil/be5298a255c037d119752604f25e83c4 to your computer and use it in GitHub Desktop.
Save Fil/be5298a255c037d119752604f25e83c4 to your computer and use it in GitHub Desktop.
Urquhart Force Mesh, with a responsive center [UNLISTED]
license: gpl-3.0

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

<!DOCTYPE html>
<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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment