|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<canvas width="960" height="500"></canvas> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
|
|
var canvas = d3.select("canvas").on("touchmove mousemove", moved).node(), |
|
context = canvas.getContext("2d"), |
|
width = canvas.width, |
|
height = canvas.height; |
|
|
|
|
|
var w = width * 0.6, |
|
h = height * 0.7; |
|
|
|
|
|
var sites = d3.range(300) |
|
.map(function(d) { return [ |
|
width / 2 + w * (0.5 - Math.random()) |
|
, height / 2 + h * (0.5 - Math.random()) ]; }); |
|
|
|
var voronoi = d3.voronoi() |
|
.extent([[-1, -1], [width + 1, height + 1]]); |
|
|
|
|
|
redraw(); |
|
|
|
function moved() { |
|
var d = d3.mouse(this); |
|
if (d[0] < width/2 - w/2) d[0] += w; |
|
if (d[0] > width/2 + w/2) d[0] -= w; |
|
sites[0] = d; |
|
redraw(); |
|
} |
|
|
|
|
|
function redraw() { |
|
var sites2 = d3.merge([sites, |
|
sites.slice().map(function(d){ |
|
d = [d[0]-w, d[1]]; |
|
d.shadow = true; |
|
return d; |
|
}), |
|
sites.slice().map(function(d){ |
|
d = [d[0]+w, d[1]]; |
|
d.shadow = true; |
|
return d; |
|
}), |
|
]); |
|
|
|
var diagram = voronoi(sites2), |
|
links = diagram.links().filter(function(l){ |
|
return !l.source.shadow || !l.target.shadow; |
|
}); |
|
// remove spurious cells |
|
diagram.cells = diagram.cells.slice(0,sites.length); |
|
var polygons = diagram.polygons(); |
|
|
|
context.clearRect(0, 0, width, height); |
|
context.strokeStyle = "#00b703"; |
|
context.lineWidth = 3; |
|
context.beginPath(); |
|
context.moveTo(width/2-w/2, 0); |
|
context.lineTo(width/2-w/2, height); |
|
context.stroke(); |
|
context.beginPath(); |
|
context.moveTo(width/2+w/2, 0); |
|
context.lineTo(width/2+w/2, height); |
|
context.stroke(); |
|
context.lineWidth = 1; |
|
|
|
|
|
|
|
context.beginPath(); |
|
drawCell(polygons[0]); |
|
context.fillStyle = "#f00"; |
|
context.fill(); |
|
|
|
context.beginPath(); |
|
for (var i = 0, n = polygons.length; i < n; ++i) drawCell(polygons[i]); |
|
context.strokeStyle = "#000"; |
|
context.stroke(); |
|
|
|
context.beginPath(); |
|
for (var i = 0, n = links.length; i < n; ++i) drawLink(links[i]); |
|
context.strokeStyle = "rgba(0,0,0,0.2)"; |
|
context.stroke(); |
|
|
|
context.beginPath(); |
|
drawSite(sites[0]); |
|
context.fillStyle = "#fff"; |
|
context.fill(); |
|
|
|
context.beginPath(); |
|
for (var i = 1, n = sites.length; i < n; ++i) drawSite(sites[i]); |
|
context.fillStyle = "#000"; |
|
context.fill(); |
|
context.strokeStyle = "#fff"; |
|
context.stroke(); |
|
context.beginPath(); |
|
for (var i = sites.length, n = sites2.length; i < n; ++i) drawSite(sites2[i]); |
|
context.fillStyle = "#ccc"; |
|
context.fill(); |
|
context.strokeStyle = "#fff"; |
|
context.stroke(); |
|
|
|
} |
|
|
|
function drawSite(site) { |
|
context.moveTo(site[0] + 2.5, site[1]); |
|
context.arc(site[0], site[1], 2.5, 0, 2 * Math.PI, false); |
|
} |
|
|
|
function drawLink(link) { |
|
context.moveTo(link.source[0], link.source[1]); |
|
context.lineTo(link.target[0], link.target[1]); |
|
} |
|
|
|
function drawCell(cell) { |
|
if (!cell) return false; |
|
context.moveTo(cell[0][0], cell[0][1]); |
|
for (var j = 1, m = cell.length; j < m; ++j) { |
|
context.lineTo(cell[j][0], cell[j][1]); |
|
} |
|
context.closePath(); |
|
return true; |
|
} |
|
|
|
</script> |