Skip to content

Instantly share code, notes, and snippets.

@kleem
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kleem/230ff9a14751b8f3092d to your computer and use it in GitHub Desktop.
Save kleem/230ff9a14751b8f3092d to your computer and use it in GitHub Desktop.
Arc diagram: Italian tongue-twister

An example of arc diagram visualizing repetitions of sequences of two or more characters in an italian tongue-twister.

Arc diagrams were first introduced in Wattenberg 2002. To avoid cluttering, not all repetions of sequences are shown; only the ones that are considered fundamental for the understanding of the structure are displayed. Refer to the paper to have more details. In this example, meaningful matches are manually selected.

sequence = ({'char': char, 'i': i} for char, i in 'Tre tigri contro tre tigri')
#equals = (a,b) -> a.char.toLowerCase() is b.char.toLowerCase()
one = sequence[0...9]
two = sequence[17...26]
three = sequence[0...2]
four = sequence[13...15]
five = sequence[17...19]
matches = [
{source: one, target: two},
{source: three, target: four},
{source: four, target: five}
]
svg = d3.select('svg')
width = svg.node().getBoundingClientRect().width
height = svg.node().getBoundingClientRect().height
# translate the viewBox to have (0,0) at the center of the vis
svg
.attr
viewBox: "#{-width/2} #{-height+40} #{width} #{height}"
width -= 60
x = d3.scale.ordinal()
.domain([0...sequence.length])
.rangeBands([-width/2,width/2])
characters = svg.selectAll('.character')
.data(sequence)
characters.enter().append('text')
.text((item) -> item.char)
.attr
class: 'character'
x: (item) -> x(item.i) + x.rangeBand()/2
dy: '1em'
arcs = svg.selectAll('.arc')
.data(matches)
arcs.enter().append('path')
.attr
class: 'arc'
d: (link) ->
start_left = x(link.source[0].i)
start_right = x(link.source[link.source.length-1].i) + x.rangeBand()
end_left = x(link.target[0].i)
end_right = x(link.target[link.target.length-1].i) + x.rangeBand()
big_radius = (end_right-start_left)/2
small_radius = (end_left-start_right)/2
return "M#{start_left} 0 A#{big_radius} #{big_radius} 0 1 1 #{end_right} 0 L#{end_left} 0 A#{small_radius} #{small_radius} 0 1 0 #{start_right} 0 z"
svg {
background: white;
}
.character {
text-anchor: middle;
font-size: 24px;
font-family: Georgia;
}
.arc {
fill-opacity: 0.2;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="Arc diagram: Italian tongue-twister" />
<title>Arc diagram: Italian tongue-twister</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg height="500" width="960"></svg>
<script src="index.js"></script>
</body>
</html>
(function() {
var arcs, char, characters, five, four, height, i, matches, one, sequence, svg, three, two, width, x, _i, _ref, _results;
sequence = (function() {
var _i, _len, _ref, _results;
_ref = 'Tre tigri contro tre tigri';
_results = [];
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
char = _ref[i];
_results.push({
'char': char,
'i': i
});
}
return _results;
})();
one = sequence.slice(0, 9);
two = sequence.slice(17, 26);
three = sequence.slice(0, 2);
four = sequence.slice(13, 15);
five = sequence.slice(17, 19);
matches = [
{
source: one,
target: two
}, {
source: three,
target: four
}, {
source: four,
target: five
}
];
svg = d3.select('svg');
width = svg.node().getBoundingClientRect().width;
height = svg.node().getBoundingClientRect().height;
svg.attr({
viewBox: "" + (-width / 2) + " " + (-height + 40) + " " + width + " " + height
});
width -= 60;
x = d3.scale.ordinal().domain((function() {
_results = [];
for (var _i = 0, _ref = sequence.length; 0 <= _ref ? _i < _ref : _i > _ref; 0 <= _ref ? _i++ : _i--){ _results.push(_i); }
return _results;
}).apply(this)).rangeBands([-width / 2, width / 2]);
characters = svg.selectAll('.character').data(sequence);
characters.enter().append('text').text(function(item) {
return item.char;
}).attr({
"class": 'character',
x: function(item) {
return x(item.i) + x.rangeBand() / 2;
},
dy: '1em'
});
arcs = svg.selectAll('.arc').data(matches);
arcs.enter().append('path').attr({
"class": 'arc',
d: function(link) {
var big_radius, end_left, end_right, small_radius, start_left, start_right;
start_left = x(link.source[0].i);
start_right = x(link.source[link.source.length - 1].i) + x.rangeBand();
end_left = x(link.target[0].i);
end_right = x(link.target[link.target.length - 1].i) + x.rangeBand();
big_radius = (end_right - start_left) / 2;
small_radius = (end_left - start_right) / 2;
return "M" + start_left + " 0 A" + big_radius + " " + big_radius + " 0 1 1 " + end_right + " 0 L" + end_left + " 0 A" + small_radius + " " + small_radius + " 0 1 0 " + start_right + " 0 z";
}
});
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment