block by renecnielsen b0b1234372be0bc31f89

Hexbin Canvas Transitions

Full Screen

Hexbin Heightmap Transitions with Canvas

forked from syntagmatic‘s block: Hexbin Canvas Transitions

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>
html, body {
  background: #222;
}
canvas {
  position: absolute;
  top: 0;
  left: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="d3.canvas-hexbin.js"></script>
<script>

var width = 960,
    height = 500;

var hexbin = d3.canvasHexbin()
    .size([width, height])
    .radius(4);

var color = d3.scale.linear()
   .domain([0, 14, 15, 35, 132])
   .range(["#050505", "#222", "#d7191c", "#ffffbf", "#2c7bb6"])
   .interpolate(d3.interpolateHcl);

var canvas = d3.select("body").append("canvas")
    .attr("width", width)
    .attr("height", height);

var context = canvas.node().getContext("2d");
var points = [];
var hexagons = [];

hexbin.context(context);

getImage("readme.png", function(image) {
  context.drawImage(image, 0, 0, width, height);
  image = context.getImageData(0, 0, width, height);

  // Rescale the colors.
  for (var c, i = 0, n = width * height * 4, d = image.data; i < n; i += 4) {
    points.push([i/4%width, Math.floor(i/4/width), d[i]]);
  }

  hexagons = hexbin(points);
  hexagons.forEach(function(d) {
    d.min = d3.min(d, function(p) { return p[2]; });
    d.max = d3.max(d, function(p) { return p[2]; });
    d.mean = d3.mean(d, function(p) { return p[2]; });
  });

  // optimization: filter out sea level
  hexagons = hexagons.filter(function(d) {
    return d.max > 16 || d.min < 10;
  });

  var interpolated = d3.interpolate(
    hexagons.map(function(d) { return d.min; }),
    hexagons.map(function(d) { return d.max; })
  );

  function toggle() {
    d3.timer(function(t) {
      if (t > 9000) return true;
      if (t > 4500) t = 9000 - t;

      context.clearRect(0,0,width,height);

      interpolated(t/4500).forEach(function(d,i) {
        context.save();
        context.fillStyle = color(d);
        context.translate(hexagons[i].x, hexagons[i].y);
        hexbin.hexagon(4.5);
        context.fill();
        context.restore();
      });
    });
  };

  loop(toggle, 9000);

  function loop(f,t) {
    f();
    setTimeout(function() {
      loop(f,t);
    },t)
  };
});


function getImage(path, callback) {
  var image = new Image;
  image.onload = function() { callback(image); };
  image.src = path;
}

</script>

circles.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>
html, body {
  background: #444;
}
canvas {
  position: absolute;
  top: 0;
  left: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="d3.canvas-hexbin.js"></script>
<script>

var width = 960,
    height = 500;

var hexbin = d3.canvasHexbin()
    .size([width, height])
    .radius(5.5);

var color = d3.scale.linear()
   .domain([0, 14, 15, 35, 132])
   .range(["#000", "#444", "#d7191c", "#ffffbf", "#2c7bb6"])
   .interpolate(d3.interpolateHcl);

var canvas = d3.select("body").append("canvas")
    .attr("width", width)
    .attr("height", height);

var context = canvas.node().getContext("2d");
var points = [];
var hexagons = [];

hexbin.context(context);

getImage("readme.png", function(image) {
  context.drawImage(image, 0, 0, width, height);
  image = context.getImageData(0, 0, width, height);

  // Rescale the colors.
  for (var c, i = 0, n = width * height * 4, d = image.data; i < n; i += 4) {
    points.push([i/4%width, Math.floor(i/4/width), d[i]]);
  }

  hexagons = hexbin(points);
  hexagons.forEach(function(d) {
    d.min = d3.min(d, function(p) { return p[2]; });
    d.max = d3.max(d, function(p) { return p[2]; });
    d.mean = d3.mean(d, function(p) { return p[2]; });
  });

  // filter sea level
  hexagons = hexagons.filter(function(d) {
    return d.max > 16 || d.min < 10;
  });

  var interpolated = d3.interpolate(
    hexagons.map(function(d) { return d.min; }),
    hexagons.map(function(d) { return d.max; })
  );

  function toggle() {
    d3.timer(function(t) {
      if (t > 6000) return true;
      if (t > 3000) t = 6000 - t;

      context.clearRect(0,0,width,height);

      interpolated(t/3000).forEach(function(d,i) {
        context.fillStyle = color(d);
        context.beginPath();
        context.arc(hexagons[i].x,hexagons[i].y,4,0,2*Math.PI);
        context.fill();
      });
    });
  };

  loop(toggle, 6000);

  function loop(f,t) {
    f();
    setTimeout(function() {
      loop(f,t);
    },t)
  };
});


function getImage(path, callback) {
  var image = new Image;
  image.onload = function() { callback(image); };
  image.src = path;
}

</script>

d3.canvas-hexbin.js

(function() {

d3.canvasHexbin = function() {
  var width = 1,
      height = 1,
      r,
      x = d3_hexbinX,
      y = d3_hexbinY,
      dx,
      dy,
      context = null;

  function hexbin(points) {
    var binsById = {};

    points.forEach(function(point, i) {
      var py = y.call(hexbin, point, i) / dy, pj = Math.round(py),
          px = x.call(hexbin, point, i) / dx - (pj & 1 ? .5 : 0), pi = Math.round(px),
          py1 = py - pj;

      if (Math.abs(py1) * 3 > 1) {
        var px1 = px - pi,
            pi2 = pi + (px < pi ? -1 : 1) / 2,
            pj2 = pj + (py < pj ? -1 : 1),
            px2 = px - pi2,
            py2 = py - pj2;
        if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) pi = pi2 + (pj & 1 ? 1 : -1) / 2, pj = pj2;
      }

      var id = pi + "-" + pj, bin = binsById[id];
      if (bin) bin.push(point); else {
        bin = binsById[id] = [point];
        bin.i = pi;
        bin.j = pj;
        bin.x = (pi + (pj & 1 ? 1 / 2 : 0)) * dx;
        bin.y = pj * dy;
      }
    });

    return d3.values(binsById);
  }

  function hexagon(radius) {
    var x0 = 0, y0 = 0;
    return d3_hexbinAngles.map(function(angle) {
      var x1 = Math.sin(angle) * radius,
          y1 = -Math.cos(angle) * radius,
          dx = x1 - x0,
          dy = y1 - y0;
      x0 = x1, y0 = y1;
      return [dx, dy];
    });
  }

  hexbin.x = function(_) {
    if (!arguments.length) return x;
    x = _;
    return hexbin;
  };

  hexbin.y = function(_) {
    if (!arguments.length) return y;
    y = _;
    return hexbin;
  };

  hexbin.hexagon = function(radius) {
    if (arguments.length < 1) radius = r;
    
    hexagon(radius).forEach(function(d,i) {
      if (i == 0) {
        context.moveTo(d[0], d[1]);
        context.beginPath();
      } else {
       context.lineTo(d[0], d[1]);
      }
    });
    context.closePath();
    return context;
  };

  hexbin.centers = function() {
    var centers = [];
    for (var y = 0, odd = false, j = 0; y < height + r; y += dy, odd = !odd, ++j) {
      for (var x = odd ? dx / 2 : 0, i = 0; x < width + dx / 2; x += dx, ++i) {
        var center = [x, y];
        center.i = i;
        center.j = j;
        centers.push(center);
      }
    }
    return centers;
  };

  hexbin.mesh = function() {
    var fragment = hexagon(r).slice(0, 4).join("l");
    return hexbin.centers().map(function(p) { return "M" + p + "m" + fragment; }).join("");
  };

  hexbin.size = function(_) {
    if (!arguments.length) return [width, height];
    width = +_[0], height = +_[1];
    return hexbin;
  };

  hexbin.radius = function(_) {
    if (!arguments.length) return r;
    r = +_;
    dx = r * 2 * Math.sin(Math.PI / 3);
    dy = r * 1.5;
    return hexbin;
  };

  hexbin.context = function(_) {
    if (!arguments.length) return context;
    context = _;
    return hexbin;
  };
 
  return hexbin.radius(1);
};

var d3_hexbinAngles = d3.range(0, 2 * Math.PI, Math.PI / 3),
    d3_hexbinX = function(d) { return d[0]; },
    d3_hexbinY = function(d) { return d[1]; };

})();