|
(function() { |
|
var fractalize, global, hex_coords, new_hex; |
|
|
|
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 coords, dx, dy, e, gosper, height, hexes, i, path_generator, radius, seq, 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(540,10)'); |
|
/* create the input sequence (random length between 1000 and 2401) |
|
*/ |
|
/* give the element a random value (random choice between uniform and triangular distribution) |
|
*/ |
|
seq = (function() { |
|
var _ref, _results; |
|
_results = []; |
|
for (i = 0, _ref = 1000 + Math.floor(Math.random() * 1401); 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) { |
|
_results.push(Math.random() > 0.5 ? Math.random() : (Math.random() + Math.random()) / 2); |
|
} |
|
return _results; |
|
})(); |
|
/* sort the sequence by value |
|
*/ |
|
seq.sort(); |
|
/* create the Gosper curve |
|
*/ |
|
gosper = fractalize({ |
|
axiom: 'A', |
|
steps: 4, |
|
rules: { |
|
A: 'A+BF++BF-FA--FAFA-BF+', |
|
B: '-FA+BFBF++BF+FA--FA-B' |
|
} |
|
}); |
|
/* convert the curve into coordinates of hex cells |
|
*/ |
|
coords = hex_coords({ |
|
fractal: gosper |
|
}); |
|
/* create the GeoJSON hexes |
|
*/ |
|
hexes = { |
|
type: 'FeatureCollection', |
|
features: (function() { |
|
var _len, _results; |
|
_results = []; |
|
for (i = 0, _len = seq.length; i < _len; i++) { |
|
e = seq[i]; |
|
_results.push(new_hex(coords[i], e)); |
|
} |
|
return _results; |
|
})() |
|
}; |
|
/* custom projection to make hexagons appear regular (y axis is also flipped) |
|
*/ |
|
radius = 5; |
|
dx = radius * 2 * Math.sin(Math.PI / 3); |
|
dy = radius * 1.5; |
|
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); |
|
} |
|
})); |
|
/* draw the cells |
|
*/ |
|
global.vis.selectAll('.hex').data(hexes.features).enter().append('path').attr('class', 'hex').attr('d', path_generator).append('title'); |
|
recolor(false); |
|
return document.getElementById('default').focus(); |
|
}; |
|
|
|
/* color the cells according to the selected quantization |
|
*/ |
|
|
|
window.recolor = function(quantization) { |
|
/* define a quantize scale |
|
*/ |
|
var colorify, perc, quantize; |
|
if (quantization !== false) { |
|
quantize = d3.scale.quantize().domain([0, 1]).range(d3.range(0, 1, 1.0 / quantization)); |
|
} else { |
|
quantize = function(x) { |
|
return x; |
|
}; |
|
} |
|
/* define a linear color scale |
|
*/ |
|
colorify = d3.scale.linear().domain([0, 1]).range(['rgb(247,251,255)', 'rgb(8,48,107)']).interpolate(d3.interpolateHcl); |
|
/* define a percentage format |
|
*/ |
|
perc = d3.format('.4p'); |
|
return global.vis.selectAll('.hex').attr('fill', function(d) { |
|
return colorify(quantize(d.properties['value'])); |
|
}).attr('stroke', function(d) { |
|
return colorify(quantize(d.properties['value'])); |
|
}).select('title').text(function(d) { |
|
return "" + (perc(d.properties['value'])) + " > " + (perc(quantize(d.properties['value']))); |
|
}); |
|
}; |
|
|
|
/* create a new hexagon |
|
*/ |
|
|
|
new_hex = function(c, e) { |
|
/* conversion from hex coordinates to rect |
|
*/ |
|
var x, y; |
|
x = 2 * (c.x + c.z / 2.0); |
|
y = 2 * c.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]]] |
|
}, |
|
properties: { |
|
'value': e |
|
} |
|
}; |
|
}; |
|
|
|
}).call(this); |