Skip to content

Instantly share code, notes, and snippets.

@akuhn
Created December 13, 2013 09:40
Show Gist options
  • Save akuhn/7942030 to your computer and use it in GitHub Desktop.
Save akuhn/7942030 to your computer and use it in GitHub Desktop.
Nested layouts for d3.
<html>
<head>
<script src="d3.v3.min.js" charset="utf-8"></script>
<style>
rect {
stroke: blue;
fill: #eee;
}
</style>
</head>
<body>
<script>
// Nested layouts for d3, inspired by http://www.moosetechnology.org/tools/mondrian
// Example: arranges a two-dimensional array of elements as centered rows of
// bottom-aligned boxes. Uses d.w and d.h as width and height.
//
// data.layout = my.layout()
// .margin(4)
// .padding(12)
// .belowEachOther()
// .alignCenter()
// .group()
// .padding(4)
// .besideEachOther()
// .alignBottom();
//
// data.layout()
var my = {}
my.layout = function(node) {
var padding = 0,
child_layout = null,
children = function(self) { return self },
margin = 0,
fun = [];
// Helper functions
function my_layout_width(self) {
return d3.max(children(self), function(d) { return d.x + d.w });
}
function my_layout_height(self) {
return d3.max(children(self), function(d) { return d.y + d.h });
}
// The main working horse
var layout = function(self) {
children(self).forEach(function(d) {
child_layout && child_layout(d)
});
fun.forEach(function(f) { f(self) });
self.w = my_layout_width(self) + 2 * margin;
self.h = my_layout_height(self) + 2 * margin;
}
// Descent down to the next nesting level
layout.group = function() {
return child_layout = my.layout();
}
// Setters and getters
layout.nodes = function(value) {
children = value;
return layout;
}
layout.padding = function(value) {
padding = value;
return layout;
}
layout.margin = function(value) {
margin = value;
return layout;
}
// Arrangement
layout.besideEachOther =
layout.horizontally = function(value) {
fun.push(function(self) {
var x = margin;
children(self).forEach(function(d) {
d.x = x;
x += d.w + padding;
})
})
return layout;
}
layout.belowEachOther =
layout.vertically = function(value) {
fun.push(function(self) {
var y = margin;
children(self).forEach(function(d) {
d.y = y;
y += d.h + padding;
})
})
return layout;
}
// Alignment
function alignment(aggregation, callback) {
return function() {
fun.push(function(self) {
var a = aggregation && aggregation(self);
children(self).forEach(function(d) {
callback(d, a)
})
});
return layout;
}
}
layout.alignRight = alignment(undefined, function(d) { d.x = margin });
layout.alignCenter = alignment(my_layout_width, function(d,w) { d.x = (w - d.w) / 2 + margin});
layout.alignLeft = alignment(my_layout_width, function(d,w) { d.x = w - d.w + margin });
layout.alignTop = alignment(undefined, function(d) { d.y = margin });
layout.alignMiddle = alignment(my_layout_height, function(d,h) { d.y = (h - d.h) / 2 + margin });
layout.alignBottom = alignment(my_layout_height, function(d,h) { d.y = h - d.h + margin });
// Start with all children aligned to top right.
layout.alignTop();
layout.alignRight();
return layout;
}
my.layout.transform = function(d) { return 'translate('+d.x+','+d.y+')' }
my.layout.width = function(d) { return d.w }
my.layout.height = function(d) { return d.h }
var w = function() { return 10 + Math.random() * 40 }
var h = function() { return 15 + Math.random() * 20 }
var data = d3.nest()
.key(function(d) { return Math.floor(Math.random() * 7) })
.entries(d3.range(40).map(function(n) { return { w: w(), h: h(), n: n }}));
var layout = my.layout();
layout
.margin(4)
.padding(12)
.belowEachOther()
.alignCenter()
.group()
.padding(4)
.besideEachOther()
.alignBottom()
.nodes(function(d) { return d.values });
layout(data);
d3.select(document.body)
.append('svg')
.attr('width', data.w)
.attr('height', data.h)
.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('transform', my.layout.transform)
.selectAll('g')
.data(function(d) { return d.values })
.enter()
.append('g')
.attr('transform', my.layout.transform)
.append('rect')
.attr('width', my.layout.width)
.attr('height', my.layout.height);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment