A simple implementation of fog of war using hexbinning. Mouseover the darkened region to reveal the data (or lack of data) underneath. If you mouse over a region with no data, it will reveal any hexes nearby that also don't have data underneath.
Last active
May 8, 2017 10:15
-
-
Save emeeks/f0f96f11c7d1afceaeb3 to your computer and use it in GitHub Desktop.
Fog of War III
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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]; }; | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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 = [[162, 332], [182, 299], [141, 292], [158, 264], [141, 408], [160, 400], [177, 430], [151, 442], [155, 425], [134, 430], [126, 447], [139, 466], [160, 471], [167, 447], [182, 466], [192, 442], [187, 413], [173, 403], [168, 425], [153, 413], [179, 275], [163, 292], [134, 270], [143, 315], [177, 320], [163, 311], [162, 281], [182, 255], [141, 226], [156, 235], [173, 207], [187, 230], [204, 194], [165, 189], [145, 201], [158, 167], [190, 165], [206, 145], [179, 153], [204, 114], [221, 138], [243, 112], [248, 139], [177, 122], [179, 99], [196, 82], [219, 90], [240, 75], [218, 61], [228, 53], [211, 34], [197, 51], [179, 65], [155, 70], [165, 85], [134, 80], [124, 58], [153, 44], [173, 34], [192, 27], [156, 19], [119, 32], [128, 17], [138, 36], [100, 58], [112, 73], [100, 92], [78, 100], [83, 78], [61, 63], [80, 44], [100, 26], [60, 39], [43, 71], [34, 54], [32, 90], [53, 104], [60, 82], [66, 99], [247, 94], [187, 180], [221, 168]]; | |
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); | |
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", "#001276") | |
.attr("r", 2); | |
d3.select("svg").selectAll("path") | |
.data(hexdata) | |
.enter() | |
.append("path") | |
.attr("d", function (d) {return hexbin.hexagon(25); }) | |
.attr("transform", function(d) { console.log(d); return "translate(" + d.x + "," + d.y + ")"; }) | |
.style("fill", "#001276") | |
.style("stroke", "#001276") | |
.on("mouseover", defog) | |
function defog(d) { | |
d3.select(this) | |
.transition() | |
.duration(1000) | |
.style("opacity", 0); | |
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); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment