block by alexmacy 05bce18b77003ce39217a7ba33b52ac8

Voronoi Sorting v2 update for d3.unconf badge

Full Screen

Made some changes for using this as a badge for d3.unconf. The background color is now black and the stroke color is now white for a cleaner look with the badge’s color scheme. It also sorts vertically to fit with the badge’s orientation.

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

forked from alexmacy‘s block: Voronoi Sorting v2 update for d3.unconf badge

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<head>

    <style>

        body { 
          	background-color: black;
            margin: 0; 
        }

        path { 
            fill-opacity: .75; 
            stroke: white; 
        }

    </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)())]],
        y = d3.scaleLog().range([height,0]);

    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] = [samples[i].point[0], y(samples[i].area)];}
        }

        //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]])
        y.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(0," + (-samples[i].point[1] + y(samples[i].area)) + ")"})

        //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>