|
<!DOCTYPE html> |
|
<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> |