(function() { var fractalize, global, hex_coords, new_hex, redraw; global = {}; /* compute a Lindenmayer system given an axiom, a number of steps and rules */ fractalize = function(config) { var char, i, input, output, _i, _len, _ref; input = config.axiom; for (i = 0, _ref = config.steps; 0 <= _ref ? i < _ref : i > _ref; 0 <= _ref ? i++ : i--) { output = ''; for (_i = 0, _len = input.length; _i < _len; _i++) { char = input[_i]; if (char in config.rules) { output += config.rules[char]; } else { output += char; } } input = output; } return output; }; /* convert a Lindenmayer string into an array of hexagonal coordinates */ hex_coords = function(config) { var char, current, dir, dir_i, directions, path, _i, _len, _ref; directions = [ { x: +1, y: -1, z: 0 }, { x: +1, y: 0, z: -1 }, { x: 0, y: +1, z: -1 }, { x: -1, y: +1, z: 0 }, { x: -1, y: 0, z: +1 }, { x: 0, y: -1, z: +1 } ]; /* start the walk from the origin cell, facing east */ path = [ { x: 0, y: 0, z: 0 } ]; dir_i = 0; _ref = config.fractal; for (_i = 0, _len = _ref.length; _i < _len; _i++) { char = _ref[_i]; if (char === '+') { dir_i = (dir_i + 1) % directions.length; } else if (char === '-') { dir_i = dir_i - 1; if (dir_i === -1) dir_i = 5; } else if (char === 'F') { dir = directions[dir_i]; current = path[path.length - 1]; path.push({ x: current.x + dir.x, y: current.y + dir.y, z: current.z + dir.z }); } } return path; }; window.main = function() { var d, data, dx, dy, gosper, height, hexes, radius, svg, width; width = 960; height = 500; svg = d3.select('body').append('svg').attr('width', width).attr('height', height); global.vis = svg.append('g').attr('transform', 'translate(490,30)'); /* create the Gosper curve */ gosper = fractalize({ axiom: 'A', steps: 3, rules: { A: 'A+BF++BF-FA--FAFA-BF+', B: '-FA+BFBF++BF+FA--FA-B' } }); /* convert the curve into coordinates of hex cells */ data = hex_coords({ fractal: gosper }); /* create the GeoJSON hexes */ hexes = { type: 'FeatureCollection', features: (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = data.length; _i < _len; _i++) { d = data[_i]; _results.push(new_hex(d)); } return _results; })() }; /* custom projection to make hexagons appear regular (y axis is also flipped) */ radius = 12; dx = radius * 2 * Math.sin(Math.PI / 3); dy = radius * 1.5; global.path_generator = d3.geo.path().projection(d3.geo.transform({ point: function(x, y) { return this.stream.point(x * dx / 2, -(y - (2 - (y & 1)) / 3) * dy / 2); } })); /* start the animation */ redraw(hexes.features, 1); /* draw the origin */ return global.vis.append('circle').attr('cx', 0).attr('cy', 0).attr('r', 3); }; /* create a new hexagon */ new_hex = function(d) { /* conversion from hex coordinates to rect */ var x, y; x = 2 * (d.x + d.z / 2.0); y = 2 * d.z; return { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[[x, y + 2], [x + 1, y + 1], [x + 1, y], [x, y - 1], [x - 1, y], [x - 1, y + 1], [x, y + 2]]] } }; }; /* update the drawing, then call again this function till data ends */ redraw = function(data, size) { return global.vis.selectAll('.hex').data(data.slice(0, size)).enter().append('path').attr('class', 'hex').attr('d', global.path_generator).attr('fill', '#EB5B5B').transition().duration(100).each('end', (function() { if (size > data.length) return; return redraw(data, size + 1); })).transition().duration(600).attr('fill', '#FF9F4A').transition().duration(600).attr('fill', '#5FD05F').transition().duration(600).attr('fill', '#76B0DA'); }; }).call(this);