Skip to content

Instantly share code, notes, and snippets.

@micahscopes
Created June 9, 2012 00:52
Show Gist options
  • Save micahscopes/2898894 to your computer and use it in GitHub Desktop.
Save micahscopes/2898894 to your computer and use it in GitHub Desktop.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta name="generator" content=
"HTML Tidy for Linux (vers 25 March 2009), see www.w3.org">
<meta http-equiv="Content-type" content=
"text/html; charset=us-ascii">
<title>Graph from a matrix</title>
<script type="text/javascript" src=
"http://mbostock.github.com/d3/d3.js?1.29.1">
</script>
<script type="text/javascript" src=
"http://mbostock.github.com/d3/d3.geom.js?1.29.1">
</script>
<script type="text/javascript" src=
"http://mbostock.github.com/d3/d3.layout.js?1.29.1">
</script>
<script type="text/javascript" src=
"http://www.numericjs.com/lib/numeric-1.0.2.min.js">
</script>
<style type="text/css">
path.link {
fill: none;
stroke: #111;
stroke-width: 1.5px;
}
marker#licensing {
fill: green;
}
path.link.licensing {
stroke: green;
}
path.link.resolved {
stroke-dasharray: 0,2 1;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
circle.self-loop {
display: none;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
text.self-loop {
display: none;
}
text.shadow {
stroke: #fff;
stroke-width: 3px;
stroke-opacity: .8;
}
</style>
</head>
<body>
<form action=""><input id="expressionA" name="expression" onchange=
"compute(this.value)" size="100%" value=
"a=[[0,1,0,0,0],[0,0,1,0,0],[1,0,0,0,1],[0,1,0,0,0],[0,0,0,1,0]]; matrixPow(a,4)"></form>
<script type="text/javascript">
function compute(expr){
matrix = eval(expr);
draw(matrix);
}
function draw(matrix){
var links = [];
for(m=0;m< matrix.length;m++) {
a = matrix[m];
for(n=0;n< a.length;n++) {
if (a[n]>0) {
if (n!=m) {
links.push( {
source: String(m+1),
target: String(n+1),
type: String(a[n])
});
} else {
// These are for rendering self reference. The 'null' vertex is a secret extra vertex.
links.push( {
source: String(m+1)+"-null",
target: String(m+1),
type: "self-loop"
});
}
}
}
}
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
t = (link.type == "self-loop"?"self-loop": "basic");
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source, type: t});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
var w = 960,
h = 500;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.linkStrength(function(d) { return (d.type == "self-loop"? 1 : 0.5); })
.size([w, h])
.linkDistance(function(d) { return (d.type == "self-loop"? 20 : 150); })
.charge(-1200)
.gravity(0.3)
.on("tick", tick)
.start();
oldsvgs = document.getElementsByTagName("svg");
for (i=0;i<oldsvgs.length;i++) {
oldsvgs[i].parentNode.removeChild(oldsvgs[i]);
}
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
// Per-type markers, as they don't inherit styles.
svg.append("svg:defs").selectAll("marker")
.data(["basic"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 20)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", "url(#basic)");
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 10)
.call(force.drag)
.attr("class",function(d){ return d.type });
var text = svg.append("svg:g").selectAll("g")
.data(force.nodes())
.enter().append("svg:g")
.attr("class",function(d){ return d.type });
// A copy of the text with a thick white stroke for legitimacy.
text.append("svg:text")
.attr("x", 0)
.attr("y", ".31em")
.attr("class", "shadow")
.attr("class",function(d){ return d.type })
.text(function(d) { return d.name; });
text.append("svg:text")
.attr("x", 0)
.attr("y", ".31em")
.attr("class",function(d){ return d.type })
.text(function(d) { return d.name; });
function tick() {
path.attr("d", function(d) {
if (d.type != "self-loop") {
// Use elliptical arc path segments to doubly-encode directionality.
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;
} else if (d.type == "self-loop") {
// draw a little loop back to the self, keeping in mind that all self loops from the dummy node toward the self.
var dx = d.source.x - d.target.x,
dy = d.source.y - d.target.y,
dr = Math.sqrt(dx * dx + dy * dy);
a = Math.atan2(dx,dy);
da = 0.4;
b = 1;
return "M" + d.target.x + "," + d.target.y + "q" +
b*dr*numeric.sin(a) + "," + b*dr*numeric.cos(a) + " " +
b*dr*numeric.sin(a+da) + "," + b*dr*numeric.cos(a+da) + " " + " T " + d.target.x + "," + d.target.y;
}
});
circle.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
}
// extra functions
function matrixPow(m,p) {
if(p==0) return numeric.identity(m[0].length);
a = m;
for (i=0;i<p-1;i++) {
a = numeric.dot(a,m);
}
return a;
}
function getParameterByName(name)
{
// to get the parameters for the expression
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
var regexS = "[\\?&]" + name + "=([^&#]*)";
var regex = new RegExp(regexS);
var results = regex.exec(window.location.search);
if(results == null)
return "";
else
return decodeURIComponent(results[1].replace(/\+/g, " "));
}
input = document.getElementById("expressionA");
pExpression = getParameterByName("expression");
if (pExpression) input.value = pExpression;
compute(document.getElementById("expressionA").value);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment