Skip to content

Instantly share code, notes, and snippets.

@steveharoz
Forked from mbostock/.block
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steveharoz/440963591fcff26cb10f to your computer and use it in GitHub Desktop.
Save steveharoz/440963591fcff26cb10f to your computer and use it in GitHub Desktop.
Colorful Rotating Voronoi

I'm just adding a little color to Mike Bostock's demo.

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 {
stroke: #000;
stroke-width: 1.5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var points = [];
var colors = [];
// Color Brewer "Set1"
var set1 = ["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#ffff33","#a65628","#f781bf","#999999"];
var bounds = d3.geom.polygon([
[-width / 2, -height / 2],
[-width / 2, +height / 2],
[+width / 2, +height / 2],
[+width / 2, -height / 2]
]);
circle(-750, 120, 20, 1, -.05, [set1[1]]);
circle(-750, -120, 20, 1, -.05, [set1[2]]);
circle(0, 0, 120, 96, -.001, [set1[8]]);
circle(0, 0, 30, 10, .03, [set1[2]]);
circle(0, 0, 60, 3, -.05, [set1[1]]);
circle(0, 0, 15, 4, -.02, [set1[0]]);
circle(0, 0, 0, 1, -.02, ["black"]);
circle(240, -120, 80, 4, -.02, [set1[1], set1[2]]);
circle(240, -120, 0, 1, -.02, [set1[0]]);
circle(280, +120, 40, 8, .02, [set1[1], set1[2]]);
circle(280, +120, 20, 8, -.02, [set1[2], set1[1]]);
circle(280, +120, 0, 1, .02, [set1[0]]);
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])); });
path.attr("fill", function(point, i) { return colors[i]; });
});
function circle(cx, cy, r, n, δθ, colorSet) {
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() {
θ += δθ;
point[0] = cx + Math.cos(θ) * r;
point[1] = cy + Math.sin(θ) * r;
});
points.push(point);
colors.push(colorSet[i%colorSet.length]);
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