(function() { var global, hexProjection, hexTopology, register; global = { registry: {} }; register = function(hexes) { return hexes.each(function(d) { if (!(d.x in global.registry)) global.registry[d.x] = {}; if (!(d.y in global.registry[d.x])) global.registry[d.x][d.y] = {}; if (!(d.z in global.registry[d.x][d.y])) { return global.registry[d.x][d.y][d.z] = this; } }); }; window.main = function() { var coord_format, height, new_label, radius, svg, width; width = 960; height = 500; radius = 40; coord_format = d3.format(' 03d'); global.hex_topology = hexTopology(radius, width, height); global.path_generator = d3.geo.path().projection(hexProjection(radius)); svg = d3.select('body').append('svg').attr('width', width).attr('height', height); /* draw the axes */ svg.append('line').attr('class', 'x axis').attr('x1', 450).attr('y1', 300).attr('x2', 550).attr('y2', 357); svg.append('line').attr('class', 'y axis').attr('x1', 450).attr('y1', 300).attr('x2', 350).attr('y2', 357); svg.append('line').attr('class', 'z axis').attr('x1', 450).attr('y1', 300).attr('x2', 450).attr('y2', 177); /* draw the hexagons */ svg.append('g').attr('class', 'hexagon').selectAll('path').data(global.hex_topology.objects.hexagons.geometries).enter().append('path').attr('d', function(d) { return global.path_generator(topojson.feature(global.hex_topology, d)); }).style('stroke', function(d) { if (d.x === 0 && d.y === 0 && d.z === 0) { return 'black'; } else { return 'none'; } }).call(register); /* draw the coordinates */ new_label = svg.append('g').attr('class', 'label').selectAll('text').data(global.hex_topology.objects.hexagons.geometries).enter().append('text').attr('transform', function(d) { return "translate(" + (global.path_generator.centroid(topojson.feature(global.hex_topology, d))) + ")"; }); new_label.append('tspan').text(function(d) { return coord_format(d.x); }).attr('x', '0.6em').attr('y', '-1em'); new_label.append('tspan').text(function(d) { return coord_format(d.y); }).attr('x', '0.6em').attr('y', '.35em'); new_label.append('tspan').text(function(d) { return coord_format(d.z); }).attr('x', '0.6em').attr('y', '1.65em'); svg.append('path').datum(topojson.mesh(global.hex_topology, global.hex_topology.objects.hexagons)).attr('class', 'mesh').attr('d', global.path_generator); /* select a few example hexagons by using their coordinates */ d3.select(global.registry[-4][2][2]).attr('fill', 'yellow'); return d3.select(global.registry[1][-2][1]).attr('fill', 'yellow'); }; /* create the hex mesh TopoJSON */ hexTopology = function(radius, width, height) { var arcs, dx, dy, geometries, i, j, m, n, q, x, y; dx = radius * 2 * Math.sin(Math.PI / 3); dy = radius * 1.5; m = Math.ceil((height + radius) / dy) + 1; n = Math.ceil(width / dx) + 1; geometries = []; arcs = []; for (j = -1; -1 <= m ? j <= m : j >= m; -1 <= m ? j++ : j--) { for (i = -1; -1 <= n ? i <= n : i >= n; -1 <= n ? i++ : i--) { y = j * 2; x = (i + (j & 1) / 2) * 2; arcs.push([[x, y - 1], [1, 1]], [[x + 1, y], [0, 1]], [[x + 1, y + 1], [-1, 1]]); } } q = 3; for (j = 0; 0 <= m ? j < m : j > m; 0 <= m ? j++ : j--) { for (i = 0; 0 <= n ? i < n : i > n; 0 <= n ? i++ : i--) { geometries.push({ type: 'Polygon', arcs: [[q, q + 1, q + 2, ~(q + (n + 2 - (j & 1)) * 3), ~(q - 2), ~(q - (n + 2 + (j & 1)) * 3 + 2)]], x: i - 6 + Math.floor((j - 6) / 2), y: -(i - 6) + Math.ceil((j - 6) / 2), z: Math.floor(-j + 6) }); q += 3; } q += 6; } return { transform: { translate: [0, 0], scale: [1, 1] }, objects: { hexagons: { type: 'GeometryCollection', geometries: geometries } }, arcs: arcs }; }; /* define a custom projection to make hexagons appear regular */ hexProjection = function(radius) { var dx, dy; dx = radius * 2 * Math.sin(Math.PI / 3); dy = radius * 1.5; return { stream: function(stream) { return { point: (function(x, y) { return stream.point(x * dx / 2, (y - (2 - (y & 1)) / 3) * dy / 2); }), lineStart: (function() { return stream.lineStart(); }), lineEnd: (function() { return stream.lineEnd(); }), polygonStart: (function() { return stream.polygonStart(); }), polygonEnd: (function() { return stream.polygonEnd(); }) }; } }; }; }).call(this);