|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<body> |
|
<script src="//d3js.org/d3.v4.js"></script> |
|
<script> |
|
|
|
var width = 400 * 1.618, |
|
height = 400, |
|
τ = 2 * Math.PI; |
|
|
|
var nodes = d3.range(200).map(function () { |
|
return { |
|
x: width/2 + (-0.5 + Math.random())*150, |
|
y: height/2 + (-0.5 + Math.random())*150 |
|
}; |
|
}); |
|
|
|
var force = d3.forceSimulation() |
|
.nodes(nodes.slice()) |
|
.force("collide", d3.forceCollide(4)) |
|
.force("brownian", function () { |
|
for (var i = 0, n = nodes.length, node, k = 0.1; i < n; ++i) { |
|
if (Math.random() > 1 - k) { |
|
var angle = Math.random() * τ; |
|
node = nodes[i]; |
|
node.vx += 0.2 * Math.cos(angle); |
|
node.vy += 0.2 * Math.sin(angle); |
|
|
|
} |
|
} |
|
}) |
|
.force("bounce-on-container", function () { |
|
for (var i = 0, n = nodes.length, node; i < n; ++i) { |
|
node = nodes[i]; |
|
var dx = node.x / width - 1 / 2, |
|
dy = node.y / height - 1 / 2; |
|
if (Math.random() < Math.pow(dx/0.4, 40)) { |
|
node.vx = - dx * 3; |
|
} |
|
if (Math.random() < Math.pow(dy/0.4, 40)) { |
|
node.vy = - dy * 3; |
|
} |
|
|
|
} |
|
}) |
|
.on("tick", ticked) |
|
.alphaDecay(0) |
|
.velocityDecay(0.001); |
|
|
|
|
|
|
|
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); |
|
|
|
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); |
|
}); |
|
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); |
|
//var links = []; |
|
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; |
|
context.moveTo(link.source.x, link.source.y); |
|
context.lineTo(link.target.x, link.target.y); |
|
} |
|
context.strokeStyle = "#eee"; |
|
context.lineWidth = 8; |
|
context.stroke(); |
|
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; |
|
context.moveTo(link.source.x, link.source.y); |
|
context.lineTo(link.target.x, link.target.y); |
|
} |
|
context.strokeStyle = "#000"; |
|
context.lineWidth = 0.5; |
|
context.stroke(); |
|
|
|
context.strokeStyle = "#fcc"; |
|
context.beginPath(); |
|
context.moveTo(width * 0.1, height * 0.1); |
|
context.lineTo(width * 0.1, height * 0.9); |
|
context.lineTo(width * 0.9, height * 0.9); |
|
context.lineTo(width * 0.9, height * 0.1); |
|
context.lineTo(width * 0.1, height * 0.1); |
|
context.lineWidth = 6; |
|
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, 1, 0, τ); |
|
} |
|
context.lineWidth = 1; |
|
context.strokeStyle = "#fff"; |
|
context.stroke(); |
|
context.fillStyle = "yellow"; |
|
context.fill(); |
|
|
|
} |
|
|
|
</script> |