Skip to content

Instantly share code, notes, and snippets.

@alexmacy
Last active March 8, 2017 19:50
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 alexmacy/2dc8e86b3fde00c23ccad92e390a2ef5 to your computer and use it in GitHub Desktop.
Save alexmacy/2dc8e86b3fde00c23ccad92e390a2ef5 to your computer and use it in GitHub Desktop.
Voronoi Sorting v2
license: gpl-3.0

An update to Voronoi Sorting, the polygons now get their fill color from a logarithmic scale similar to how they are sorted horizontally. This creates a more pleasing dispersion of color. Each iteration also randomly selects a color scheme from 18 of D3 4.0's sequential color schemes.

The previous versions are here: Voronoi Shuffling v1, Voronoi Shuffling v2, & Voronoi Shuffling v3

<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
body {
margin: 0;
}
path {
fill-opacity: .75;
stroke: black;
}
</style>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//d3js.org/d3-scale-chromatic.v1.min.js"></script>
</head>
<body></body>
<script>
var width = window.innerWidth,
height = window.innerHeight,
samples = [],
minMax;
var voronoi = d3.voronoi()
.size([width, height])
var schemes = ["interpolateBlues", "interpolateGreens", "interpolateGreys",
"interpolateOranges", "interpolatePurples", "interpolateReds",
"interpolateBuGn", "interpolateBuPu", "interpolateGnBu",
"interpolateOrRd", "interpolatePuBuGn","interpolatePuBu",
"interpolatePuRd", "interpolateRdPu", "interpolateYlGnBu",
"interpolateYlGn", "interpolateYlOrBr", "interpolateYlOrRd"]
// the size of the polygon will determine it's fill color - and later, it's placement along the x axis
var logForColor = d3.scaleLog().range([1,0]),
color = d3[schemes[Math.floor(d3.randomUniform(0,schemes.length)())]],
x = d3.scaleLog().range([0,width]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
getData();
var paths = svg.selectAll("path")
.data(samples)
.enter().append("path")
paths.attr("transform", "translate(0,0)")
.attr("d", function(d, i) {return getPath(samples[i].sample); })
.style("fill", function(d, i) {return color(logForColor(samples[i].area));})
function getData() {
// if loading data for the first time, generate an array of random coordinates
if (!samples.length) {
for (i=0; i<500; i++) {samples[i] = [Math.random() * width, Math.random() * height];}
//otherwise, update the x coordinate based on the polygon's size
} else {
for (i in samples) {samples[i] = [x(samples[i].area), samples[i].point[1]];}
}
// create new polygons and bind them and their calculated area to the main data array
var voronoiData = voronoi(samples).polygons();
for (i in voronoiData) {
samples[i] = {
point: samples[i],
sample: voronoiData[i],
area: d3.polygonArea(voronoiData[i]),
}
}
// set the domain of the scales by finding the min and max area values
minMax = d3.extent(samples, function(d) {return d.area;});
logForColor.domain([minMax[1],minMax[0]])
x.domain(minMax);
}
// create the paths for the polygons
function getPath(points) {
// redrawing the polygons was causing some unwanted effects when transitioning a
// polygon to a new shape that has more sides. this was because the new points
// would be drawn off screen before transitioning into place.
// my solution was to default each polygon to having more points than (hopefully)
// necessary, with the extra points placed on top of each other. this is potentially
// not performant as it creates multiple points in the same location.
var thisPath = "M" + points[0];
for (n=1; n<15; n++) {
thisPath += "L" + (n < points.length ? points[n] : points[0])
}
return thisPath;
}
var sortShapes = function() {
color = d3[schemes[Math.floor(d3.randomUniform(0,schemes.length)())]]
// slide the polygons into order of size along the x-axis according to the log scale
paths.transition().duration(2000)
.attr("transform", function(d, i) {
return "translate(" + (-samples[i].point[0] + x(samples[i].area)) + ",0)"
})
// re-calculate the voronoi polygons and their sizes based on the new positions
getData();
// transition to the new sizes and shapes
paths.transition().delay(2000).duration(2000)
.attr("transform", "translate(0,0)")
.attr("d", function(d, i) {return getPath(samples[i].sample);})
.style("fill", function(d, i) {return color(logForColor(samples[i].area));})
// loop the sorting process
d3.timeout(sortShapes, 6000);
}
d3.timeout(sortShapes, 2000);
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment