Skip to content

Instantly share code, notes, and snippets.

@syntagmatic
Forked from mbostock/.block
Last active November 8, 2019 07:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save syntagmatic/e7dc072643b1a5f121047280093f459e to your computer and use it in GitHub Desktop.
Save syntagmatic/e7dc072643b1a5f121047280093f459e to your computer and use it in GitHub Desktop.
Dynamic Hexbins
license: gpl-3.0
border: no
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.d3 = global.d3 || {})));
}(this, function (exports) { 'use strict';
var version = "0.2.1";
var thirdPi = Math.PI / 3;
var angles = [0, thirdPi, 2 * thirdPi, 3 * thirdPi, 4 * thirdPi, 5 * thirdPi];
function pointX(d) {
return d[0];
}
function pointY(d) {
return d[1];
}
function hexbin() {
var x0 = 0,
y0 = 0,
x1 = 1,
y1 = 1,
x = pointX,
y = pointY,
r,
dx,
dy;
function hexbin(points) {
var binsById = {}, bins = [], i, n = points.length;
for (i = 0; i < n; ++i) {
if (isNaN(px = +x.call(null, point = points[i], i, points))
|| isNaN(py = +y.call(null, point, i, points))) continue;
var point,
px,
py,
pj = Math.round(py = py / dy),
pi = Math.round(px = px / dx - (pj & 1) / 2),
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 {
bins.push(bin = binsById[id] = [point]);
bin.x = (pi + (pj & 1) / 2) * dx;
bin.y = pj * dy;
}
}
return bins;
}
function hexagon(radius) {
var x0 = 0, y0 = 0;
return angles.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.hexagon = function(radius) {
return "m" + hexagon(radius == null ? r : +radius).join("l") + "z";
};
hexbin.centers = function() {
var centers = [],
j = Math.round(y0 / dy),
i = Math.round(x0 / dx);
for (var y = j * dy; y < y1 + r; y += dy, ++j) {
for (var x = i * dx + (j & 1) * dx / 2; x < x1 + dx / 2; x += dx) {
centers.push([x, y]);
}
}
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.x = function(_) {
return arguments.length ? (x = _, hexbin) : x;
};
hexbin.y = function(_) {
return arguments.length ? (y = _, hexbin) : y;
};
hexbin.radius = function(_) {
return arguments.length ? (r = +_, dx = r * 2 * Math.sin(thirdPi), dy = r * 1.5, hexbin) : r;
};
hexbin.extent = function(_) {
return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], hexbin) : [[x0, y0], [x1, y1]];
};
return hexbin.radius(1);
}
exports.version = version;
exports.hexbin = hexbin;
Object.defineProperty(exports, '__esModule', { value: true });
}));
<!DOCTYPE html>
<meta charset="utf-8">
<style>
html, body {
background: #fff;
}
.hexagons path {
fill: none;
stroke: #fff;
}
</style>
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="d3-hexbin.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>
var margin = {top: 12, right: 12, bottom: 12, left: 12},
width = 960,
height = 500,
θ = 0,
δθ = .03,
i = -1,
n = 2000,
k = 20; // samples to replace per frame
var randomX = d3.randomBates(12),
randomY = d3.randomBates(6),
points = d3.range(n)
.map(function() {
return [(width + Math.round(Math.random()) - 80 + 80 * Math.cos(θ)) * randomX(), (height - 80 + 80 * Math.sin(θ)) * randomY()];
});
var color = d3.scaleSequential(d3.interpolateBuPu)
.domain([0, 35]);
var hexbin = d3.hexbin()
.extent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]])
.radius(13);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var hexagon = svg.append("g")
.attr("class", "hexagons")
.selectAll("path")
.data(hexbin(points), function(d) { return d.x + "," + d.y; })
.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.style("fill", function(d) { return color(d.length); });
d3.timer(function() {
θ += δθ;
var new_points = d3.range(k)
.map(function() {
return [(width - 80 + 80 * Math.cos(θ)) * randomX(), (height - 80 + 80 * Math.sin(θ)) * randomY()];
});
points = points.slice(k)
.concat(new_points);
hexagon = svg.select(".hexagons").selectAll("path")
.data(hexbin(points), function(d) { return d.x + "," + d.y; });
hexagon.exit().remove();
hexagon.enter().append("path")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.merge(hexagon)
.style("fill", function(d) { return color(d.length); });
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment