Working towards a Hilbert Curve based grid layout. The idea would be to quickly layout a 1-dimensional array of data in a compact 2D area.
It has the added benefit that the sorting of the 1D array impacts the 2D distance, namely that items close together in 1D will be close together in 2D
LSystem code lifted from this block by nitaku who has many fascinating d3 experiments
Built with blockbuilder.org
forked from enjalot's block: hilbert grid
forked from enjalot's block: hilbert grid layout
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="lsystem.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg { width: 100%; height: 100%; }
.curve {
fill: none;
stroke: #b5b5b5;
}
</style>
</head>
<body>
<svg></svg>
<script>
/*
TODO / the main problem with this code is that all cells are 1x1 rect, not paths on the hilbert curve; so we cannot represent a State with its number of representatives.
*/
// data copied from the snake viz at https://projects.fivethirtyeight.com/2016-election-forecast/
d3.json('snake.json', (_, snake)=>{
var height = 400;
var n=8;
console.log("snake", snake[0])
var exampleData = d3.range(n*n).map(function(d) { return Math.random() });
exampleData = snake
.map(d => {return { name: d.state.state, evs: d.evs, margin: d.state.margin.polls.margin }; })
.sort((a,b) => d3.ascending(a.margin, b.margin))
var sumevs = exampleData.map(d => d.evs).reduce((a,b) => a+b,0);
var layout = new hilbert()
.sideLength(Math.floor(height/Math.sqrt(sumevs)))
//console.log("NODES", layout.nodes(exampleData))
var color = d3.scale.linear()
.domain(d3.extent(exampleData.map(d => d.margin)))
.range(["red", "blue"])
.interpolate(d3.interpolateHcl)
var svg = d3.select("svg")
var g = svg.append("g")
.attr("transform", "translate(270, 40)")
function drawCells() {
var cells = g.selectAll("rect")
.data(layout.nodes(exampleData)) // everything is recalculated when nodes is called
cells.enter().append("rect")
cells.exit().remove()
cells
.attr({
x: function(d) { return d.x },
y: function(d) { return d.y },
width: layout.sideLength() - 1,
height: layout.sideLength() - 1,
fill: function(d,i) { return color(d.data.margin) }
})
}
drawCells();
function hilbert() {
var angle = 270 * Math.PI / 180;
var nodes = [];
var grid = [];
var data = [];
var sideLength = 20;
var steps, hilbertConfig, hilbertFractal;
function calculate() {
steps = Math.ceil(Math.log2(data.length || 1) / 2)
hilbertConfig = {
steps: steps,
axiom: 'A',
rules: {
A: '-BF+AFA+FB-',
B: '+AF-BFB-FA+'
}
}
hilbertFractal = LSystem.fractalize(hilbertConfig);
}
function newNodes() {
calculate();
nodes = [];
grid = LSystem.grid({
fractal: hilbertFractal,
side: sideLength,
angle: angle
})
console.log(data, grid)
data.forEach(function(d,i) {
var node = {
x: grid[i].x,
y: grid[i].y,
data: d,
index: i
}
nodes.push(node);
})
}
this.nodes = function(val) {
if(val) {
data = val
}
newNodes();
return nodes;
}
this.sideLength = function(val) {
if(val) {
sideLength = val;
return this;
}
return sideLength;
}
}
})
</script>
</body>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js