2012-10-25 13:31:34 +00:00
<!DOCTYPE html>
< html >
< head >
< title > Springy - A force directed graph layout algorithm in JavaScript.< / title >
< link href = 'http://fonts.googleapis.com/css?family=IM+Fell+English+SC|IM+Fell+English:400,400italic' rel = 'stylesheet' type = 'text/css' >
< script src = "springy.js" > < / script >
< style >
body {
background: #FFF;
font-family: 'IM Fell English', serif;
font-size: 18px;
line-height: 1.4;
text-align: center;
}
h1 {
font-weight: normal;
font-size: 48px;
font-family: 'IM Fell English SC', serif;
}
.subtitle {
font-size: 24px;
font-style: italic;
}
h2 {
font-weight: normal;
font-size: 32px;
font-family: 'IM Fell English SC', serif;
margin-top: 0;
}
.section {
width: 650px;
margin: 0 auto;
text-align: left;
}
.divider {
margin: 30px 0;
text-align: center;
}
.example {
box-sizing: border-box;
background: #EEEEEE;
2012-10-26 03:50:48 +00:00
box-shadow: 0 0 50px 0 rgba(0,0,0,0.1) inset;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
2012-10-25 13:31:34 +00:00
}
pre {
font-family: monospace;
background: #EEEEEE;
2012-10-26 03:50:48 +00:00
box-shadow: 0 0 50px 0 rgba(0,0,0,0.1) inset;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
2012-10-25 13:31:34 +00:00
padding: 15px;
}
.Function { font-weight: bold; }
.Normal { font-weight: bold; }
.Identifier { font-weight: bold; }
.Comment { color: #808080; }
< / style >
< / head >
< body >
< h1 > Springy.js< / h1 >
< p class = "subtitle" > A force directed graph layout algorithm for JavaScript.< / p >
< div class = "divider" > < img src = "branch.png" align = "top" / > < / div >
2012-10-25 14:25:55 +00:00
< p > < a href = "https://github.com/dhotson/springy/" > GitHub< / a > | < a href = "demo.html" > Demo< / a > | < a href = "mailto:dennis.hotson@gmail.com" > Contact< / a > < / p >
2012-10-25 13:31:34 +00:00
< div id = "what" class = "section" >
< h2 > What is Springy?< / h2 >
< p > Springy is a force directed graph layout algorithm.< / p >
< p > So what does this “ force directed” stuff mean anyway? Excellent question!< / p >
< p > It basically means that it uses some real world physics to try and figure out how to show a network graph in a nice way.< / p >
< p > Here's an example:< / p >
< canvas id = "adv" class = "example" width = "650" height = "300" > < / canvas >
< p > You can imagine it as a bunch of springs connected to each other.< / p >
< / div >
< script >
(function() {
// make a new graph
var graph = new Graph();
// make some nodes
var node1 = graph.newNode({label: 'Norway Spruce'});
var node2 = graph.newNode({label: 'Sicilian Fir'});
var node3 = graph.newNode({label: 'Sumatran Pine'});
var node4 = graph.newNode({label: 'Japanese Larch'});
// connect them with an edge
graph.newEdge(node1, node2);
graph.newEdge(node2, node3);
graph.newEdge(node2, node4);
graph.newEdge(node1, node4);
var layout = new Layout.ForceDirected(
graph,
200.0, // Spring stiffness
400.0, // Node repulsion
0.5 // Damping
);
var canvas = document.getElementById('adv');
var ctx = canvas.getContext('2d');
var renderer = new Renderer(10, layout,
function clear() {
ctx.clearRect(0, 0, 650, 300);
},
function drawEdge(edge, p1, p2) {
ctx.save();
ctx.translate(325, 150);
ctx.strokeStyle = 'rgba(0,0,0,0.1)';
ctx.lineWidth = 3.0;
ctx.beginPath();
ctx.moveTo(p1.x * 50, p1.y * 40);
ctx.lineTo(p2.x * 50, p2.y * 40);
ctx.stroke();
ctx.restore();
},
function drawNode(node, p) {
ctx.save();
ctx.translate(325, 150);
ctx.font = "18px 'IM Fell English', serif";
var width = ctx.measureText(node.data.label).width;
var x = p.x * 50;
var y = p.y * 40;
ctx.clearRect(x - width / 2.0 - 5, y - 12, width + 10, 24);
ctx.fillStyle = '#000000';
ctx.fillText(node.data.label, x - width / 2.0, y + 5);
ctx.restore();
}
);
renderer.start();
})()
< / script >
< div class = "divider" > < img src = "pine.png" / > < / div >
< div id = "usage" class = "section" >
< h2 > Basic usage< / h2 >
< p > springy.js by itself is quite plain and doesn't include any code to do rendering or drag and drop etc. It's just for calculating the layout.< / p >
< p > The drawing and interaction stuff is mostly up to you.< / p >
< p > However, I've written a little helper jQuery plugin called springyui.js to help get you started. It's got a semi-decent default renderer and some half assed drag and drop.< / p >
< p > Take a look at < a href = "#" > demo.html< / a > and < a href = "#" > springyui.js< / a > for an example of usage.< / p >
< / div >
< div class = "divider" > < img src = "nut.png" / > < / div >
< div id = "usage" class = "section" >
< h2 > Advanced usage< / h2 >
< p > If you're keen to do your own custom drawing, you'll need to know a few things before you get started.< / p >
< p > This is the basic graph API, you can create nodes and edges etc.< / p >
< pre >
< span class = "Comment" > // make a new graph< / span >
< span class = "Identifier" > var< / span > graph = < span class = "Normal" > new< / span > Graph();
< span class = "Comment" > // make some nodes< / span >
< span class = "Identifier" > var< / span > node1 = graph.newNode(< span class = "Function" > {< / span > label: < span class = "String" > 'Norway Spruce'< / span > < span class = "Function" > }< / span > );
< span class = "Identifier" > var< / span > node2 = graph.newNode(< span class = "Function" > {< / span > label: < span class = "String" > 'Sicilian Fir'< / span > < span class = "Function" > }< / span > );
< span class = "Comment" > // connect them with an edge< / span >
graph.newEdge(node1, node2);
< / pre >
< p > So now to draw this graph, lets make a layout object:< / p >
< pre >
< span class = "Identifier" > var< / span > layout = < span class = "Normal" > new< / span > Layout.ForceDirected(
graph,
< span class = "Constant" > 400< / span > .< span class = "Constant" > 0< / span > , < span class = "Comment" > // Spring stiffness< / span >
< span class = "Constant" > 400< / span > .< span class = "Constant" > 0< / span > , < span class = "Comment" > // Node repulsion< / span >
< span class = "Constant" > 0< / span > .< span class = "Constant" > 5< / span > < span class = "Comment" > // Damping< / span >
);
< / pre >
< p > I've written a Renderer class, which will handle the rendering loop. You just need to provide some callbacks to do the actual drawing.< / p >
< pre >
< span class = "Identifier" > var< / span > renderer = < span class = "Normal" > new< / span > Renderer(< span class = "Constant" > 10< / span > , layout,
< span class = "Function" > function< / span > clear() < span class = "Function" > {< / span >
< span class = "Comment" > // code to clear screen< / span >
< span class = "Function" > }< / span > ,
< span class = "Function" > function< / span > drawEdge(edge, p1, p2) < span class = "Function" > {< / span >
< span class = "Comment" > // draw an edge< / span >
< span class = "Function" > }< / span > ,
< span class = "Function" > function< / span > drawNode(node, p) < span class = "Function" > {< / span >
< span class = "Comment" > // draw a node< / span >
< span class = "Function" > }< / span >
);
< / pre >
< p > Now, just start the rendering loop:< / p >
< pre >
renderer.start();
< / pre >
< div class = "divider" > < img src = "cone.png" / > < / div >
< div id = "usage" class = "section" >
< h2 > Acknowledgements< / h2 >
< / div >
< div class = "divider" > < img src = "dome.png" / > < / div >
2012-10-25 14:25:55 +00:00
< script type = "text/javascript" >
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-35866821-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
< / script >
2012-10-25 13:31:34 +00:00
< / body >