block by emeeks 0cef4dac46d7ada4128e

Fog of War IV

Full Screen

Fog of war is a technique used in video games for nearly four decades–first and often seen in complex tactical and strategy games. Like many data visualization techniques found in video games, it hasn’t been adopted in more traditional data visualization. Here is a simple implementation. There are a set of points displayed on-screen with a set of hexes overlaid upon them. By clicking on a hex, you reveal what is underneath, and if there are no points underneath that hex, nearby empty hexes are also revealed. This hearkens to the mechanics of another, simpler game: minesweeper.

Other examples:

index.html


<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="utf-8" />
<title>Fog of War with Hexbin</title>
<style>

</style>
</head>
<body>
<svg width="500" height="500" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js" charset="utf-8" type="text/javascript"></script>
<script src="d3.hexbin.js" charset="utf-8" type="text/javascript"></script>

    <script type="text/javascript">

    vertices = [[303,348],[354,364],[261,285],[368,403],[315,330],[435,120],[121,275],[98,257],[299,474],[388,369],[216,256],[309,286],[38,287],[131,132],[230,241],[260,334],[189,187],[247,192],[229,219],[224,473],[180,211],[101,157],[198,235],[137,221],[139,228],[130,219],[294,251],[110,189],[201,277],[145,251],[176,261],[170,263],[180,259],[178,264],[83,416],[365,185],[313,196],[198,333],[255,227],[287,171],[333,318],[155,453],[120,242],[82,211],[76,263],[98,234],[142,190],[346,151],[466,220],[250,25],[467,302]];

    hexbin = d3.hexbin()
      .size([500,500])
      .radius(25)
      .x(function (d) {return d.x})
      .y(function (d) {return d.y})

    fullpoints = [];

    vertices.forEach(function (d) {
      fullpoints.push({x: d[0], y: d[1], type: "data"})
    })

    d3.range(20).forEach(function (d) {
      d3.range(20).forEach(function (p) {
        fullpoints.push({x: d * 25, y: p * 25, type: "hex"})
      })
    })

    hexdata = hexbin(fullpoints);

    console.log(hexdata)

    d3.select("svg").selectAll("circle")
      .data(fullpoints.filter(function (d) {return d.type === "data"}))
      .enter()
      .append("circle")
      .attr("cx", function (d) {return d.x})
      .attr("cy", function (d) {return d.y})
      .style("fill", "#fff3b1")
      .style("stroke-width", "2px")
      .style("stroke", "#766400")
      .attr("r", 3);

    d3.select("svg").selectAll("path")
      .data(hexdata)
      .enter()
      .append("path")
      .attr("d", function (d) {return hexbin.hexagon(26); })
      .attr("transform", function(d) { console.log(d); return "translate(" + d.x + "," + d.y + ")"; })
      .style("fill", "#001276")
      .style("stroke", "#001276")
      .style("stroke-width", "10px")
      .style("stroke-opacity", 0.5)
      .on("click", defog)

    function defog(d) {
      d3.selectAll("path")
        .filter(function (p) {
          return Math.abs(p.x - d.x) < 65 && Math.abs(p.y - d.y) < 65 && d.filter(function (d) {return d.type === "data"}).length === 0 && p.filter(function (p) {return p.type === "data"}).length === 0;
        })
        .transition()
        .duration(1000)
        .style("opacity", 0);

      d3.select(this)
        .transition()
        .duration(500)
        .style("fill", "#fff3b1")
        .style("opacity", 0.5)
        .transition()
        .duration(1000)
        .style("opacity", 0);

    }

    </script>
</body>
</html>

d3.hexbin.js

(function() {

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

  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;
    return "m" + hexagon(radius).join("l") + "z";
  };

  hexbin.hexagonArray = function(radius) {
    if (arguments.length < 1) radius = r;
    return hexagon(radius);
  };

  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;
  };

  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]; };

})();