Click to drag nodes. Dashed links are resolved suits; green links are licensing.
This graphs tries to visualize my education as I went through it at the University of Copenhagen, Department of Computer Science.
Implemented in D3.js.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
.link { | |
fill: none; | |
stroke: #666; | |
stroke-width: 1.5px; | |
} | |
#licensing { | |
fill: green; | |
} | |
.link.licensing { | |
stroke: green; | |
} | |
.link.resolved { | |
stroke-dasharray: 0,2 1; | |
} | |
circle { | |
fill: #ccc; | |
stroke: #333; | |
stroke-width: 1.5px; | |
} | |
text { | |
font: 10px sans-serif; | |
pointer-events: none; | |
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff; | |
} | |
</style> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
// http://blog.thomsonreuters.com/index.php/mobile-patent-suits-graphic-of-the-day/ | |
var links = [ | |
// {source: "DiMS", target: "IP", type: "B1"}, | |
{source: "IP", target: "DiMS", type: "B1"}, | |
{source: "IP", target: "OOPD", type: "course"}, | |
{source: "DiMS", target: "LinAlg", type: "course"}, | |
{source: "DiMS", target: "DiMS", type: "reexam"}, | |
{source: "OOPD", target: "DBW", type: "course"}, | |
{source: "LinAlg", target: "ProjDat", type: "course"}, | |
{source: "DBW", target: "Algorithms", type: "course"}, | |
{source: "DBW", target: "DBW", type: "reexam"}, | |
{source: "ProjDat", target: "ProjDat", type: "reexam"}, | |
{source: "Algorithms", target: "Sabatical", type: "course"}, | |
{source: "ProjDat", target: "Sabatical", type: "course"}, | |
{source: "Sabatical", target: "Architecture", type: "course"}, | |
{source: "Sabatical", target: "HCI", type: "course"}, | |
{source: "Architecture", target: "DatVid", type: "course"}, | |
{source: "HCI", target: "Compilers", type: "course"}, | |
{source: "DatVid", target: "OSM", type: "course"} | |
]; | |
var nodes = {}; | |
// Compute the distinct nodes from the links. | |
links.forEach(function(link) { | |
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source}); | |
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target}); | |
}); | |
var width = 960, | |
height = 500; | |
var force = d3.layout.force() | |
.nodes(d3.values(nodes)) | |
.links(links) | |
.size([width, height]) | |
.linkDistance(60) | |
.charge(-300) | |
.on("tick", tick) | |
.start(); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
// Per-type markers, as they don't inherit styles. | |
svg.append("defs").selectAll("marker") | |
.data(["course", "reexam", "B1"]) | |
.enter().append("marker") | |
.attr("id", function(d) { return d; }) | |
.attr("viewBox", "0 -5 10 10") | |
.attr("refX", 15) | |
.attr("refY", -1.5) | |
.attr("markerWidth", 6) | |
.attr("markerHeight", 6) | |
.attr("orient", "auto") | |
.append("path") | |
.attr("d", "M0,-5L10,0L0,5"); | |
var path = svg.append("g").selectAll("path") | |
.data(force.links()) | |
.enter().append("path") | |
.attr("class", function(d) { return "link " + d.type; }) | |
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; }); | |
var circle = svg.append("g").selectAll("circle") | |
.data(force.nodes()) | |
.enter().append("circle") | |
.attr("r", 6) | |
.call(force.drag); | |
var text = svg.append("g").selectAll("text") | |
.data(force.nodes()) | |
.enter().append("text") | |
.attr("x", 8) | |
.attr("y", ".31em") | |
.text(function(d) { return d.name; }); | |
// Use elliptical arc path segments to doubly-encode directionality. | |
function tick() { | |
path.attr("d", linkArc); | |
circle.attr("transform", transform); | |
text.attr("transform", transform); | |
} | |
function linkArc(d) { | |
var dx = d.target.x - d.source.x, | |
dy = d.target.y - d.source.y, | |
dr = Math.sqrt(dx * dx + dy * dy); | |
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; | |
} | |
function transform(d) { | |
return "translate(" + d.x + "," + d.y + ")"; | |
} | |
</script> |