Skip to content

Instantly share code, notes, and snippets.

@bouriate
Forked from mbostock/.block
Created March 7, 2016 07:02
Show Gist options
  • Save bouriate/12f87b42c7655ffc8407 to your computer and use it in GitHub Desktop.
Save bouriate/12f87b42c7655ffc8407 to your computer and use it in GitHub Desktop.
Rotating Voronoi
license: gpl-3.0

Mario Klingemann has made some beautiful Voronoi diagrams. This is my attempt to recreate them using D3. To achieve the curved cells, each side of the Voronoi polygon is subdivided into three equal-length segments and then joined using the "basis-closed" line interpolator. There are some discontinuities in the animation when the sides of a polygon approach zero which could likely be avoided by adding a minimum-length threshold to the subdivision.

If you’d like to see other instances of this pattern, Mario describes the algorithm on Flickr.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #000;
}
path {
fill: #fff;
stroke: #000;
stroke-width: 1.5px;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var start = Date.now(),
points = [];
var bounds = d3.geom.polygon([
[-width / 2, -height / 2],
[-width / 2, +height / 2],
[+width / 2, +height / 2],
[+width / 2, -height / 2]
]);
circle(0, 0, 120, 96, -.001);
circle(0, 0, 30, 10, .03);
circle(0, 0, 60, 3, -.05);
circle(0, 0, 15, 4, -.02);
circle(0, 0, 0, 1, -.02);
circle(240, -120, 80, 4, -.02);
circle(240, -120, 0, 1, -.02);
circle(280, +120, 40, 8, .02);
circle(280, +120, 20, 8, -.02);
circle(280, +120, 0, 1, .02);
var line = d3.svg.line()
.interpolate("basis-closed");
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var path = svg.selectAll("path")
.data(points)
.enter().append("path");
d3.timer(function() {
var voronoi = d3.geom.voronoi(points).map(function(cell) { return bounds.clip(cell); });
path.attr("d", function(point, i) { return line(resample(voronoi[i])); });
});
function circle(cx, cy, r, n, δθ) {
d3.range(1e-6, 2 * Math.PI, 2 * Math.PI / n).map(function(θ, i) {
var point = [cx + Math.cos(θ) * r, cy + Math.sin(θ) * r];
d3.timer(function(elapsed) {
var angle = θ + δθ * elapsed / 60;
point[0] = cx + Math.cos(angle) * r;
point[1] = cy + Math.sin(angle) * r;
}, 0, start);
points.push(point);
return point;
});
}
function resample(points) {
var i = -1,
n = points.length,
p0 = points[n - 1], x0 = p0[0], y0 = p0[1], p1, x1, y1,
points2 = [];
while (++i < n) {
p1 = points[i], x1 = p1[0], y1 = p1[1];
points2.push(
[(x0 * 2 + x1) / 3, (y0 * 2 + y1) / 3],
[(x0 + x1 * 2) / 3, (y0 + y1 * 2) / 3],
p1
);
p0 = p1, x0 = x1, y0 = y1;
}
return points2;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment