Skip to content

Instantly share code, notes, and snippets.

@apcj
Forked from mbostock/.block
Last active January 4, 2016 18:19
Show Gist options
  • Save apcj/8659617 to your computer and use it in GitHub Desktop.
Save apcj/8659617 to your computer and use it in GitHub Desktop.

#Code Changes Tree Map

Run count_files.sh, passing a directory and a commit range you want to analyse. For example:

$ ./count_files.sh projects/neo4j HEAD~10..HEAD >data.tsv

Then start a web server and open index.html in your browser. Something like this:

$ python -m SimpleHTTPServer 8888 &
$ open http://localhost:8888/

A treemap recursively subdivides area into rectangles; the area of any node in the tree corresponds to its value. Treemap design invented by Ben Shneiderman. Squarified algorithm by Bruls, Huizing and van Wijk.

#!/bin/bash
echo -e "path\tfiles\tchanges"
for i in `find $1 -type d`;
do
count=`find $i -name "*.java" -or -name "*.scala" -type f|wc -l`
if [ $count -gt 0 ]
then
changes=`(cd $i; git diff $2 --name-only -- $i)|grep -e java -e scala|wc -l`
echo -e $i"\t"$count"\t"$changes
fi;
done|cut -b 2-
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
.node {
border: solid 1px white;
font: 10px sans-serif;
line-height: 12px;
overflow: hidden;
position: absolute;
text-indent: 2px;
}
</style>
<body>
<p id="filename"></p>
</body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 10, right: 10, bottom: 10, left: 10},
width = window.innerWidth - margin.left - margin.right,
height = window.innerHeight - margin.top - margin.bottom;
var greenToRed = d3.scale.linear().domain([0, 1]).range([120, 0]);
var saturation = d3.scale.linear().domain([0, 1]).range([.5, 1]);
var brightness = d3.scale.linear().domain([0, 1]).range([.8, .5]);
var color = function(file) {
var ratio = (file.changes || 0) / file.size;
if (ratio > 1) console.log(ratio, file);
ratio = Math.min(1, ratio);
return d3.hsl(greenToRed(ratio), saturation(ratio), brightness(ratio)).toString();
}
var treemap = d3.layout.treemap()
.size([width, height])
.sticky(true)
// .padding(5)
.value(function(d) { return d.size; });
var div = d3.select("body").append("div")
.style("position", "relative")
.style("width", (width + margin.left + margin.right) + "px")
.style("height", (height + margin.top + margin.bottom) + "px")
.style("left", margin.left + "px")
.style("top", margin.top + "px");
function updateTree(root, files, key) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var extension = (/\.([^\.]+)$/.exec(file.filename) || [])[1];
var excluded = ["js", "css", "graffle", "in", "txt", "less", "svg"];
if (excluded.indexOf(extension) !== -1) {
continue;
}
var segments = file.filename.split("/");
var node = root;
for (var d = 0; d < segments.length; d++) {
var segment = segments[d];
if (!node.children) {
node.children = [];
}
var child = node.children.filter(function (child) { return child.name === segment; })[0];
if (!child) {
node.children.push(child = { name: segment } );
}
node = child;
}
node[key] = file.changes;
node.file = file;
}
}
var hashParams = function() {
var list = window.location.hash.substr(1).split("&");
var map = {};
for (var i = 0; i < list.length; i++) {
var tokens = list[i].split("=");
map[tokens[0]] = tokens[1];
}
return map;
}();
var initialCommit = hashParams["initial"];
var startCommit = hashParams["start"];
var endCommit = hashParams["end"];
// 9c4f3c010b03070098c2102d46d347bac809f4e5
function getCachedJson(uri, callback) {
var cached = window.localStorage.getItem(uri);
console.log(uri)
if (cached) {
callback(null, JSON.parse(cached));
} else {
d3.json(uri, function(error, data) {
var simplified = data.files.map(function(file) { return { filename: file.filename, changes: file.changes }; });
window.localStorage.setItem(uri, JSON.stringify(simplified));
callback(error, simplified);
});
}
}
getCachedJson("https://api.github.com/repos/neo4j/neo4j/compare/" + initialCommit + "..." + endCommit, function(error, files) {
var root = {};
updateTree(root, files, "size");
getCachedJson("https://api.github.com/repos/neo4j/neo4j/compare/" + startCommit + "..." + endCommit, function(error, files) {
updateTree(root, files, "changes");
console.log(root);
var node = div.datum(root).selectAll(".node")
.data(treemap.nodes)
.enter().append("div")
.attr("class", "node")
.call(position)
.style("background", function(d) { return color(d); })
.text(function(d) { return d.children ? null : d.name; })
.on("mouseover", function(d) { d3.select("#filename").text(d.file.filename); });
});
});
function position() {
this.style("left", function(d) { return d.x + "px"; })
.style("top", function(d) { return d.y + "px"; })
.style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; })
.style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; });
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment