|
// Generated by CoffeeScript 1.4.0 |
|
(function() { |
|
var color, 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, _j, _len, _ref; |
|
input = config.axiom; |
|
for (i = _i = 0, _ref = config.steps; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { |
|
output = ''; |
|
for (_j = 0, _len = input.length; _j < _len; _j++) { |
|
char = input[_j]; |
|
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(660,360) rotate(120)'); |
|
/* 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 |
|
*/ |
|
|
|
|
|
color = function(i) { |
|
var i_49, i_7; |
|
i_7 = Math.floor(i / 7); |
|
i_49 = Math.floor(i / 49); |
|
return d3.hcl((i_7 % 7) * 360 / 7, 30, (function() { |
|
switch (i_49) { |
|
case 0: |
|
return 80; |
|
case 1: |
|
return 55; |
|
case 2: |
|
return 30; |
|
case 3: |
|
return 55; |
|
case 4: |
|
return 80; |
|
case 5: |
|
return 55; |
|
case 6: |
|
return 80; |
|
} |
|
})()); |
|
}; |
|
|
|
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', function(d, i) { |
|
return color(i); |
|
}).transition().duration(60).each('end', (function() { |
|
if (size > data.length) { |
|
return; |
|
} |
|
return redraw(data, size + 1); |
|
})); |
|
}; |
|
|
|
}).call(this); |