Skip to content

Instantly share code, notes, and snippets.

@gilmoreorless
Last active June 30, 2016 07:42
Show Gist options
  • Save gilmoreorless/7b265331aadd4eac2108fccf80211c4d to your computer and use it in GitHub Desktop.
Save gilmoreorless/7b265331aadd4eac2108fccf80211c4d to your computer and use it in GitHub Desktop.
Directional ring of nodes
license: cc-by-4.0
height: 600

Say you have a sequence of data nodes – here represented by numbered dots – and you want to evenly place them around a circle. The simplest way is to go around the circumference of the circle and place the nodes at regular intervals in sequence order.

But what if you want the node sequence to run left-to-right while still being in a circle? That’s what this experiment is for. The key is the rearrange function which shuffles the nodes into a new sequence. The reshuffled node list is still distributed around the circumference, but the node numbers are in left-to-right order.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: sans-serif;
margin: auto;
position: relative;
width: 600px;
}
.controls {
left: 50%;
position: absolute;
top: 2em;
transform: translateX(-50%);
}
.blob {
stroke: #fff;
stroke-width: 1.5px;
}
.text {
fill: #fff;
font-size: 0.8125em;
text-anchor: middle;
}
.link {
stroke: #433;
}
</style>
<body>
<div class="controls">
<label><input type="checkbox" id="rearrange"> Use left-to-right arrangement</label>
</div>
<script src="//d3js.org/d3.v3.js"></script>
<!--<script src="/ausvoteflow/frontbench/d3.js"></script>-->
<script>
var width = 600,
height = 600,
nodeRadius = 15,
nodeCount = 23,
circleRadius = height / 3;
var cx = width / 2,
cy = height / 2,
shouldRearrange = false;
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + [cx, cy] + ')');
var nodes = d3.range(nodeCount).map(function (n) {
return {
num: n,
hue: n / nodeCount * 300
};
});
var links = d3.range(nodeCount - 1).map(function (n) {
return {
from: nodes[n],
to: nodes[n + 1]
};
});
var angleRads = Math.PI * 2 / nodeCount;
var links = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr({
'class': 'link',
x1: 0,
y1: 0,
x2: 0,
y2: 0
});
var blobs = svg.selectAll('.node')
.data(nodes, function (d) { return d.num; })
.enter().append('g')
.attr('class', 'node');
blobs.append('circle')
.attr('class', 'blob')
.attr('r', nodeRadius)
.attr('fill', function (d) { return 'hsl(' + d.hue + ', 50%, 50%)'; });
blobs.append('text')
.attr('class', 'text')
.attr('dy', '0.35em')
.text(function (d) { return d.num + 1; });
function render() {
var nodeData = shouldRearrange ? rearrange(nodes) : nodes;
d3.transition()
.duration(1000)
.each(function () {
blobs
.data(nodeData, function (d) { return d.num; })
.transition()
.attr('transform', function (d, i) {
var angle = angleRads * i - Math.PI / 2;
d.x = Math.sin(angle) * circleRadius;
d.y = Math.cos(angle) * circleRadius;
return 'translate(' + [d.x, d.y] + ')';
});
links
.transition()
.attr({
x1: function (d) { return d.from.x; },
y1: function (d) { return d.from.y; },
x2: function (d) { return d.to.x; },
y2: function (d) { return d.to.y; }
});
});
}
// [1, 2, 3, 4, 5, 6, 7] => [1, 3, 5, 7, 6, 4, 2]
function rearrange(nodes) {
var left = [], right = [];
nodes.forEach(function (node, i) {
(i % 2 ? right : left).push(node);
});
return left.concat(right.reverse());
}
document.getElementById('rearrange').addEventListener('change', function () {
shouldRearrange = this.checked;
render();
}, false);
render();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment