Skip to content

Instantly share code, notes, and snippets.

@gustavderdrache
Last active December 28, 2015 10:39
Show Gist options
  • Save gustavderdrache/7487735 to your computer and use it in GitHub Desktop.
Save gustavderdrache/7487735 to your computer and use it in GitHub Desktop.

By-radius and by-area bubble scaling

Assumptions

  • 0 ≤ population ≤ 100 (unit: percentage)
  • 0 ≤ radius ≤ 40 (unit: pixels)
  • 0 ≤ area ≤ 5026.55 (unit: pixels²)

Formulas

The formulas given below use are functions of population, returning radius. This mimics the behavior of SVG's <circle> r attribute.

  • radius: radius = 0.4 * population
  • area: A = population * area/100; radius = sqrt(A/π)

Display

At the top and bottom are statically-computed circles. There are twelve, in equal increments (from 1/12 to 12/12). The top uses the by-radius formula, whereas the bottom uses the by-area formula. The black lines are shown as guides for the circle sizes.

In the middle are two circles. Each has its radius computed by the by-radius function (at left) or the by-area function (at right). Population ranges from 0 to 100 over five seconds.

<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style>
body {
width: 960px;
margin: 0 auto;
padding: 0;
}
p {
text-align: center;
}
circle {
fill: #bdbdbd;
}
path {
fill: none;
stroke: #000;
}
</style>
</head>
<body>
<script>
'use strict';
var radius = 40,
area = Math.PI * radius * radius,
data = [
{
direction: false,
true: byRadius,
false: byRadiusInv
},
{
direction: false,
true: byArea,
false: byAreaInv
}
];
var g = d3.select('body')
.insert('svg:svg', ':first-child')
.attr('width', 960)
.attr('height', 260)
.append('svg:g')
.attr('transform', 'translate(15, 10)');
var circles = g.selectAll('circle')
.data(data)
.enter()
.append('svg:circle')
.attr('cx', function (d, i) { return 400 + 160 * i; })
.attr('cy', radius * 3)
.attr('r', 0)
.each(animate);
var groups = g.selectAll('g')
.data([byRadius, byArea])
.enter()
.append('svg:g')
.attr('transform', function (d, i) {
return 'translate(0, ' + (radius + radius * 4 * i) + ')';
});
groups.selectAll('circle')
.data(function (fn) {
return d3.range(12).map(function (d) { return fn((d + 1)/12); });
})
.enter()
.append('svg:circle')
.attr('cx', function (d, i) { return radius * 2 * i; })
.attr('r', function (d) { return d; });
groups.selectAll('path')
.data(function (fn) {
var range = d3.range(12).map(function (i) { return radius * 2 * i; });
return [
range.map(function (d, i) { return [d, fn((i + 1)/12)]; }),
range.map(function (d, i) { return [d, -fn((i + 1)/12)]; })
];
})
.enter()
.append('svg:path')
.attr('d', d3.svg.line());
function byRadius(t) {
return t * radius;
}
function byRadiusInv(t) {
return (1 - t) * radius;
}
function byArea(t) {
return Math.sqrt(t * area / Math.PI);
}
function byAreaInv(t) {
return Math.sqrt((1 - t) * area / Math.PI);
}
function animate(d, i) {
d.direction = !d.direction;
d3.select(this)
.transition()
.duration(5000)
.ease('linear')
.attrTween('r', function () { return d[d.direction]; })
.each('end', animate);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment