Some force-directed points, with the Urquhart graph overlaid. Thanks @enjalot for the idea and time wasted :)
In another variant I toss in some Brownian motion.
Forked from erlenstar's block: Delaunay Force Mesh II
license: gpl-3.0 |
Some force-directed points, with the Urquhart graph overlaid. Thanks @enjalot for the idea and time wasted :)
In another variant I toss in some Brownian motion.
Forked from erlenstar's block: Delaunay Force Mesh II
<!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: Math.random() * width, | |
y: Math.random() * height | |
}; | |
}); | |
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(width / 2, height / 2)) | |
.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]; | |
nodes[0].fy = p1[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); | |
context.clearRect(0, 0, width, height); | |
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(); | |
} | |
</script> |