Group data points around the largest points.
The recipe is as follows:
sort data points according to their sizes (here size = d[3]
)
use top 10% points as Voronoi sites
bin all data points according to their Voronoi cell, using voronoi.find(). The binning is rendered by using the Voronoi sites's color.
See also the static version — much less code to parse!
Original work by Philippe Rivière for d3-voronoi (issue 17).
forked from Fil's block: Voronoi binning (animated)
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {margin:0;padding: 0}
</style>
<svg width="1050" height="1500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width")-80,
height = +svg.attr("height") - 260;
svg
.append('text')
.text('@recifs visionscarto.net')
.attr('transform', 'translate(350,210)')
.attr('fill','black')
.attr('font-size','50')
.attr('font-family','sans-serif')
svg = svg.append('g')
.attr('transform', 'translate(40,260)')
var warp = 2;
d3.interval(algo, warp * 9000);
algo();
function algo() {
var color = d3.scaleOrdinal().range(d3.schemeCategory20);
var n = 3000;
var data = d3.range(n)
.map(function(d,i) { return [Math.random() * width, Math.random() * height, color(i), Math.random()]; });
var polygon = svg.append("g")
.attr("class", "polygons");
var voronoi = d3.voronoi()
.size([width, height]);
var sites = data.sort(function(a,b) {
return d3.descending(a[3], b[3]);
})
.slice(0,n/10);
sites.forEach(function(d) {
d[4] = true;
})
var dots = svg
.append('g')
.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('r', function(d) {return 20 * d[3] * d[3];})
.attr('transform', function(d) { return 'translate('+ [ d[0], d[1] ] +')'; })
.attr('fill', 'white')
.attr('stroke', '#444')
.attr('stroke-width', 1)
.attr('opacity', 1)
;
var diagram = voronoi(sites);
diagram.find = function (x, y, radius){
// optimization: start from most recent result
var i, next = diagram.find.found || Math.floor(Math.random() * diagram.cells.length);
var cell = diagram.cells[next] || diagram.cells[next=0];
var dx = x - cell.site[0],
dy = y - cell.site[1],
dist = dx*dx + dy*dy;
do {
cell = diagram.cells[i=next];
next = null;
cell.halfedges.forEach(function(e) {
var edge = diagram.edges[e];
var ea = edge.left;
if (ea === cell.site || !ea) {
ea = edge.right;
}
if (ea){
var dx = x - ea[0],
dy = y - ea[1],
ndist = dx*dx + dy*dy;
if (ndist < dist){
dist = ndist;
next = ea.index;
return;
}
}
});
} while (next !== null);
diagram.find.found = i;
if (!radius || dist < radius * radius) return cell.site;
};
polygon.selectAll("path")
.data(diagram.polygons())
.enter().append("path")
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; })
.attr("fill", function(d,i) { return sites[i][2] })
.attr("fill-opacity", 0.001)
.attr('stroke', 'white')
.transition()
.delay(warp * 2000)
.duration(warp * 2000)
.attr("stroke", '#777')
.attr("fill-opacity", 0.2)
dots.transition()
.duration(warp * 2000)
.attr('fill', function(d) { return d[4] ? d[2] : 'white'; })
.attr('stroke-width', function(d) { return d[4] ? 4 : 0.5; })
;
dots
.transition()
.delay(warp * 1000)
.attr('opacity', function(d) { return d[4] ? 1 : 0.4; });
dots
.transition()
.delay(warp * 4000)
.duration(warp * 2000)
.attr('fill', function(d) {
var found = diagram.find(d[0],d[1]);
return sites[found.index][2];
})
.attr('opacity', 1);
polygon
.attr('opacity', 1)
.transition().delay(warp * 6000)
.duration(warp * 2000)
.attr('opacity', 0.1)
.remove();
svg.selectAll('g')
.attr('opacity', 1)
.transition()
.delay(warp * 8000 + 900)
.duration(100)
.attr('opacity', 0)
.remove();
}
</script>
https://d3js.org/d3.v4.min.js