Bounded Voronoi Tesselation using the algorithm described in xlr8r.info
See the variant: Circular Bounded Voronoi Tesselation.
Based on mbostock's block: Voronoi Tessellation
Author: Philippe Rivière, August 2016
license: gpl-3.0 |
Bounded Voronoi Tesselation using the algorithm described in xlr8r.info
See the variant: Circular Bounded Voronoi Tesselation.
Based on mbostock's block: Voronoi Tessellation
Author: Philippe Rivière, August 2016
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
.links { | |
stroke: #000; | |
stroke-opacity: 0.2; | |
} | |
.polygons { | |
stroke: #000; | |
} | |
.polygons :first-child { | |
fill: #ff3d5a; | |
} | |
.sites { | |
fill: #666; | |
stroke: #fff; | |
} | |
.sites :first-child { | |
fill: #fff; | |
} | |
.convex-hull { | |
fill: none; | |
stroke: #fec1f4; | |
stroke-width: 2px; | |
} | |
</style> | |
<svg width="960" height="500"></svg> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> | |
<script> | |
var svg = d3.select("svg").on("touchmove mousemove", moved), | |
width = +svg.attr("width"), | |
height = +svg.attr("height"), | |
margin = 0.2; // 0 ≤ m < 0.5] | |
var color = d3.scaleOrdinal(d3.schemePastel1); | |
var sites = d3.range(100) | |
.map(function(d) { return [ | |
width * (margin + Math.random() * (1 - 2 * margin)), | |
height * (margin + Math.random() * (1 - 2 * margin)) | |
]; }); | |
var voronoi = d3.voronoi() | |
.extent([[-1, -1], [width + 1, height + 1]]); | |
var polygon = svg.append("g") | |
.attr("class", "polygons"); | |
var convexhull = svg.append('path') | |
.attr('class', 'convex-hull'); | |
var link = svg.append("g") | |
.attr("class", "links"); | |
var site = svg.append("g") | |
.attr("class", "sites"); | |
redraw(); | |
function moved() { | |
sites[0] = d3.mouse(this); | |
redraw(); | |
} | |
function redraw() { | |
var links = voronoi.links(sites), | |
ext = d3.mean(links, function(l) { | |
var dx = l.source[0] - l.target[0], | |
dy = l.source[1] - l.target[1]; | |
return Math.sqrt(dx*dx + dy*dy); | |
}); | |
var convex = d3.polygonHull(sites); | |
convex.centroid = d3.polygonCentroid(convex); | |
convex = convex.map(function(p){ | |
var dx = p[0] - convex.centroid[0], | |
dy = p[1] - convex.centroid[1], | |
angle = Math.atan2(dy, dx); | |
return [p[0] + Math.cos(angle) * ext, p[1] + Math.sin(angle) * ext]; | |
}); | |
var sites2 = sites.slice(); // clone | |
for (var i = 0; i < convex.length; i++) { | |
var n = convex[i], m = convex[i+1]||convex[0]; | |
var dx = n[0] - m[0], | |
dy = n[1] - m[1], | |
dist = Math.sqrt(dx * dx + dy * dy); | |
var pts = Math.ceil(dist / 2 / ext); | |
for(var j=0; j <= pts; j++) { | |
var p = [m[0] + dx *j / pts, m[1] + dy * j / pts]; | |
p.artificial = 1; | |
sites2.push(p); | |
} | |
} | |
var diagram = voronoi(sites2); | |
var p = polygon.selectAll("path") | |
.data(diagram.polygons()); | |
p.enter().append("path").merge(p).call(redrawPolygon) | |
p.exit().remove(); | |
var l = link | |
.selectAll("line") | |
.data(diagram.links().filter(function(l){ | |
return !l.source.artificial && !l.target.artificial; | |
})); | |
l .enter() | |
.append("line") | |
.merge(l) | |
.call(redrawLink) | |
.exit() | |
.remove(); | |
var s = site | |
.selectAll("circle") | |
.data(sites); | |
s.enter() | |
.append("circle") | |
.attr("r", 2.5) | |
.merge(s) | |
.call(redrawSite); | |
convexhull.attr('d', "M" + convex.join("L") + "Z"); | |
} | |
function redrawPolygon(polygon) { | |
polygon | |
.attr("fill", function(d, i) { | |
return i < sites.length ? color(i) : '#faf6f1'; | |
}) | |
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; }); | |
} | |
function redrawLink(link) { | |
link | |
.attr("x1", function(d) { return d.source[0]; }) | |
.attr("y1", function(d) { return d.source[1]; }) | |
.attr("x2", function(d) { return d.target[0]; }) | |
.attr("y2", function(d) { return d.target[1]; }); | |
} | |
function redrawSite(site) { | |
site | |
.attr("cx", function(d) { return d[0]; }) | |
.attr("cy", function(d) { return d[1]; }); | |
} | |
</script> |
�PNG | |